diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..46b6302 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(mockturtle INTERFACE) +target_include_directories(mockturtle SYSTEM INTERFACE ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(mockturtle INTERFACE kitty lorina parallel_hashmap percy json bill libabcesop) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) +target_link_libraries(mockturtle INTERFACE stdc++fs) +endif() + +if(ENABLE_ABC) +target_link_libraries(mockturtle INTERFACE ${PROJECT_SOURCE_DIR}/lib/abc_static/libabc.a) +target_link_libraries(mockturtle INTERFACE dl) +target_compile_definitions(mockturtle INTERFACE ENABLE_ABC) +endif() diff --git a/include/mockturtle/algorithms/aig_balancing.hpp b/include/mockturtle/algorithms/aig_balancing.hpp new file mode 100644 index 0000000..cf5a475 --- /dev/null +++ b/include/mockturtle/algorithms/aig_balancing.hpp @@ -0,0 +1,482 @@ +/* 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 aig_balancing.hpp + \brief Balances the AIG to reduce the depth + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +namespace mockturtle +{ + +struct 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 +{ + +template +class aig_balance_impl +{ +public: + static constexpr size_t storage_init_size = 30; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using storage_t = std::vector>; + +public: + aig_balance_impl( Ntk& ntk, aig_balancing_params const& ps ) + : ntk( ntk ), ps( ps ), storage( storage_init_size ) + { + } + + void run() + { + ntk.clear_values(); + + for ( auto i = 0; i < storage_init_size; ++i ) + storage[i].reserve( 10 ); + + /* balance every CO */ + ntk.foreach_co( [&]( auto const& f ) { + balance_rec( ntk.get_node( f ), 0 ); + } ); + } + +private: + signal balance_rec( node const& n, uint32_t level ) + { + if ( ntk.is_ci( n ) ) + return ntk.make_signal( n ); + + /* node has been replaced in a previous recursion */ + if ( ntk.is_dead( n ) || ntk.value( n ) > 0 ) + { + return ntk.make_signal( find_substituted_node( n ) ); + } + + if ( level >= storage.size() ) + { + storage.emplace_back( std::vector() ); + storage.back().reserve( 10 ); + } + + /* collect leaves of the AND tree */ + collect_leaves( n, storage[level] ); + + if ( storage[level].size() == 0 ) + { + ntk.substitute_node( n, ntk.get_constant( false ) ); + return ntk.get_constant( false ); + } + + /* recur over the leaves */ + for ( auto& f : storage[level] ) + { + signal new_signal = balance_rec( ntk.get_node( f ), level + 1 ); + f = new_signal ^ ntk.is_complemented( f ); + } + + assert( storage[level].size() > 1 ); + + /* sort by decreasing level */ + std::stable_sort( storage[level].begin(), storage[level].end(), [this]( auto const& a, auto const& b ) { + return ntk.level( ntk.get_node( a ) ) > ntk.level( ntk.get_node( b ) ); + } ); + + /* mark TFI cone of n */ + ntk.incr_trav_id(); + mark_tfi( ntk.make_signal( n ), true ); + + /* generate the AND tree */ + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + 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 + { + 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(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_and( child1, child2 ); + + /* update level for AND node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted( storage[level], new_sig ); + } + + signal root = storage[level][0]; + + /* replace if new */ + if ( n != ntk.get_node( root ) ) + { + ntk.substitute_node_no_restrash( n, root ); + } + + /* remember the substitution and the new node as already balanced */ + ntk.set_value( n, ntk.node_to_index( ntk.get_node( root ) ) ); + ntk.set_value( ntk.get_node( root ), ntk.node_to_index( ntk.get_node( root ) ) ); + + /* clean leaves storage */ + storage[level].clear(); + + return root; + } + + void collect_leaves( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_rec( ntk.make_signal( n ), leaves, true ); + + /* check for constant false */ + if ( ret < 0 ) + { + leaves.clear(); + } + } + + int collect_leaves_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + for ( signal const& s : leaves ) + { + if ( ntk.get_node( s ) != n ) + continue; + + if ( s == f ) + return 1; /* same polarity: duplicate */ + else + return -1; /* opposite polarity: const0 */ + } + + return 0; + } + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret |= collect_leaves_rec( child, leaves, false ); + } ); + + return ret; + } + + size_t find_left_most_at_level( std::vector const& leaves ) + { + size_t pointer = leaves.size() - 1; + uint32_t current_level = ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ); + + while ( pointer > 0 ) + { + if ( ntk.level( ntk.get_node( leaves[pointer - 1] ) ) > current_level ) + break; + + --pointer; + } + + assert( ntk.level( ntk.get_node( leaves[pointer] ) ) == current_level ); + return pointer; + } + + inline void pick_nodes( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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 ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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 ); + + /* check uniqueness */ + for ( auto const& s : leaves ) + { + if ( s == f ) + return; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void update_level( node const& n ) + { + uint32_t l = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + l = std::max( l, ntk.level( ntk.get_node( f ) ) ); + } ); + + ntk.set_level( n, l + 1 ); + } + + node find_substituted_node( node n ) + { + while ( ntk.is_dead( n ) ) + n = ntk.index_to_node( ntk.value( n ) ); + + return n; + } + + void mark_tfi( signal const& f, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( auto const& child ) { + mark_tfi( child, false ); + } ); + } + +private: + Ntk& ntk; + aig_balancing_params const& ps; + + storage_t storage; +}; + +} /* namespace detail */ + +/*! \brief AIG balancing. + * + * This method balance the AIG to reduce the + * depth. Level minimization can be turned off. + * In this case, balancing tries to reconstruct + * AND trees such that logic sharing is maximized. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_and` + */ +template +void aig_balance( Ntk& ntk, aig_balancing_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_clone_node_v, "Ntk does not implement the clone_node method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_has_and_v, "Ntk does not implement the has_and method" ); + + fanout_view f_ntk{ ntk }; + depth_view> d_ntk{ f_ntk }; + + detail::aig_balance_impl p( d_ntk, ps ); + p.run(); + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/aig_resub.hpp b/include/mockturtle/algorithms/aig_resub.hpp new file mode 100644 index 0000000..e59a730 --- /dev/null +++ b/include/mockturtle/algorithms/aig_resub.hpp @@ -0,0 +1,986 @@ +/* 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 aig_resub.hpp + \brief Resubstitution + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../utils/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/xag_resyn.hpp" + +namespace mockturtle +{ + +struct aig_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted single AND-resubsitutions */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted single OR-resubsitutions */ + uint64_t num_div1_or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + /*! \brief Number of accepted single 2AND-resubsitutions */ + uint64_t num_div12_2and_accepts{ 0 }; + + /*! \brief Number of accepted single 2OR-resubsitutions */ + uint64_t num_div12_2or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted double AND-OR-resubsitutions */ + uint64_t num_div2_and_or_accepts{ 0 }; + + /*! \brief Number of accepted double OR-AND-resubsitutions */ + uint64_t num_div2_or_and_accepts{ 0 }; + + /*! \brief Number of accepted three resubsitutions */ + uint64_t num_div3_accepts{ 0 }; + + /*! \brief Number of accepted AND-2OR-resubsitutions */ + uint64_t num_div3_and_2or_accepts{ 0 }; + + /*! \brief Number of accepted OR-2AND-resubsitutions */ + uint64_t num_div3_or_2and_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: aig_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] 12-resub {:6d} = {:6d} 2AND + {:6d} 2OR ({:>5.2f} secs)\n", + num_div12_accepts, num_div12_2and_accepts, num_div12_2or_accepts, to_seconds( time_resub12 ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} = {:6d} AND-OR + {:6d} OR-AND ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_and_or_accepts, num_div2_or_and_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] 3-resub {:6d} = {:6d} AND-2OR + {:6d} OR-2AND ({:>5.2f} secs)\n", + num_div3_accepts, num_div3_and_2or_accepts, num_div3_or_2and_accepts, to_seconds( time_resub3 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_div1_accepts + num_div12_accepts + num_div2_accepts + num_div3_accepts ) ); + } +}; /* aig_resub_stats */ + +template +struct aig_resub_functor +{ +public: + using node = aig_network::node; + using signal = aig_network::signal; + using stats = aig_resub_stats; + + struct unate_divisors + { + using signal = typename aig_network::signal; + + std::vector positive_divisors; + std::vector negative_divisors; + std::vector next_candidates; + + void clear() + { + positive_divisors.clear(); + negative_divisors.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + using signal = typename aig_network::signal; + + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + } + }; + +public: + explicit aig_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t max_depth, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, max_depth ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, max_depth ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, max_depth ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_mffc == 2 ) + return std::nullopt; + + /* consider triples */ + g = call_with_stopwatch( st.time_resub12, [&]() { return resub_div12( root, max_depth ); } ); + if ( g ) + { + ++st.num_div12_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, max_depth ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, max_depth ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + if ( max_inserts == 2 || num_mffc == 3 ) + return std::nullopt; + + /* consider three nodes */ + g = call_with_stopwatch( st.time_resub3, [&]() { return resub_div3( root, max_depth ); } ); + if ( g ) + { + ++st.num_div3_accepts; + last_gain = num_mffc - 3; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root ) const + { + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t max_depth ) const + { + (void)max_depth; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + assert( ntk.level( d ) <= max_depth ); + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t max_depth ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.level( d ) > max_depth - 1 ) + continue; + + auto const& tt_d = sim.get_tt( ntk.make_signal( d ) ); + + /* check positive containment */ + if ( kitty::implies( tt_d, tt ) ) + { + udivs.positive_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + /* check negative containment */ + if ( kitty::implies( tt, tt_d ) ) + { + udivs.negative_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + if ( true ) // ( ps.fix_bug ) + { + /* unreachable case */ + // if ( kitty::implies( ~tt_d, tt ) ) + // { + // udivs.positive_divisors.emplace_back( !ntk.make_signal( d ) ); + // continue; + // } + if ( kitty::implies( tt, ~tt_d ) ) + { + udivs.negative_divisors.emplace_back( !ntk.make_signal( d ) ); + continue; + } + } + + udivs.next_candidates.emplace_back( ntk.make_signal( d ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const& s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const& s1 = udivs.positive_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( ( tt_s0 | tt_s1 ) == tt ) + { + ++st.num_div1_or_accepts; + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + assert( ntk.level( ntk.get_node( l ) ) <= max_depth - 1 && ntk.level( ntk.get_node( r ) ) <= max_depth - 1 ); + return sim.get_phase( root ) ? !ntk.create_or( l, r ) : ntk.create_or( l, r ); + } + } + } + + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors.size(); ++i ) + { + auto const& s0 = udivs.negative_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.negative_divisors.size(); ++j ) + { + auto const& s1 = udivs.negative_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( ( tt_s0 & tt_s1 ) == tt ) + { + ++st.num_div1_and_accepts; + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + assert( ntk.level( ntk.get_node( l ) ) <= max_depth - 1 && ntk.level( ntk.get_node( r ) ) <= max_depth - 1 ); + return sim.get_phase( root ) ? !ntk.create_and( l, r ) : ntk.create_and( l, r ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div12( node const& root, uint32_t max_depth ) + { + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + /* check positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( ( tt_s0 | tt_s1 | tt_s2 ) == tt ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= max_depth - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + if ( ntk.level( ntk.get_node( min0 ) ) > max_level - 2 || ntk.level( ntk.get_node( min1 ) ) > max_level - 2 ) + continue; + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + ++st.num_div12_2or_accepts; + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_or( b, c ) ) : ntk.create_or( a, ntk.create_or( b, c ) ); + } + } + } + } + + /* check negative unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( ( tt_s0 & tt_s1 & tt_s2 ) == tt ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= max_depth - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + if ( ntk.level( ntk.get_node( min0 ) ) > max_level - 2 || ntk.level( ntk.get_node( min1 ) ) > max_level - 2 ) + continue; + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + ++st.num_div12_2and_accepts; + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_and( b, c ) ) : ntk.create_and( a, ntk.create_and( b, c ) ); + } + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t max_depth ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > max_depth - 2 ) + continue; + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > max_depth - 2 ) + continue; + + if ( bdivs.positive_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( ~tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + } + + if ( bdivs.negative_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt, tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + } + } + } + } + + std::optional resub_div2( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + /* check positive unate divisors */ + for ( const auto& s0 : udivs.positive_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s1 = bdivs.positive_divisors0.at( j ); + auto const s2 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( ( tt_s0 | ( tt_s1 & tt_s2 ) ) == tt ) + { + ++st.num_div2_or_and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 1 ); + assert( ntk.level( ntk.get_node( b ) ) <= max_depth - 2 && ntk.level( ntk.get_node( c ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_and( b, c ) ) : ntk.create_or( a, ntk.create_and( b, c ) ); + } + } + } + + /* check negative unate divisors */ + for ( const auto& s0 : udivs.negative_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s1 = bdivs.negative_divisors0.at( j ); + auto const s2 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( ( tt_s0 | ( tt_s1 & tt_s2 ) ) == tt ) + { + ++st.num_div2_or_and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 1 ); + assert( ntk.level( ntk.get_node( b ) ) <= max_depth - 2 && ntk.level( ntk.get_node( c ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_or( b, c ) ) : ntk.create_and( a, ntk.create_or( b, c ) ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div3( node const& root, uint32_t max_depth ) + { + (void)max_depth; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + + for ( auto i = 0u; i < bdivs.positive_divisors0.size(); ++i ) + { + auto const s0 = bdivs.positive_divisors0.at( i ); + auto const s1 = bdivs.positive_divisors1.at( i ); + + for ( auto j = i + 1; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s2 = bdivs.positive_divisors0.at( j ); + auto const s3 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + + if ( ( ( tt_s0 | tt_s1 ) & ( tt_s2 | tt_s3 ) ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + + ++st.num_div3_and_2or_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 2 && ntk.level( ntk.get_node( b ) ) <= max_depth - 2 ); + assert( ntk.level( ntk.get_node( c ) ) <= max_depth - 2 && ntk.level( ntk.get_node( d ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_and( ntk.create_or( a, b ), ntk.create_or( c, d ) ) : ntk.create_and( ntk.create_or( a, b ), ntk.create_or( c, d ) ); + } + } + } + + for ( auto i = 0u; i < bdivs.negative_divisors0.size(); ++i ) + { + auto const s0 = bdivs.negative_divisors0.at( i ); + auto const s1 = bdivs.negative_divisors1.at( i ); + + for ( auto j = i + 1; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s2 = bdivs.negative_divisors0.at( j ); + auto const s3 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + + if ( ( ( tt_s0 & tt_s1 ) | ( tt_s2 & tt_s3 ) ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + + ++st.num_div3_or_2and_accepts; + assert( ntk.level( ntk.get_node( a ) ) <= max_depth - 2 && ntk.level( ntk.get_node( b ) ) <= max_depth - 2 ); + assert( ntk.level( ntk.get_node( c ) ) <= max_depth - 2 && ntk.level( ntk.get_node( d ) ) <= max_depth - 2 ); + return sim.get_phase( root ) ? !ntk.create_or( ntk.create_and( a, b ), ntk.create_and( c, d ) ) : ntk.create_or( ntk.create_and( a, b ), ntk.create_and( c, d ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* aig_resub_functor */ + +struct aig_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* aig_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with AIG resynthesis engines for `window_based_resub_engine`. + */ +template>> +struct aig_resyn_functor +{ +public: + using node = aig_network::node; + using signal = aig_network::signal; + using stats = aig_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit aig_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + 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( ntk.make_signal( d ) ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + 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 + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* aig_resyn_functor */ + +template +void aig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + /* TODO: check if basetype of ntk is aig */ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if ( ps.max_pis == 8 ) + { + 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>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + 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>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +/*! \brief AIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from aig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void aig_resubstitution2( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not aig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = aig_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/akers_synthesis.hpp b/include/mockturtle/algorithms/akers_synthesis.hpp new file mode 100644 index 0000000..187d924 --- /dev/null +++ b/include/mockturtle/algorithms/akers_synthesis.hpp @@ -0,0 +1,880 @@ +/* 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 akers_synthesis.hpp + \brief Akers synthesis + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/* cube operators needed from Kitty : +- & (bitwise and ) +- is_subset_of +- is_const0 +*/ + +inline kitty::cube operator&( kitty::cube a, kitty::cube b ) +{ + assert( a.num_literals() == b.num_literals() ); + kitty::cube c; + for ( auto h = 0; h < a.num_literals(); h++ ) + { + c.add_literal( h, a.get_bit( h ) & b.get_bit( h ) ); + } + return c; +} + +inline bool is_subset_of( kitty::cube a, kitty::cube b ) +{ + assert( a.num_literals() == b.num_literals() ); + for ( auto h = 0; h < a.num_literals(); h++ ) + { + if ( ( a.get_bit( h ) == 1 ) && ( b.get_bit( h ) == 1 ) ) + continue; + else if ( ( a.get_bit( h ) == 1 ) && ( b.get_bit( h ) == 0 ) ) + return false; + } + return true; +} + +inline bool all_zeros( kitty::cube a ) +{ + for ( auto g = 0; g < a.num_literals(); g++ ) + { + if ( a.get_bit( g ) == 0 ) + continue; + else + return false; + } + return true; +} + +class unitized_table +{ +public: + using row_t = kitty::cube; + + unitized_table( const std::string& columns ) + : columns( columns ) + { + } + + row_t create_row() const + { + row_t r( 0, ( 1 << columns.size() ) - 1 ); + // for ( auto i = 0u; i < columns.size(); i++ ) + // r.add_literal( i, 1 ); + return r; + } + + row_t create_mask() const + { + row_t r( ( 1 << columns.size() ) - 1, ( 1 << columns.size() ) - 1 ); + // for ( auto i = 0u; i < columns.size(); i++ ) + // r.add_literal( i, 0 ); + return r; + } + + void add_row( const row_t& row ) + { + rows.push_back( row ); + } + + void reduce() + { + auto progress{ true }; + + while ( progress ) + { + progress = reduce_columns(); + progress = progress || reduce_rows(); + } + } + + friend std::ostream& operator<<( std::ostream& os, const unitized_table& table ); + + inline std::vector::const_iterator begin() const + { + return rows.begin(); + } + + inline std::vector::const_iterator end() const + { + return rows.end(); + } + + inline int operator[]( unsigned index ) const + { + return (int)(unsigned char)columns[index]; + } + + inline bool is_opposite( unsigned c1, unsigned c2 ) const + { + const auto _n1 = columns[c1]; + const auto _n2 = columns[c2]; + + const auto n1 = std::min( _n1, _n2 ); + const auto n2 = std::max( _n1, _n2 ); + + return ( n1 == 0x30 && n2 == 0x31 ) || ( ( n1 + 0x20 ) == n2 ); + } + + inline auto num_columns() const + { + return columns.size(); + } + + int add_gate( const std::set& gate ) + { + assert( gate.size() == 3u ); + + auto it = gate.begin(); + const auto c1 = *it++; + const auto c2 = *it++; + const auto c3 = *it; + + for ( auto& r : rows ) + { + const auto bi1 = r.get_bit( c1 ); + const auto bi2 = r.get_bit( c2 ); + const auto bi3 = r.get_bit( c3 ); + + r.add_literal( r.num_literals(), ( bi1 && bi2 ) || ( bi1 && bi3 ) || ( bi2 && bi3 ) ); + } + + auto ret = (int)next_gate_id; + columns.push_back( next_gate_id++ ); + return ret; + } + + unsigned count_essential_ones( bool skip_last_column = true ) const + { + auto count = 0u; + auto end = columns.size(); + if ( skip_last_column ) + { + --end; + } + + for ( auto column = 0u; column < end; ++column ) + { + std::vector one_rows; + + /* find rows with a 1 at column */ + for ( const auto& row : rows ) + { + if ( row.get_bit( column ) ) + { + auto rt = row; + rt.clear_bit( column ); + one_rows.push_back( rt ); + } + } + + /* find essential rows */ + for ( auto i = 0u; i < one_rows.size(); ++i ) + { + for ( auto j = 0u; j < one_rows.size(); ++j ) + { + if ( i == j ) + { + continue; + } + if ( all_zeros( one_rows[i] & one_rows[j] ) ) + { + ++count; /* entry i is essential in column */ + break; + } + } + } + } + + return count; + } + +private: + bool reduce_rows() + { + std::vector to_be_removed; + + for ( auto i = 0u; i < rows.size(); ++i ) + { + for ( auto j = i + 1u; j < rows.size(); ++j ) + { + if ( rows[i] == rows[j] ) + { + to_be_removed.push_back( i ); + } + else + { + if ( is_subset_of( rows[i], rows[j] ) ) + { + to_be_removed.push_back( j ); + } + if ( is_subset_of( rows[j], rows[i] ) ) + { + to_be_removed.push_back( i ); + } + } + } + } + + std::stable_sort( to_be_removed.begin(), to_be_removed.end() ); + to_be_removed.erase( std::unique( to_be_removed.begin(), to_be_removed.end() ), to_be_removed.end() ); + + std::reverse( std::begin( to_be_removed ), std::end( to_be_removed ) ); + + for ( auto index : to_be_removed ) + { + rows.erase( rows.begin() + index ); + } + return !to_be_removed.empty(); + } + + bool reduce_columns() + { + std::vector to_be_removed; + + auto mask = create_mask(); + + for ( auto c = 0u; c < columns.size(); ++c ) + { + mask.clear_bit( c ); + auto can_be_removed = true; + + /* pretend column c is removed */ + for ( auto i = 0u; i < rows.size(); ++i ) + { + for ( auto j = i + 1u; j < rows.size(); ++j ) + { + const auto result = ( rows[i] & rows[j] ) & mask; + if ( all_zeros( result ) ) + { + can_be_removed = false; + break; + } + } + + if ( !can_be_removed ) + { + break; + } + } + + if ( can_be_removed ) + { + to_be_removed.push_back( c ); + } + else + { + mask.set_bit( c ); + } + } + + /* remove columns */ + std::reverse( to_be_removed.begin(), to_be_removed.end() ); + + for ( auto index : to_be_removed ) + { + columns.erase( columns.begin() + index ); + + for ( auto& row : rows ) + { + erase_bit( row, index ); + } + } + + return !to_be_removed.empty(); + } + + void erase_bit( row_t& row, unsigned pos ) const + { + for ( auto i = pos + 1; i < unsigned( row.num_literals() ); ++i ) + { + if ( row.get_bit( i ) == 1 ) + row.set_bit( i - 1u ); + else + row.clear_bit( i - 1u ); + } + row.remove_literal( row.num_literals() - 1u ); + } + +public: + std::string columns; + + std::vector rows; + + unsigned char next_gate_id = 'z' + 0x21; +}; + +inline std::ostream& operator<<( std::ostream& os, const unitized_table& table ) +{ + os << table.columns << std::endl; + + for ( const auto& row : table.rows ) + { + std::string buffer; + std::bitset<32> to_print; + for ( auto i = 0; i < row.num_literals(); i++ ) + to_print[i] = row.get_bit( i ); + buffer = to_print.to_string(); + std::string reversed = buffer; + std::reverse( reversed.begin(), reversed.end() ); + os << reversed << std::endl; + } + + return os; +} + +inline bool operator==( unitized_table const& table, unitized_table const& original_table ) +{ + if ( table.rows.size() != original_table.rows.size() ) + return false; + for ( auto x = 0u; x < table.rows.size(); x++ ) + { + if ( table.rows[x] == original_table.rows[x] ) + continue; + else + return false; + } + + return true; +} + +namespace detail +{ +template +class akers_synthesis_impl +{ +public: + akers_synthesis_impl( Ntk ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, LeavesIterator begin, LeavesIterator end ) + : ntk( ntk ), + func( func ), + care( care ), + begin( begin ), + end( end ) + { + } + +public: + signal run() + { + auto table = create_unitized_table(); + return synthesize( table ); + } + +private: + unitized_table create_unitized_table() + { + const unsigned int num_vars = func.num_vars(); + + /* create column names */ + std::string columns; + + for ( auto i = 0u; i < num_vars; ++i ) + { + columns += 'a' + i; + } + for ( auto i = 0u; i < num_vars; ++i ) + { + columns += 'A' + i; + } + columns += '0'; + columns += '1'; + + unitized_table table( columns ); + + /* add rows */ + for ( auto pos = 0u; pos < care.num_bits(); pos++ ) + { + auto half = std::bitset<16>( pos ); + auto row = table.create_row(); + /* copy the values */ + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( half[i] == 0 ) + { + row.clear_bit( i ); + row.set_bit( i + num_vars ); + } + else + { + row.set_bit( i ); + row.clear_bit( i + num_vars ); + } + } + row.clear_bit( num_vars << 1 ); + row.set_bit( ( num_vars << 1 ) + 1 ); + + if ( !kitty::get_bit( func, pos ) ) + { + for ( auto i = 0; i < row.num_literals(); ++i ) + { + if ( row.get_bit( i ) == 0 ) + { + row.set_bit( i ); + } + else + { + row.clear_bit( i ); + } + } + } + table.add_row( row ); + } + table.reduce(); + + return table; + } + + std::set> find_gates_for_column( const unitized_table& table, unsigned column ) const + { + std::vector one_rows; + std::vector matrix; + /* find rows with a 1 at column */ + + for ( const auto& row : table ) + { + if ( row.get_bit( column ) ) + { + auto rt = row; + rt.clear_bit( column ); + one_rows.push_back( rt ); + } + } + + /* find essential rows */ + for ( auto i = 0u; i < one_rows.size(); ++i ) + { + for ( auto j = 0u; j < one_rows.size(); ++j ) + { + if ( i == j ) + { + continue; + } + if ( all_zeros( one_rows[i] & one_rows[j] ) ) + { + for ( auto k = 0; k < one_rows[i].num_literals(); ++k ) + matrix.push_back( one_rows[i].get_bit( k ) ); + break; + } + } + } + return clauses_to_products_enumerative( table, column, matrix ); + } + + std::set find_gate_for_table( unitized_table& table ) + { + + std::map, unsigned> gates; + std::vector> random_gates; + auto g_count = 0u; + + for ( auto c = 0u; c < table.num_columns(); ++c ) + { + for ( const auto& g : find_gates_for_column( table, c ) ) + { + assert( g.size() == 3u ); + gates[g]++; + g_count++; + } + } + + if ( gates.empty() ) + { + reduce++; + return find_gate_for_table_brute_force( table ); + } + if ( gates.size() == previous_size ) + { + reduce++; + return find_gate_for_table_brute_force( table ); + } + + assert( !gates.empty() ); + reduce = 0; + previous_size = gates.size(); + using pair_t = decltype( gates )::value_type; + + for ( auto f = 0u; f < g_count; f++ ) + { + auto pr = std::max_element( std::begin( gates ), std::end( gates ), []( const pair_t& p1, const pair_t& p2 ) { return p1.second < p2.second; } ); + random_gates.push_back( pr->first ); + gates.erase( pr->first ); + if ( gates.size() == 0 ) + break; + } + + auto this_table = table; + for ( auto f = 0u; f < random_gates.size(); f++ ) + { + table.add_gate( random_gates[f] ); + table.reduce(); + if ( ( table.rows.size() != this_table.rows.size() ) || ( table.columns.size() != this_table.columns.size() - 1 ) ) + { + table = this_table; + return random_gates[f]; + } + table = this_table; + } + + reduce++; + return random_gates[0u]; + } + + std::set find_gate_for_table_brute_force( const unitized_table& table ) const + { + auto best_count_iter = std::numeric_limits::max(); + std::set best_gate_iter; + + std::vector numbers( table.num_columns() ); + std::iota( numbers.begin(), numbers.end(), 0u ); + + for ( auto i = 0u; i < table.num_columns(); i++ ) + { + for ( auto j = i + 1u; j < table.num_columns(); j++ ) + { + for ( auto k = j + 1u; k < table.num_columns(); k++ ) + { + std::set gate; + gate.insert( numbers[i] ); + gate.insert( numbers[j] ); + gate.insert( numbers[k] ); + + auto table_copy = table; + table_copy.add_gate( gate ); + + const auto new_count = table_copy.count_essential_ones(); + if ( new_count < best_count_iter ) + { + best_count_iter = new_count; + best_gate_iter = gate; + } + } + } + } + return best_gate_iter; + } + + signal synthesize( unitized_table& table ) + { + + std::unordered_map> c_to_f; + + c_to_f[0x30] = ntk.get_constant( false ); + c_to_f[0x31] = ntk.get_constant( true ); + + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + auto pi = *begin++; // should take the leaves values + c_to_f[0x41 + i] = !pi; + c_to_f[0x61 + i] = pi; + } + + auto last_gate_id = 0; + + while ( table.num_columns() ) + { + auto gate = find_gate_for_table( table ); + + auto it = gate.begin(); + const auto f1 = *it++; + const auto f2 = *it++; + const auto f3 = *it; + + last_gate_id = table.add_gate( gate ); + + c_to_f[last_gate_id] = ntk.create_maj( c_to_f[table[f1]], c_to_f[table[f2]], c_to_f[table[f3]] ); + + if ( reduce == 0 ) + table.reduce(); + } + + if ( ntk.node_to_index( ntk.get_node( c_to_f[last_gate_id] ) ) == 0 ) + return ntk.get_constant( 0 ^ ntk.is_complemented( c_to_f[last_gate_id] ) ); + + return c_to_f[last_gate_id]; + } + + std::vector> create_gates( const unitized_table& table ) + { + const auto num_vars = func.num_vars(); + std::vector count( table.columns.size(), 0 ); + auto best_count = 0; + + std::vector> gates; + + for ( auto c = 0u; c < table.columns.size(); ++c ) + { + for ( const auto& row : table ) + { + best_count++; + if ( row.get_bit( c ) == 0 ) + { + count[c]++; + } + } + } + + auto best_column = 0u; + + for ( auto c = 0u; c < count.size(); ++c ) + { + if ( count[c] < best_count ) + { + best_column = c; + best_count = count[c]; + } + } + + auto icx = 0; + auto name = table.columns[best_column]; + if ( islower( name ) ) + icx = name - 'a'; + else if ( isupper( name ) ) + icx = name - 'A' + num_vars; + else if ( name == '0' ) + icx = num_vars * 2; + else + icx = num_vars * 2 + 1; + + std::vector best_c; + + best_c.push_back( icx ); + gates.push_back( best_c ); + + for ( const auto& row : table ) + { + + if ( row.get_bit( best_column ) == 0 ) + { + std::vector gate1; + for ( auto c = 0u; c < table.num_columns(); ++c ) + { + if ( c == best_column ) + continue; + if ( row.get_bit( c ) == 1 ) + { + unsigned icx; + auto name = table.columns[c]; + if ( islower( name ) ) + icx = name - 'a'; + else if ( isupper( name ) ) + icx = name - 'A' + num_vars; + else if ( name == '0' ) + icx = num_vars * 2; + else + icx = num_vars * 2 + 1; + gate1.push_back( icx ); + } + } + gates.push_back( gate1 ); + } + } + std::vector g; + g.push_back( num_vars ); + + gates.push_back( g ); + return gates; + } + + std::set> clauses_to_products_enumerative( const unitized_table& table, unsigned column, + const std::vector& matrix ) const + { + std::set> products; + + const auto num_columns = table.num_columns(); + const auto num_rows = matrix.size() / num_columns; + + for ( auto i = 0u; i < num_columns; ++i ) + { + if ( table.is_opposite( column, i ) ) + { + continue; + } + if ( column == i ) + continue; + for ( auto j = i + 1u; j < num_columns; ++j ) + { + if ( table.is_opposite( i, j ) || table.is_opposite( column, j ) ) + { + continue; + } + if ( column == j ) + continue; + auto found = true; + std::size_t offset = 0u; + for ( auto r = 0u; r < num_rows; ++r, offset += num_columns ) + { + if ( !matrix[offset + i] && !matrix[offset + j] ) + { + found = false; + break; + } + } + + if ( found ) + { + std::set product; + product.insert( i ); + product.insert( j ); + product.insert( column ); + assert( product.size() == 3 ); + products.insert( product ); + } + } + } + + return products; + } + +private: + Ntk ntk; + kitty::dynamic_truth_table const& func; + kitty::dynamic_truth_table const& care; + LeavesIterator begin; + LeavesIterator end; + + unsigned reduce{ 0 }; + std::size_t previous_size{ 0 }; +}; + +} // namespace detail + +/*! \brief Performs Akers majority-3 synthesis inside network. + * + * Note that the number of variables in `func` and `care` must be the same. + * Also the distance between `begin` and `end` must equal the number of + * variables in `func`. + * + * **Required network functions:** + * - `create_maj` + * + * \param ntk Network + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \param begin Begin iterator to child signals + * \param end End iterator to child signals + * \return Signal that realizes function in terms of child signals + */ +template +signal akers_synthesis( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, LeavesIterator begin, LeavesIterator end ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + + assert( func.num_vars() == care.num_vars() ); + assert( std::distance( begin, end ) == func.num_vars() ); + + auto pi = begin; + + if ( is_const0( func ) ) + return ntk.get_constant( 0 ); + auto tt_1 = unary_not( func ); + if ( is_const0( tt_1 ) ) + return ntk.get_constant( 1 ); + + for ( auto i = 0u; i < func.num_vars(); i++ ) + { + create_nth_var( tt_1, i ); + auto it = *pi++; + if ( tt_1 == func ) + { + return it; + } + tt_1 = unary_not( tt_1 ); + if ( tt_1 == func ) + { + return !it; + } + } + + detail::akers_synthesis_impl tt( ntk, func, care, begin, end ); + return tt.run(); +} + +/*! \brief Performs Akers majority-3 synthesis to create network. + * + * Note that the number of variables in `func` and `care` must be the same. + * The function will create a network with as many primary inputs as number of + * variables in `func` and a single output. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_maj` + * + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \return A network that realizes the function + */ +template +Ntk akers_synthesis( kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + + Ntk ntk; + std::vector> pis; + + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + pis.push_back( ntk.create_pi() ); + } + + const auto f = akers_synthesis( ntk, func, care, pis.begin(), pis.end() ); + ntk.create_po( f ); + return ntk; +} +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp b/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp new file mode 100644 index 0000000..b8f22e3 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_assumptions.hpp @@ -0,0 +1,138 @@ +/* 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 aqfp_assumptions.hpp + \brief Technology assumptions for AQFP + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +/*! \brief More realistic AQFP technology assumptions. */ +struct aqfp_assumptions_realistic +{ + /*! \brief Whether CIs and COs need to be path-balanced. */ + bool balance_cios{ false }; + + /*! \brief Ignores the complementations of COs because they can be merged into register inputs. */ + bool ignore_co_negation{ true }; + + /*! \brief Number of phases per clock cycle (for phase alignment). + * + * Each CO (a node with external reference) must be scheduled at a level being a multiple of + * `num_phases` (i.e., an imaginary CO node should be placed at a level `num_phases * k + 1`). + */ + uint32_t num_phases{ 4u }; + + /*! \brief The maximum number of fanouts a splitter/buffer can have. */ + uint32_t splitter_capacity{ 3u }; + + /*! \brief The maximum number of fanouts a mega splitter can have. */ + //uint32_t mega_splitter_capacity{ 7u }; + + /*! \brief The maximum number of fanouts a CI can have. */ + uint32_t ci_capacity{ 1u }; // simplicity + //uint32_t ci_capacity{ 2u }; // best possible + + /*! \brief The phase offsets (after a change in register input) when new register output is available. + * + * Assumes that the register inputs (D and E) are scheduled at phase 0 (i.e., the last phase of + * the previous clock cycle), a new state is available to be taken at these numbers of phases + * afterwards. + * + * An ascending order is assumed. At least one element should be given. + * + * Each CI must be scheduled at a level `num_phases * k + ci_phases[i]` (for any `i`; for any + * integer `k >= 0` when `balance_cios = false`, or `k=0` otherwise). + */ + std::vector ci_phases{ { 4u } }; // simplicity + //std::vector ci_phases{ { 3u, 4u, 5u } }; // best possible + + /*! \brief Maximum phase-skip (in consideration of clock skew). */ + uint32_t max_phase_skip{ 4u }; +}; + +/*! \brief AQFP technology assumptions. + * + * POs count toward the fanout sizes and always have to be branched. + * If PIs need to be balanced, then they must also need to be branched. + */ +struct aqfp_assumptions_legacy +{ + /*! \brief Whether PIs need to be branched with splitters. */ + bool branch_pis{ true }; + + /*! \brief Whether PIs need to be path-balanced. */ + bool balance_pis{ false }; + + /*! \brief Whether POs need to be path-balanced. */ + bool balance_pos{ false }; + + /*! \brief The maximum number of fanouts each splitter (buffer) can have. */ + uint32_t splitter_capacity{ 3u }; +}; + +using aqfp_assumptions = aqfp_assumptions_legacy; + +/* Temporary helper function to bridge old and new code. */ +inline aqfp_assumptions_realistic legacy_to_realistic( aqfp_assumptions_legacy const& legacy ) +{ + aqfp_assumptions_realistic realistic; + + if ( !legacy.branch_pis ) + { + realistic.ci_capacity = std::numeric_limits::max(); + } + else + { + realistic.ci_capacity = 1u; + } + + if ( legacy.balance_pis && legacy.balance_pos ) + { + realistic.balance_cios = true; + } + else if ( !legacy.balance_pis && !legacy.balance_pos ) + { + realistic.balance_cios = false; + } + else + { + std::cerr << "[e] Cannot convert this combinaiton of assumptions.\n"; + } + + realistic.splitter_capacity = legacy.splitter_capacity; + realistic.num_phases = 1u; // no phase alignment + realistic.ci_phases = {0u}; // PIs at level 0 + realistic.max_phase_skip = std::numeric_limits::max(); // no clock skew issue + return realistic; +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp b/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp new file mode 100644 index 0000000..c269b3f --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_cleanup.hpp @@ -0,0 +1,160 @@ +/* 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 aqfp_cleanup.hpp + \brief Buffered network cleanup and types conversion + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include "../../networks/buffered.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Buffered cleanup dangling. + * + * This function implements `cleanup_dangling` for buffered networks. + * + * \param ntk buffered network type + */ +template +Ntk cleanup_dangling_buffered( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network type" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "Ntk does not implement the create_buf method" ); + + using signal = typename Ntk::signal; + + Ntk res; + node_map old2new( ntk ); + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ ntk.is_complemented( f ) ); + } ); + + signal f; + if ( ntk.is_buf( n ) ) + { + f = res.create_buf( children[0] ); + } + else + { + f = res.clone_node( ntk, n, children ); + } + + old2new[n] = f; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; +} + +/*! \brief Converts a `buffered_mig_network` to a `buffered_aqfp_network`. + * + * This function converts a `buffered_mig_network` to a `buffered_aqfp_network`. + * + * \param ntk `buffered_mig_network` + */ +buffered_aqfp_network convert_buffered_mig_to_aqfp( buffered_mig_network const& ntk ) +{ + using signal = typename buffered_aqfp_network::signal; + + buffered_aqfp_network res; + node_map old2new( ntk ); + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ ntk.is_complemented( f ) ); + } ); + + signal f; + if ( ntk.is_buf( n ) ) + { + f = res.create_buf( children[0] ); + } + else + { + f = res.create_maj( children[0], children[1], children[2] ); + } + + old2new[n] = f; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_db.hpp b/include/mockturtle/algorithms/aqfp/aqfp_db.hpp new file mode 100644 index 0000000..301800e --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_db.hpp @@ -0,0 +1,450 @@ +/* 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 aqfp_db.hpp + \brief AQFP DAG database + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "detail/dag.hpp" +#include "detail/dag_cost.hpp" +#include "detail/npn_cache.hpp" + +namespace mockturtle +{ + +template +T bitwise_majority( const T& a, const T& b, const T& c ) +{ + return ( a & b ) | ( c & ( a | b ) ); +} + +template +T bitwise_majority( const T& a, const T& b, const T& c, const T& d, const T& e ) +{ + return ( a & b & c ) | ( a & b & d ) | ( a & b & e ) | ( a & c & d ) | ( a & c & e ) | + ( a & d & e ) | ( b & c & d ) | ( b & c & e ) | ( b & d & e ) | ( c & d & e ); +} + +/*! \brief Returns the level of input with index `input_idx` from level configuration `lvl_cfg`. */ +inline uint32_t level_of_input( uint64_t lvl_cfg, uint32_t input_idx ) +{ + return ( lvl_cfg >> ( 8u * input_idx ) ) & 0xff; +} + +/*! \brief Returns the vector representation of the level configuration `lvl_cfg`. */ +inline std::vector lvl_cfg_to_vec( uint64_t lvl_cfg, uint32_t num_leaves ) +{ + std::vector res( num_leaves ); + for ( auto i = 0u; i < num_leaves; i++ ) + { + res[i] = level_of_input( lvl_cfg, i ); + } + return res; +} + +/*! \brief Returns the level configuration for levels represented by `levels`. */ +inline uint64_t lvl_cfg_from_vec( std::vector levels ) +{ + uint64_t res = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + res |= ( levels[i] << ( 8u * i ) ); + } + return res; +} + +/*! \brief A class to represent an AQFP exact synthesis database. */ +template> +class aqfp_db +{ + +public: + struct replacement + { + double cost; + Ntk ntk; + std::vector input_levels; // input levels + std::vector input_perm; // input permutation so that ntk compute the npn-class + }; + + aqfp_db( + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), db( get_default_db() ), cc( gate_costs, splitters ) + { + } + + aqfp_db( const std::unordered_map>& db, + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), db( db ), cc( gate_costs, splitters ) + { + } + + using gate_info = std::vector; // fanin list with lsb denoting the inversion + using mig_structure = std::tuple, std::vector, bool>; // (gates, levels, output inverted flag); + + template + mig_structure get_best_replacement( uint64_t f, std::vector _levels, std::vector _is_const, ComparisonFn&& comparison_fn ) + { + /* find the npn class for the function */ + auto tmp = npndb( f ); + auto& npntt = std::get<0>( tmp ); + auto& npnperm = std::get<2>( tmp ); + + if ( db[npntt].empty() ) + { + assert( false ); + return { {}, {}, false }; + } + + /* map input levels */ + std::vector levels( _levels.size() ); + std::vector is_const( _levels.size() ); + for ( auto i = 0u; i < levels.size(); i++ ) + { + levels[i] = _levels[npnperm[i]]; + is_const[i] = _is_const[npnperm[i]]; + } + + double best_cost = std::numeric_limits::infinity(); + uint32_t best_lev = std::numeric_limits::max(); + replacement best = db[npntt].begin()->second; + uint32_t best_ind = 0; + + uint32_t temp_ind = 0; + for ( auto it = db[npntt].begin(); it != db[npntt].end(); it++ ) + { + const auto& lvl_cfg = it->first; + const auto& r = it->second; + + uint32_t max_lev = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + auto temp = levels[i] + level_of_input( lvl_cfg, i ); + max_lev = std::max( max_lev, temp ); + } + uint32_t buffer_count = 0u; + for ( auto i = 0u; i < levels.size(); i++ ) + { + if ( !is_const[i] ) + { + auto temp = levels[i] + level_of_input( lvl_cfg, i ); + buffer_count += ( max_lev - temp ); + } + } + + double cost = buffer_count * splitters.at( 1u ) + r.cost; + + if ( comparison_fn( { cost, max_lev }, { best_cost, best_lev } ) ) + { + best_cost = cost; + best_lev = max_lev; + best = r; + best_ind = temp_ind; + } + temp_ind++; + } + + usage_stats[{ npntt, best_ind }]++; + return compute_replacement_structure( best, f ); + } + + /*! \brief Load database from input stream `is`. */ + void load_db( std::istream& is, uint32_t version = 1u ) + { + load_db( is, db, version ); + } + + /*! \brief Load database from input stream `is`. */ + template + static void load_db( std::istream& is, T& db, uint32_t version = 1u ) + { + std::string line; + + std::getline( is, line ); + uint32_t num_func = std::stoul( line ); + + for ( auto func = 0u; func < num_func; func++ ) + { + uint32_t N = 4; + if ( version > 1u ) + { + std::getline( is, line ); + N = std::stoul( line ); + } + + std::getline( is, line ); + uint64_t npn = std::stoul( line, 0, 16 ); + + std::getline( is, line ); + uint32_t num_entries = std::stoul( line ); + + for ( auto j = 0u; j < num_entries; j++ ) + { + std::getline( is, line ); + uint64_t lvl_cfg = std::stoul( line, 0, 16 ); + auto levels = lvl_cfg_to_vec( lvl_cfg, N ); + + std::getline( is, line ); + double cost = std::stod( line ); + + std::getline( is, line ); + Ntk ntk( line ); + + std::vector perm( N ); + for ( int i = 0; i < N; i++ ) + { + uint32_t t; + is >> t; + perm[i] = t; + } + std::getline( is, line ); // ignore the current line end + + lvl_cfg = lvl_cfg_from_vec( levels ); + + if ( !db[npn].count( lvl_cfg ) || db[npn][lvl_cfg].cost > cost ) + { + db[npn][lvl_cfg] = { cost, ntk, levels, perm }; + } + } + } + } + + void print_usage_state( std::ostream& os ) + { + os << "printing stats\n"; + for ( auto& x : usage_stats ) + { + os << fmt::format( "{}, {}, {}\n", x.first.first, x.first.second, x.second ); + } + os << "printing stats done\n"; + } + + template + void for_each_db_entry( Fn&& func ) + { + for ( const auto& [npn_class, entries_for_npn_class] : db ) + { + for ( const auto& [depth_config, replacement] : entries_for_npn_class ) + { + func( npn_class, compute_replacement_structure( replacement, npn_class ), replacement.cost ); + } + } + } + +private: + std::unordered_map gate_costs; + std::unordered_map splitters; + std::unordered_map> db; + std::map, uint32_t> usage_stats; + dag_aqfp_cost_and_depths cc; + npn_cache npndb; + + std::pair> inverter_config_for_func( const std::vector& input_tt, const Ntk& net, uint64_t func ) + { + uint32_t num_inputs = net.input_slots.size(); + if ( net.zero_input != 0 ) + { + num_inputs--; + } + + std::vector tt( net.nodes.size(), input_tt[0] ); + auto input_ind = 1u; + + auto tmp_input_slots = net.input_slots; + std::stable_sort( tmp_input_slots.begin(), tmp_input_slots.end() ); + assert( tmp_input_slots == net.input_slots ); + + for ( auto i : net.input_slots ) + { + if ( i != (int)net.zero_input ) + { + tt[i] = input_tt[input_ind++]; + } + } + + auto shift = 0u; + for ( auto i = 0u; i < net.num_gates(); i++ ) + { + shift += ( net.nodes[i].size() - 1 ); + } + + const auto n_gates = net.num_gates(); + std::vector res( n_gates ); + + for ( auto inv_config_itr = 0ul; inv_config_itr < ( 1ul << shift ); inv_config_itr++ ) + { + auto inv_config = inv_config_itr; + + for ( auto i = n_gates; i > 0; i-- ) + { + const auto& n = net.nodes[i - 1]; + + const auto n_fanin = n.size(); + const auto shift = n_fanin - 1; + const auto mask = ( 1 << shift ) - 1; + const auto ith_gate_config = ( inv_config & mask ); + + res[i - 1] = ith_gate_config; + + // only consider half the inverter configurations, the other half is covered by output inversion + if ( n_fanin == 3u ) + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + tt[n[2]] ); + } + else + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + ( ith_gate_config & 4 ) ? ~tt[n[2]] : tt[n[2]], + ( ith_gate_config & 8 ) ? ~tt[n[3]] : tt[n[3]], + tt[n[4]] ); + } + inv_config >>= shift; + } + + if ( ( func & 0xffff ) == ( tt[0] & 0xffff ) ) + { + return { false, res }; + } + if ( ( ~func & 0xffff ) == ( tt[0] & 0xffff ) ) + { + return { true, res }; + } + } + + assert( false ); + return {}; + } + + mig_structure compute_replacement_structure( const replacement& rep, uint64_t func ) + { + std::vector levs( 4u ); + for ( auto i = 0u; i < 4u; i++ ) + { + levs[i] = rep.input_levels[rep.input_perm[i]]; + } + + auto [new_cost, gate_levels] = cc( rep.ntk, levs ); + (void)new_cost; + + auto [npntt, npn_inv, npnperm] = npndb( func ); + + std::vector ind = { 0u, 1u, 2u, 3u }; + + std::vector ind_func_from_npn = { + ind[npnperm[0]], + ind[npnperm[1]], + ind[npnperm[2]], + ind[npnperm[3]] }; + + std::vector ind_func_from_dag = { + ind_func_from_npn[rep.input_perm[0]], + ind_func_from_npn[rep.input_perm[1]], + ind_func_from_npn[rep.input_perm[2]], + ind_func_from_npn[rep.input_perm[3]] }; + + auto input_perm = ind_func_from_dag; + + std::vector input_tt = { + 0xaaaaUL, + 0xccccUL, + 0xf0f0UL, + 0xff00UL }; + + std::vector input_perm_tt = { + 0x0000UL, + input_tt[input_perm[0]], + input_tt[input_perm[1]], + input_tt[input_perm[2]], + input_tt[input_perm[3]] }; + + auto [output_inv, inverter_config] = inverter_config_for_func( input_perm_tt, rep.ntk, func ); + + std::vector gates{ {}, {}, {}, {}, {} }; + std::vector depths{ 0u, 0u, 0u, 0u, 0u }; + + std::map sigmap; + + std::vector inputs; + auto i = 0u; + for ( auto x : rep.ntk.input_slots ) + { + if ( x == rep.ntk.zero_input ) + { + sigmap[x] = 0u; + } + else + { + sigmap[x] = input_perm[i++] + 1; + } + depths[sigmap[x]] = gate_levels[x]; + } + + for ( auto i = rep.ntk.num_gates(); i > 0; i-- ) + { + auto j = i - 1; + sigmap[j] = rep.ntk.num_gates() + 4u - j; + + gates.push_back( {} ); + assert( gates.size() == sigmap[j] + 1 ); + depths.push_back( gate_levels[j] ); + + auto type = inverter_config[j]; + auto& node = rep.ntk.nodes[j]; + for ( auto k = 0u; k < node.size(); k++ ) + { + auto new_fanin_id = sigmap[node[k]]; + auto new_fanin_inv = ( type & ( 1u << k ) ) > 0; + gates[sigmap[j]].push_back( ( new_fanin_id << 1 ) | ( new_fanin_inv ? 1u : 0u ) ); + } + } + + return { gates, depths, output_inv }; + } + + static std::unordered_map> get_default_db() + { + return {}; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp b/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp new file mode 100644 index 0000000..d9b41d2 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp @@ -0,0 +1,172 @@ +/* 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 aqfp_fanout_resyn.hpp + \brief AQFP fanout resynthesis strategy + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include "../../traits.hpp" +#include "aqfp_assumptions.hpp" + +namespace mockturtle +{ + +/*! \brief A callback to determine the fanout levels. + * + * This is intended to be used with AQFP networks. Levels are determined assuming that + * a nearly-balanced splitter tree is used for each the considered fanout net. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +struct aqfp_fanout_resyn +{ + /*! \brief Default constructor. + * + * \param assume AQFP synthesis assumptions (only the fields `splitter_capacity` and `branch_pis` will be used) + */ + aqfp_fanout_resyn( const aqfp_assumptions& assume ) + : splitter_capacity{ assume.splitter_capacity }, branch_pis{ assume.branch_pis } {} + + /*! \brief Determine the relative levels of fanouts of a node assuming a nearly balanced splitter tree. + * + * \param ntk_src Source network with `depth()` and `level()` member functions. + * \param n Node in `ntk_src` for which the fanout levels are to be determined. + * \param fanouts_n Fanout nodes of `n` in `ntk_dest`. + * \param ntk_dest Destination network which is being synthesized as an AQFP network. + * \param f The signal in `ntk_dest` that correspond to source node `n`. + * \param level_f The level of `f` in `ntk_dest`. + * \param fanout_node_fn Callback with arguments (source network node, level in destination network). + * \param fanout_co_fn Callback with arguments (index of the combinational output, level in destination network). + */ + template + void operator()( const NtkSrc& ntk_src, node n, FOutsSrc& fanouts_n, const NtkDest& ntk_dest, signal f, uint32_t level_f, + FanoutNodeCallback&& fanout_node_fn, FanoutPoCallback&& fanout_co_fn ) + { + static_assert( has_depth_v, + "The source network does not provide a method named depth()." ); + static_assert( has_level_v, + "The source network does not provide a method named level(node)." ); + static_assert( std::is_invocable_v, uint32_t>, + "FanoutNodeCallback is not callable with arguments (node, level)" ); + static_assert( std::is_invocable_v, + "FanoutNodeCallback is not callable with arguments (index, level)" ); + + if ( ntk_src.fanout_size( n ) == 0 ) + return; + + auto offsets = balanced_splitter_tree_offsets( ntk_src.fanout_size( n ) ); + + std::stable_sort( fanouts_n.begin(), fanouts_n.end(), [&]( auto f1, auto f2 ) { return ( ntk_src.depth() - ntk_src.level( f1 ) ) > ( ntk_src.depth() - ntk_src.level( f2 ) ) || + ( ( ntk_src.depth() - ntk_src.level( f1 ) ) == ( ntk_src.depth() - ntk_src.level( f2 ) ) && ( f1 < f2 ) ); } ); + + auto n_dest = ntk_dest.get_node( f ); + auto no_splitters = ntk_dest.is_constant( n_dest ) || ( !branch_pis && ntk_dest.is_ci( n_dest ) ); + + uint32_t foind = 0u; + for ( auto fo : fanouts_n ) + { + fanout_node_fn( fo, no_splitters ? level_f : level_f + offsets[foind] ); + foind++; + } + + // remaining fanouts are either combinational outputs (primary outputs or register inputs) + for ( auto i = foind; i < ntk_src.fanout_size( n ); i++ ) + { + auto co_index = i - foind; + fanout_co_fn( co_index, no_splitters ? level_f : level_f + offsets[foind] ); + foind++; + } + } + +private: + uint32_t splitter_capacity; + bool branch_pis; + + /*! \brief Determines the relative levels of the fanouts of a balanced splitter tree with `num_fanouts` many fanouts. */ + std::vector balanced_splitter_tree_offsets( uint32_t num_fanouts ) + { + if ( num_fanouts == 1u ) + { + return { 0u }; + } + + // to get the minimum level, choose the splitters with max fanout size + + uint32_t num_splitters = 1u; + uint32_t num_levels = 1u; + uint32_t num_leaves = splitter_capacity; + + while ( num_leaves < num_fanouts ) + { + num_splitters += num_leaves; + num_leaves *= splitter_capacity; + num_levels += 1u; + } + + // we need at least `num_levels` levels, but we might not need all + std::vector result( num_fanouts, num_levels ); + uint32_t i = 0u; + while ( num_leaves >= num_fanouts + ( splitter_capacity - 1 ) ) + { + num_splitters--; + num_leaves -= ( splitter_capacity - 1 ); + result[i++]--; + } + + return result; + }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp b/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp new file mode 100644 index 0000000..9de5920 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_legalization.hpp @@ -0,0 +1,331 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file aqfp_legalization.hpp + \brief Legalization + buffer optimization flow for AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../networks/aqfp.hpp" +#include "../../networks/buffered.hpp" +#include "../../networks/mig.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "../cleanup.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_rebuild.hpp" +#include "aqfp_retiming.hpp" +#include "buffer_insertion.hpp" + +namespace mockturtle +{ + +struct aqfp_legalization_params +{ + aqfp_legalization_params() + { + aqfp_assumptions_ps.branch_pis = true; + aqfp_assumptions_ps.balance_pis = true; + aqfp_assumptions_ps.balance_pos = true; + } + + /*! \brief legalization mode. */ + enum + { + better, + portfolio + } legalization_mode = portfolio; + + /*! \brief AQFP technology assumptions. */ + aqfp_assumptions_legacy aqfp_assumptions_ps{}; + + /*! \brief Max number of optimization rounds (zero performs only insertion)*/ + uint32_t optimization_rounds{ 10 }; + + /*! \brief Maximum chunk size for chunk optimization. */ + uint32_t max_chunk_size{ 100 }; + + /*! \brief Maximum number of iterations for optimization using retiming. */ + uint32_t retime_iterations{ 250 }; + + /*! \brief Enable optimization of splitters using retiming. */ + bool retime_splitters{ true }; + + /*! \brief Enables the randomization of topological order. */ + bool topological_randomization{ true }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_legalization_stats +{ + /*! \brief Number of Josephson Junctions (JJs). */ + uint32_t num_jjs{ 0 }; + + /*! \brief Number of buffers and splitters. */ + uint32_t num_bufs{ 0 }; + + /*! \brief Depth of the circuit. */ + uint32_t depth{ 0 }; + + /*! \brief Total number of optimization rounds. */ + uint32_t rounds_total{ 0 }; + + /*! \brief Time insertion. */ + stopwatch<>::duration time_insertion{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] JJs = {:7d}\t B/S = {:7d}\t Depth = {:5d}\n", num_jjs, num_bufs, depth ); + std::cout << fmt::format( "[i] Rounds = {:7d}\t Time Insertion = {:>5.2f} secs\t Total Runtime = {:>5.2f} secs\n", rounds_total, to_seconds( time_insertion ), to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class aqfp_legalization_impl +{ +public: + explicit aqfp_legalization_impl( Ntk const& ntk, aqfp_legalization_params const& ps, aqfp_legalization_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ) + { + } + +public: + buffered_aqfp_network run() + { + stopwatch t( _st.time_total ); + _st.rounds_total = 0; + + /* convert the initial circuit as an AQFP network */ + aqfp_network aqfp_start = cleanup_dangling( _ntk ); + /* creates buffer_insertion instance for scheduling (can be reused) */ + buffer_insertion_params insertion_ps; + insertion_ps.optimization_effort = buffer_insertion_params::none; + insertion_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + buffer_insertion buf_inst( aqfp_start, insertion_ps ); + + bool retiming_backward_first = false; + + /* Starndard flow: insertion + optimization */ + if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buffered_aqfp_network buffered_aqfp = aqfp_buffer_insertion( buf_inst, retiming_backward_first ); + buffered_aqfp_network aqfp_res = aqfp_buffer_optimize( buffered_aqfp, retiming_backward_first ); + compute_stats( aqfp_res ); + return aqfp_res; + } + + /* Portfolio flow: 2 insertions + 2 optimizations */ + buffered_aqfp_network buffered_aqfp_alap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, true ); + buffered_aqfp_network aqfp_res_alap = aqfp_buffer_optimize( buffered_aqfp_alap, retiming_backward_first ); + + buffered_aqfp_network buffered_aqfp_asap = aqfp_buffer_insertion( buf_inst, retiming_backward_first, false ); + buffered_aqfp_network aqfp_res_asap = aqfp_buffer_optimize( buffered_aqfp_asap, retiming_backward_first ); + + buffered_aqfp_network aqfp_res; + if ( aqfp_res_alap.size() < aqfp_res_asap.size() ) + { + aqfp_res = aqfp_res_alap; + } + else + { + aqfp_res = aqfp_res_asap; + } + + compute_stats( aqfp_res ); + return aqfp_res; + } + +private: + buffered_aqfp_network aqfp_buffer_insertion( buffer_insertion& buf_inst, bool& direction, bool is_alap = false ) + { + stopwatch t( _st.time_insertion ); + + if ( _ps.legalization_mode == aqfp_legalization_params::portfolio ) + { + if ( is_alap ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ALAP_depth ); + } + else + { + buf_inst.set_scheduling_policy( buffer_insertion_params::ASAP_depth ); + } + } + else if ( _ps.legalization_mode == aqfp_legalization_params::better ) + { + buf_inst.set_scheduling_policy( buffer_insertion_params::better_depth ); + } + + buffered_aqfp_network buffered_aqfp; + buf_inst.run( buffered_aqfp ); + direction = buf_inst.is_scheduled_ASAP(); + + return buffered_aqfp; + } + + buffered_aqfp_network aqfp_buffer_optimize( buffered_aqfp_network& start, bool direction ) + { + if ( _ps.optimization_rounds == 0 ) + return start; + + /* retiming params */ + aqfp_retiming_params aps; + aps.aqfp_assumptions_ps = _ps.aqfp_assumptions_ps; + aps.backwards_first = direction; + aps.iterations = _ps.retime_iterations; + aps.retime_splitters = _ps.retime_splitters; + + /* chunk movement params */ + buffer_insertion_params buf_ps; + buf_ps.scheduling = buffer_insertion_params::provided; + buf_ps.optimization_effort = buffer_insertion_params::until_sat; + buf_ps.max_chunk_size = _ps.max_chunk_size; + buf_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + + aqfp_reconstruct_params reconstruct_ps; + aqfp_reconstruct_stats reconstruct_st; + reconstruct_ps.buffer_insertion_ps = buf_ps; + + /* aqfp network */ + buffered_aqfp_network buffered_aqfp; + + /* first retiming */ + { + auto buf_aqfp_ret = aqfp_retiming( start, aps ); + buffered_aqfp = buf_aqfp_ret; + } + + /* repeat loop */ + uint32_t iterations = _ps.optimization_rounds; + aps.det_randomization = _ps.topological_randomization; + std::default_random_engine rng( 111 ); + while ( iterations-- > 0 ) + { + uint32_t size_previous = buffered_aqfp.size(); + + /* chunk movement */ + auto buf_aqfp_chunk = aqfp_reconstruct( buffered_aqfp, reconstruct_ps, &reconstruct_st ); + + /* retiming */ + aps.seed = rng(); + auto buf_aqfp_ret = aqfp_retiming( buf_aqfp_chunk, aps ); + + _st.rounds_total++; + + if ( buf_aqfp_ret.size() >= size_previous ) + break; + + buffered_aqfp = buf_aqfp_ret; + } + + return buffered_aqfp; + } + + void compute_stats( buffered_aqfp_network const& buffered_aqfp ) + { + _st.depth = depth_view( buffered_aqfp ).depth(); + _st.num_bufs = 0; + _st.num_jjs = 0; + + buffered_aqfp.foreach_node( [&]( auto const& n ) { + if ( buffered_aqfp.is_pi( n ) || buffered_aqfp.is_constant( n ) ) + return; + if ( buffered_aqfp.is_buf( n ) ) + { + _st.num_jjs += 2; + _st.num_bufs++; + } + else + { + _st.num_jjs += 6; + } + } ); + } + +private: + Ntk const& _ntk; + aqfp_legalization_params const& _ps; + aqfp_legalization_stats& _st; +}; + +} /* namespace detail */ + +/*! \brief AQFP legalization. + * + * This function returns an optimized AQFP circuit + * derived from the input one by inserting buffer + * and splitter elements and optimizing their number. + * + * Parameters can be used to set the B/S insertion and + * optimization. + * + * \param ntk Boolean network as an MIG or AQFP network + * \param ps AQFP legalization parameters + */ +template +buffered_aqfp_network aqfp_legalization( Ntk const& ntk, aqfp_legalization_params const& ps = {}, aqfp_legalization_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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( std::is_same_v || std::is_same_v, "Ntk in not an MIG or AQFP network type" ); + + aqfp_legalization_stats st; + + detail::aqfp_legalization_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp b/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp new file mode 100644 index 0000000..4cfe6f3 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp @@ -0,0 +1,253 @@ +/* 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 aqfp_node_resyn.hpp + \brief AQFP node resynthesis strategy + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include "../../traits.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_db.hpp" + +namespace mockturtle +{ + +/*! \brief Strategy for resynthesizing a node. */ +enum class aqfp_node_resyn_strategy +{ + area, /*!< choose the database entry that gives the minimum area and break ties uing the delay */ + delay /*!< choose the database entry that gives the minimum delay and break ties uing the cost */ +}; + +/*! \brief AQFP node re-synthesis parameters. */ +struct aqfp_node_resyn_param +{ + aqfp_assumptions assume = { false, false, true, 4u }; + std::unordered_map splitters = { { 1u, 2.0 }, { 4u, 2.0 } }; + aqfp_node_resyn_strategy strategy = aqfp_node_resyn_strategy::area; +}; + +/*! \brief A callback to re-synthesize a node in an AQFP network. + * + * A suitable AQFP sub-graph structure for a given node will be chosen from + * a database of AQFP structures. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +struct aqfp_node_resyn +{ + + /*! \brief Default constructor. + * + * \param db AQFP database. + * \param ps AQFP note re-synthesis parameters. + */ + aqfp_node_resyn( aqfp_db<>& db, const aqfp_node_resyn_param& ps ) : params( ps ), db( db ) + { + /*! must provide the cost of a simple buffer with one output */ + assert( ps.splitters.count( 1u ) > 0 ); + /*! must provide the cost of a splitter with the maximum capacity */ + assert( ps.splitters.count( ps.assume.splitter_capacity ) > 0 ); + } + + /*! Re-synthesize a given node as a sub-graph in an AQFP network. + * + * \param ntk_dest AQFP network that is being synthesized. + * \param f Function computed by the source node that is being re-synthesized. + * \param leaves_begin Iterator (begin) for the leaves of the source node. + * \param leaves_end Iterator (end) for the leaves of the source node. + * \param level_update_callback Callback with parameters (new node, level of the new node). + * \param resyn_performed_callback Callback with the signal that correspond to the source node and its level. + */ + template + void operator()( NtkDest& ntk_dest, const TruthTable& f, LeavesIterator leaves_begin, LeavesIterator leaves_end, + LevelUpdateCallback&& level_update_callback, ResynPerformedCallback&& resyn_performed_callback ) + { + static_assert( std::is_invocable_v, uint32_t>, + "LevelUpdateCallback must be callable with arguments of types (node, level)" ); + static_assert( std::is_invocable_v, uint32_t>, + "ResynPerformedCallback must be callable with arguments of type (signal, level)" ); + + std::vector> leaves; + std::vector leaf_levels; + std::vector leaf_no_splitters; + for ( auto it = leaves_begin; it != leaves_end; it++ ) + { + auto leaf = std::get<0>( *it ); + auto leaf_level = std::get<1>( *it ); + + leaves.push_back( leaf ); + leaf_levels.push_back( leaf_level ); + leaf_no_splitters.push_back( ntk_dest.is_constant( ntk_dest.get_node( leaf ) ) || ( ntk_dest.is_ci( ntk_dest.get_node( leaf ) ) && !params.assume.branch_pis ) ); + } + + // should not have more than 4 fanin nodes + assert( leaves.size() <= 4u ); + + // if less than 4 fanins, add dummy inputs + while ( leaves.size() < 4u ) + { + leaves.push_back( ntk_dest.get_constant( false ) ); + leaf_levels.push_back( 0u ); + leaf_no_splitters.push_back( true ); + } + + auto tt = kitty::extend_to( f, 4u ); + + auto new_n = ntk_dest.get_constant( false ); + auto n_lev = 0u; + switch ( tt._bits[0] ) + { + case 0x0000u: + new_n = ntk_dest.get_constant( false ); + break; + case 0xffffu: + new_n = ntk_dest.get_constant( true ); + break; + case 0x5555u: + new_n = !leaves[0]; + n_lev = leaf_levels[0]; + break; + case 0xaaaau: + new_n = leaves[0]; + n_lev = leaf_levels[0]; + break; + case 0x3333u: + new_n = !leaves[1]; + n_lev = leaf_levels[1]; + break; + case 0xccccu: + new_n = leaves[1]; + n_lev = leaf_levels[1]; + break; + case 0x0f0fu: + new_n = !leaves[2]; + n_lev = leaf_levels[2]; + break; + case 0xf0f0u: + new_n = leaves[2]; + n_lev = leaf_levels[2]; + break; + case 0x00ffu: + new_n = !leaves[3]; + n_lev = leaf_levels[3]; + break; + case 0xff00u: + new_n = leaves[3]; + n_lev = leaf_levels[3]; + break; + default: + auto [mig, depths, output_inv] = db.get_best_replacement( + tt._bits[0], leaf_levels, leaf_no_splitters, + [&]( const std::pair& f, const std::pair& s ) { + if ( params.strategy == aqfp_node_resyn_strategy::area ) + { + return ( f.first < s.first || ( f.first == s.first && f.second < s.second ) ); + } + else + { + assert( params.strategy == aqfp_node_resyn_strategy::delay ); + return ( f.second < s.second || ( f.second == s.second && f.first < s.first ) ); + } + } ); + + std::vector> sig_map( mig.size() ); + std::vector lev_map( mig.size() ); + + sig_map[0] = ntk_dest.get_constant( false ); + lev_map[0] = 0u; + for ( auto i = 1u; i <= 4u; i++ ) + { + sig_map[i] = leaves[i - 1]; + lev_map[i] = leaf_levels[i - 1]; + } + for ( auto i = 5u; i < mig.size(); i++ ) + { + std::vector> fanin; + for ( auto fin : mig[i] ) + { + const auto fin_inv = ( ( fin & 1u ) == 1u ); + const auto fin_id = ( fin >> 1 ); + fanin.push_back( fin_inv ? !sig_map[fin_id] : sig_map[fin_id] ); + } + sig_map[i] = ntk_dest.create_maj( fanin ); + lev_map[i] = 0u; + + const auto node_i = ntk_dest.get_node( sig_map[i] ); + if ( !( ntk_dest.is_constant( node_i ) || ntk_dest.is_ci( node_i ) ) ) + { + for ( auto fin : mig[i] ) + { + const auto fin_id = ( fin >> 1u ); + const auto lev_dif = ( depths[fin_id] > depths[i] ) ? depths[fin_id] - depths[i] : 1u; + lev_map[i] = std::max( lev_map[i], lev_map[fin_id] + lev_dif ); + } + } + + level_update_callback( ntk_dest.get_node( sig_map[i] ), lev_map[i] ); + } + n_lev = lev_map[mig.size() - 1]; + new_n = output_inv ? !sig_map[mig.size() - 1] : sig_map[mig.size() - 1]; + } + + resyn_performed_callback( new_n, n_lev ); + } + +private: + aqfp_node_resyn_param params; + aqfp_db<>& db; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp b/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp new file mode 100644 index 0000000..880d7c2 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_rebuild.hpp @@ -0,0 +1,280 @@ +/* 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 aqfp_rebuild.hpp + \brief Rebuilds buffer-splitter tree in AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../networks/buffered.hpp" +#include "../../networks/generic.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_cleanup.hpp" +#include "buffer_insertion.hpp" +#include "buffer_verification.hpp" + +namespace mockturtle +{ + +struct aqfp_reconstruct_params +{ + /*! \brief AQFP buffer insertion parameters. */ + buffer_insertion_params buffer_insertion_ps{}; + + /*! \brief Randomize topological order. */ + bool det_randomization{ false }; + + /*! \brief Seed for random selection of splitters to relocate. */ + std::default_random_engine::result_type seed{ 1 }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_reconstruct_stats +{ + /*! \brief Number of buffers and splitters after reconstruction. */ + uint32_t num_buffers{ 0 }; + + /*! \brief Total runtime */ + stopwatch<>::duration total_time{ 0 }; + + /*! \brief Report stats */ + void report() + { + std::cout << fmt::format( "[i] Buffers = {}\t Total time = {}\n", num_buffers, to_seconds( total_time ) ); + } +}; + +namespace detail +{ + +class aqfp_reconstruct_impl +{ +public: + using node = typename aqfp_network::node; + using signal = typename aqfp_network::signal; + +public: + explicit aqfp_reconstruct_impl( buffered_aqfp_network const& ntk, aqfp_reconstruct_params const& ps, aqfp_reconstruct_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ), _topo_order() + { + } + + buffered_aqfp_network run() + { + stopwatch( _st.total_time ); + + /* save the level of each node */ + depth_view ntk_level{ _ntk }; + + /* create a network removing the splitter trees */ + aqfp_network clean_ntk; + node_map old2new( _ntk ); + remove_splitter_trees( clean_ntk, old2new ); + + /* compute the node level on the new network */ + node_map levels( clean_ntk ); + _ntk.foreach_gate( [&]( auto const& n ) { + levels[old2new[n]] = ntk_level.level( n ); + } ); + + uint32_t max_po_level = 0; + clean_ntk.foreach_po( [&]( auto const& f ){ + uint32_t spl = std::ceil( std::log( clean_ntk.fanout_size( clean_ntk.get_node( f ) ) ) / std::log( _ps.buffer_insertion_ps.assume.splitter_capacity ) ); + max_po_level = std::max( max_po_level, levels[f] + spl ); + }); + std::vector po_levels; + for ( auto i = 0u; i < _ntk.num_pos(); ++i ) + { + po_levels.emplace_back( max_po_level + 1 ); + } + + /* recompute splitter trees and return the new buffered network */ + buffered_aqfp_network res; + buffer_insertion buf_inst( clean_ntk, levels, po_levels, _ps.buffer_insertion_ps ); + _st.num_buffers = buf_inst.run( res ); + return res; + } + +private: + void remove_splitter_trees( aqfp_network& res, node_map& old2new ) + { + topo_sorting(); + + old2new[_ntk.get_constant( false )] = res.get_constant( false ); + _ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + for ( auto const& n : _topo_order ) + { + if ( _ntk.is_pi( n ) || _ntk.is_constant( n ) ) + continue; + + std::vector children; + _ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + + if ( _ntk.is_buf( n ) ) + { + old2new[n] = children[0]; + } + else if ( children.size() == 3 ) + { + old2new[n] = res.create_maj( children[0], children[1], children[2] ); + } + else + { + old2new[n] = res.create_maj( children ); + } + } + + _ntk.foreach_po( [&]( auto const& f ) { + res.create_po( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + } + + void topo_sorting() + { + _ntk.incr_trav_id(); + _ntk.incr_trav_id(); + _topo_order.reserve( _ntk.size() ); + + seed = _ps.seed; + + /* constants and PIs */ + const auto c0 = _ntk.get_node( _ntk.get_constant( false ) ); + _topo_order.push_back( c0 ); + _ntk.set_visited( c0, _ntk.trav_id() ); + + if ( const auto c1 = _ntk.get_node( _ntk.get_constant( true ) ); _ntk.visited( c1 ) != _ntk.trav_id() ) + { + _topo_order.push_back( c1 ); + _ntk.set_visited( c1, _ntk.trav_id() ); + } + + _ntk.foreach_ci( [&]( auto const& n ) { + if ( _ntk.visited( n ) != _ntk.trav_id() ) + { + _topo_order.push_back( n ); + _ntk.set_visited( n, _ntk.trav_id() ); + } + } ); + + _ntk.foreach_co( [&]( auto const& f ) { + /* node was already visited */ + if ( _ntk.visited( _ntk.get_node( f ) ) == _ntk.trav_id() ) + return; + + topo_sorting_rec( _ntk.get_node( f ) ); + } ); + } + + void topo_sorting_rec( node const& n ) + { + /* is permanently marked? */ + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return; + + /* ensure that the node is not temporarily marked */ + assert( _ntk.visited( n ) != _ntk.trav_id() - 1 ); + + /* mark node temporarily */ + _ntk.set_visited( n, _ntk.trav_id() - 1 ); + + /* mark children */ + if ( !_ps.det_randomization ) + { + _ntk.foreach_fanin( n, [this]( signal const& f ) { + topo_sorting_rec( _ntk.get_node( f ) ); + } ); + } + else + { + std::vector fanins; + _ntk.foreach_fanin( n, [this, &fanins]( signal const& f ) { + fanins.push_back( _ntk.get_node( f ) ); + } ); + std::shuffle( fanins.begin(), fanins.end(), std::default_random_engine( seed++ ) ); + + for ( node const& g : fanins ) + topo_sorting_rec( g ); + } + + /* mark node n permanently */ + _ntk.set_visited( n, _ntk.trav_id() ); + + /* visit node */ + _topo_order.push_back( n ); + } + +private: + buffered_aqfp_network const& _ntk; + aqfp_reconstruct_params const& _ps; + aqfp_reconstruct_stats& _st; + + std::vector _topo_order; + std::default_random_engine::result_type seed{ 1 }; +}; + +} /* namespace detail */ + +/*! \brief Rebuilds buffer/splitter trees in an AQFP network. + * + * This function rebuilds buffer/splitter trees in an AQFP network. + * + * \param ntk Buffered AQFP network + */ +buffered_aqfp_network aqfp_reconstruct( buffered_aqfp_network const& ntk, aqfp_reconstruct_params const& ps = {}, aqfp_reconstruct_stats* pst = nullptr ) +{ + aqfp_reconstruct_stats st; + + detail::aqfp_reconstruct_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( pst ) + *pst = st; + + if ( ps.verbose ) + st.report(); + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp b/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp new file mode 100644 index 0000000..0021f41 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp @@ -0,0 +1,378 @@ +/* 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 aqfp_resynthesis.hpp + \brief Resynthesis of path balanced networks + + \author Dewmini Sudara Marakkalage + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_params` holds configurable parameters + * with default arguments for `node_resynthesis`. + */ +struct aqfp_resynthesis_params +{ + bool verbose{ false }; +}; + +/*! \brief Statistics of aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_stats` holds data collected during AQFP re-synthesis. + */ +struct aqfp_resynthesis_stats +{ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>8.2f} secs\n", to_seconds( time_total ) ); + } +}; + +/*! \brief Results of aqfp_resynthesis. + * + * The data structure `aqfp_resynthesis_result` holds the resulting level assignment of nodes + * and the level of the critical primary output. + */ +template +struct aqfp_resynthesis_result +{ + std::unordered_map, uint32_t> node_level; + std::unordered_map, uint32_t> po_level; + + uint32_t critical_po_level() + { + return max_element( po_level.begin(), po_level.end(), [&]( auto n1, auto n2 ) { return n1.second < n2.second; } ) + ->second; + } +}; + +namespace detail +{ + +/*! \brief Implementation of the AQFP re-synthesis algorithm. */ +template +class aqfp_resynthesis_impl +{ +public: + aqfp_resynthesis_impl( + NtkDest& ntk_dest, + NtkSrc const& ntk_src, + NodeResynFn&& node_resyn_fn, + FanoutResynFn&& fanout_resyn_fn, + aqfp_resynthesis_params const& ps, + aqfp_resynthesis_stats& st ) + : ntk_dest( ntk_dest ), + ntk_src( ntk_src ), + node_resyn_fn( node_resyn_fn ), + fanout_resyn_fn( fanout_resyn_fn ), + ps( ps ), + st( st ) + { + } + + aqfp_resynthesis_result run() + { + stopwatch t( st.time_total ); + + node_map, NtkSrc> node2new( ntk_src ); + node_map level_of_src_node( ntk_src ); + + std::unordered_map, uint32_t> level_of_node; + std::unordered_map, uint32_t> po_level_of_node; + std::map, node>, uint32_t> level_for_fanout; + + std::unordered_map, std::vector>> fanouts; + ntk_src.foreach_gate( [&]( auto n ) { ntk_src.foreach_fanin( n, [&]( auto fi ) { fanouts[ntk_src.get_node( fi )].push_back( n ); } ); } ); + + depth_view ntk_depth{ ntk_src }; + topo_view ntk_topo{ ntk_depth }; + + /* map constants */ + auto c0 = ntk_dest.get_constant( false ); + node2new[ntk_src.get_node( ntk_src.get_constant( false ) )] = c0; + level_of_node[ntk_dest.get_node( c0 )] = 0u; + + if ( ntk_src.get_node( ntk_src.get_constant( true ) ) != ntk_src.get_node( ntk_src.get_constant( false ) ) ) + { + auto c1 = ntk_dest.get_constant( true ); + node2new[ntk_src.get_node( ntk_src.get_constant( true ) )] = c1; + level_of_node[ntk_dest.get_node( c1 )] = 0u; + } + + /* map primary inputs */ + ntk_src.foreach_pi( [&]( auto n ) { + auto pi = ntk_dest.create_pi(); + node2new[n] = pi; + level_of_node[ntk_dest.get_node( pi )] = 0u; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk_src.get_name( ntk_src.make_signal( n ) ) ); + } + + /* synthesize fanout net of `n`*/ + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = level; + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], 0u, fanout_node_callback, fanout_po_callback ); } ); + + /* map register outputs */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk_src.foreach_ro( [&]( auto n, auto i ) { + auto ro = ntk_dest.create_ro(); + node2new[n] = ro; + level_of_node[ntk_dest.get_node( ro )] = 0u; + + ntk_dest.set_register( i, ntk_src.register_at( i ) ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk_src.get_name( ntk_src.make_signal( n ) ) ); + } + + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = level; + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], 0u, fanout_node_callback, fanout_po_callback ); } ); + } + + /* map nodes */ + ntk_topo.foreach_node( [&]( auto n ) { + if ( ntk_topo.is_constant( n ) || ntk_topo.is_ci( n ) ) + return; + + /* synthesize node `n` */ + std::vector, uint32_t>> children; + ntk_topo.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( { ntk_topo.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f], level_for_fanout[{ ntk_topo.get_node( f ), n }] } ); + } ); + + auto performed_resyn = false; + + auto level_update_callback = + [&]( const auto& n, uint32_t level ) { + if ( !level_of_node.count( n ) ) + { + level_of_node[n] = level; + } + }; + + auto resyn_performed_callback = + [&]( const auto& f, auto new_level ) { + node2new[n] = f; + level_of_src_node[n] = new_level; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk_topo.has_name( ntk_topo.make_signal( n ) ) ) + ntk_dest.set_name( f, ntk_topo.get_name( ntk_topo.make_signal( n ) ) ); + } + + performed_resyn = true; + }; + + node_resyn_fn( ntk_dest, ntk_topo.node_function( n ), children.begin(), children.end(), level_update_callback, resyn_performed_callback ); + + if ( !performed_resyn ) + { + fmt::print( "[e] could not perform resynthesis for node {} in node_resynthesis\n", ntk_topo.node_to_index( n ) ); + std::abort(); + } + + /* synthesize fanout net of `n` */ + auto fanout_node_callback = [&]( const auto& f, const auto& level ) { + level_for_fanout[{ n, f }] = std::max(level_for_fanout[{n, f}], level); + }; + + auto fanout_po_callback = [&]( const auto& index, const auto& level ) { + (void)index; + auto node = ntk_dest.get_node(node2new[n]); + po_level_of_node[node] = std::max(po_level_of_node[node], level); + }; + + fanout_resyn_fn( ntk_topo, n, fanouts[n], ntk_dest, node2new[n], level_of_src_node[n], fanout_node_callback, fanout_po_callback ); } ); + + /* map primary outputs */ + ntk_src.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk_src.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_po( o ); + + assert(ntk_dest.is_constant(ntk_dest.get_node(o)) || po_level_of_node.count(ntk_dest.get_node(o)) > 0); + assert(ntk_dest.is_constant(ntk_dest.get_node(o)) || po_level_of_node.at(ntk_dest.get_node(o)) >= level_of_node.at(ntk_dest.get_node(o))); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk_src.has_output_name( index ) ) + { + ntk_dest.set_output_name( index, ntk_src.get_output_name( index ) ); + } + } } ); + + /* map register inputs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk_src.foreach_ri( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk_src.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_ri( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk_src.has_output_name( index ) ) + { + ntk_dest.set_output_name( index + ntk_src.num_pos(), ntk_src.get_output_name( index + ntk_src.num_pos() ) ); + } + } } ); + } + + return { level_of_node, po_level_of_node }; + } + +private: + NtkDest& ntk_dest; + NtkSrc const& ntk_src; + NodeResynFn&& node_resyn_fn; + FanoutResynFn&& fanout_resyn_fn; + aqfp_resynthesis_params const& ps; + aqfp_resynthesis_stats& st; +}; + +} /* namespace detail */ + +/*! \brief Re-synthesize a given source network as a path-balanced AQFP network. + * + * The algorithm outputs an AQFP network with level assignments to its nodes and combinational outputs. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aqfp_assumptions assume = { false, false, true, 4u }; + aqfp_fanout_resyn fanout_resyn{ assume }; + + std::unordered_map gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }; + std::unordered_map splitters = { { 1u, 2.0 }, { assume.splitter_capacity, 2.0 } }; + aqfp_node_resyn_param ps{ assume, splitters, aqfp_node_resyn_strategy::delay }; + + aqfp_db<> db( gate_costs, splitters ); + db.load_db( ... ); // from an input-stream (e.g., std::ifstream or std::stringstream) + + aqfp_node_resyn node_resyn( db, ps ); + + klut_network src_ntk = ...; + aqfp_network dst_ntk; + auto res = aqfp_resynthesis( dst_ntk, src_ntk, node_resyn, fanout_resyn ); + + \endverbatim + */ +template +aqfp_resynthesis_result aqfp_resynthesis( + NtkDest& ntk_dest, + NtkSrc const& ntk_src, + NodeResynFn&& node_resyn_fn, + FanoutResynFn&& fanout_resyn_fn, + aqfp_resynthesis_params const& ps = { false }, + aqfp_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSrc does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + aqfp_resynthesis_stats st; + + detail::aqfp_resynthesis_impl p( ntk_dest, ntk_src, node_resyn_fn, fanout_resyn_fn, ps, st ); + auto result = p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp b/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp new file mode 100644 index 0000000..65abffb --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/aqfp_retiming.hpp @@ -0,0 +1,681 @@ +/* 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 aqfp_retiming.hpp + \brief Retiming for AQFP networks + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../networks/buffered.hpp" +#include "../../networks/generic.hpp" +#include "../../networks/klut.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/choice_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../retiming.hpp" +#include "aqfp_assumptions.hpp" +#include "aqfp_rebuild.hpp" + +namespace mockturtle +{ + +struct aqfp_retiming_params +{ + /*! \brief AQFP technology assumptions. */ + aqfp_assumptions aqfp_assumptions_ps{}; + + /*! \brief Max number of iterations */ + uint32_t iterations{ UINT32_MAX }; + + /*! \brief Enable splitter retiming. */ + bool retime_splitters{ true }; + + /*! \brief Order of retiming is backward first. */ + bool backwards_first{ true }; + + /*! \brief Adds an additional try for retiming */ + uint32_t additional_try_iterations{ 1 }; + + /*! \brief Forward retiming only. */ + bool forward_only{ false }; + + /*! \brief Backward retiming only. */ + bool backward_only{ false }; + + /*! \brief Random seed. */ + std::default_random_engine::result_type seed{ 1 }; + + /*! \brief Randomize the network. */ + bool det_randomization{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct aqfp_retiming_stats +{ + /*! \brief Initial number of buffers/splitters. */ + uint32_t buffers_pre{ 0 }; + + /*! \brief Number of buffers/splitters after the algorithm. */ + uint32_t buffers_post{ 0 }; + + /*! \brief Total iterations. */ + uint32_t rounds_total{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Initial B/S = {:7d}\t Final B/S = {:7d}\n", buffers_pre, buffers_post ); + std::cout << fmt::format( "[i] Total rounds = {:7d}\n", rounds_total ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class aqfp_retiming_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using signal_g = typename generic_network::signal; + using classes_t = std::vector>; + +public: + explicit aqfp_retiming_impl( Ntk& ntk, aqfp_retiming_params const& ps, aqfp_retiming_stats& st ) + : _ntk( ntk ), _ps( ps ), _st( st ) + { + } + +public: + Ntk run() + { + stopwatch t( _st.time_total ); + + retime_params rps; + std::default_random_engine::result_type seed = _ps.seed; + + _st.buffers_pre = get_stats( _ntk ); + + Ntk ntk = _ntk; + uint32_t additional_try = _ps.additional_try_iterations; + + if ( _ps.aqfp_assumptions_ps.splitter_capacity != 2 ) + rps.iterations = 1; + + buffer_insertion_params buf_ps; + buf_ps.assume = legacy_to_realistic( _ps.aqfp_assumptions_ps ); + buf_ps.scheduling = buffer_insertion_params::provided; + buf_ps.optimization_effort = buffer_insertion_params::none; + aqfp_reconstruct_params reconstruct_ps; + aqfp_reconstruct_stats reconstruct_st; + reconstruct_ps.buffer_insertion_ps = buf_ps; + reconstruct_ps.det_randomization = _ps.det_randomization; + + /* retiming first direction pass */ + rps.forward_only = !_ps.backwards_first; + rps.backward_only = _ps.backwards_first; + uint32_t i = _ps.iterations; + + if ( ( _ps.backwards_first && !_ps.forward_only ) || ( !_ps.backwards_first && !_ps.backward_only ) ) + { + while ( i-- > 0 ) + { + auto net = to_generic( ntk, seed, !_ps.backwards_first ); + auto num_registers_before = net.num_registers(); + + retime( net, rps ); + + if ( net.num_registers() >= num_registers_before ) + { + if ( additional_try-- == 0 ) + break; + } + else if ( additional_try ) + { + additional_try = _ps.additional_try_iterations; + } + + ntk = to_buffered( net ); + ++_st.rounds_total; + } + } + + /* retiming second direction pass */ + rps.forward_only = _ps.backwards_first; + rps.backward_only = !_ps.backwards_first; + i = _ps.iterations; + additional_try = _ps.additional_try_iterations; + + if ( ( !_ps.backwards_first && !_ps.forward_only ) || ( _ps.backwards_first && !_ps.backward_only ) ) + { + while ( i-- > 0 ) + { + auto net = to_generic( ntk, seed, _ps.backwards_first ); + auto num_registers_before = net.num_registers(); + + retime( net, rps ); + + if ( net.num_registers() >= num_registers_before ) + { + if ( additional_try-- == 0 ) + break; + } + else if ( additional_try ) + { + additional_try = _ps.additional_try_iterations; + } + ntk = to_buffered( net ); + ++_st.rounds_total; + } + } + + auto res = aqfp_reconstruct( ntk, reconstruct_ps, &reconstruct_st ); + _st.buffers_post = reconstruct_st.num_buffers; + return res; + } + +private: + uint32_t get_stats( Ntk& ntk ) + { + uint32_t bs_count = 0; + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_buf( n ) ) + ++bs_count; + } ); + + return bs_count; + } + + generic_network to_generic( Ntk& ntk, std::default_random_engine::result_type& seed, bool forward ) + { + node_map old2new( ntk ); + generic_network res; + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + /* suppose network is in topological order */ + if ( _ps.retime_splitters && _ps.aqfp_assumptions_ps.splitter_capacity != 2 ) + { + select_retimeable_elements_random( ntk, seed, forward ); + } + else + { + select_buffers( ntk ); + } + + create_generic_network( ntk, res, old2new ); + + return res; + } + + Ntk to_buffered( generic_network const& ntk ) + { + node_map old2new( ntk ); + Ntk res; + + old2new[ntk.get_constant( false )] = res.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = res.get_constant( true ); + } + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + topo_view topo{ ntk }; + + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + /* remove not represented nodes */ + if ( ntk.is_box_input( n ) || ntk.is_box_output( n ) || ntk.is_po( n ) ) + { + signal children; + ntk.foreach_fanin( n, [&]( auto const& f ) { + children = old2new[f]; + } ); + + old2new[n] = children; + return true; + } + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ); + } ); + + signal f; + + if ( ntk.fanin_size( n ) >= 3 ) + { + if constexpr ( has_create_maj_odd_v ) + { + if ( ntk.fanin_size( n ) > 3 ) + f = res.create_maj( children ); + else + f = res.create_maj( children[0], children[1], children[2] ); + } + else + { + f = res.create_maj( children[0], children[1], children[2] ); + } + } + else if ( ntk.fanin_size( n ) == 1 && ntk.node_function( n )._bits[0] == 0x1 ) + { + /* not */ + assert( children.size() == 1 ); + f = !children[0]; + } + else + { + /* buffer */ + /* not balanced PIs */ + if ( !_ps.aqfp_assumptions_ps.balance_pis && ( res.is_pi( res.get_node( children[0] ) ) || res.is_constant( res.get_node( children[0] ) ) ) ) + f = children[0]; + else + f = res.create_buf( children[0] ); + } + + old2new[n] = f; + return true; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + res.create_po( old2new[f] ); + } ); + + return res; + } + + void select_retimeable_elements_random( Ntk& ntk, std::default_random_engine::result_type& seed, bool forward ) + { + fanout_view fntk{ ntk }; + + ntk.clear_values(); + + /* select buffers and splitters to retime as soon as found some */ + ntk.incr_trav_id(); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + if ( ntk.is_buf( n ) ) + { + if ( ntk.fanout_size( n ) == 1 ) + { + if ( forward ) + { + fntk.foreach_fanout( n, [&]( auto const& f ) { + if ( !ntk.is_buf( f ) || ntk.fanout_size( f ) != 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + else + { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() || !ntk.is_buf( ntk.get_node( f ) ) || ntk.fanout_size( ntk.get_node( f ) ) != 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + } + else if ( ntk.visited( n ) != ntk.trav_id() || ntk.value( n ) > 0 ) + { + int free_spots; + if ( ntk.value( n ) > 0 ) + { + free_spots = rec_fetch_root( ntk, n ); /* aparently useless */ + if ( free_spots == 0 ) + return true; + } + else + { + free_spots = _ps.aqfp_assumptions_ps.splitter_capacity - ntk.fanout_size( n ); + } + + int total_fanout = 0; + std::vector fanout_splitters; + + /* select retimeable splitters */ + fntk.foreach_fanout( n, [&]( auto const f ) { + if ( ntk.is_buf( f ) && ntk.fanout_size( f ) > 1 && free_spots >= ntk.fanout_size( f ) - 1 ) + { + fanout_splitters.push_back( f ); + total_fanout += ntk.fanout_size( f ) - 1; + } + } ); + + /* check if they are all retimeable together */ + if ( free_spots >= total_fanout ) + { + for ( auto f : fanout_splitters ) + { + ntk.set_value( f, free_spots - total_fanout ); + ntk.set_visited( f, ntk.trav_id() ); + } + rec_update_root( ntk, n, free_spots - total_fanout ); + return true; + } + /* select one randomly */ + std::default_random_engine gen( seed++ ); + std::uniform_int_distribution dist( 0ul, fanout_splitters.size() - 1 ); + auto index = dist( gen ); + ntk.set_value( fanout_splitters[index], free_spots - ntk.fanout_size( fanout_splitters[index] ) + 1 ); + ntk.set_visited( fanout_splitters[index], ntk.trav_id() ); + rec_update_root( ntk, n, free_spots - ntk.fanout_size( fanout_splitters[index] ) + 1 ); + } + } + return true; + } ); + } + + void create_generic_network( Ntk& ntk, generic_network& res, node_map& old2new ) + { + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return true; + + std::vector children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + children.push_back( res.create_not( old2new[f] ) ); + else + children.push_back( old2new[f] ); + } ); + + if ( ntk.is_buf( n ) && ntk.visited( n ) == ntk.trav_id() ) + { + auto const in_register = res.create_box_input( children[0] ); + auto const node_register = res.create_register( in_register ); + auto const node_register_out = res.create_box_output( node_register ); + old2new[n] = node_register_out; + } + else + { + const auto f = res.create_node( children, ntk.node_function( n ) ); + old2new[n] = f; + } + + return true; + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + } + + void forward_compatibility( Ntk& ntk, choice_view& choice_ntk ) + { + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + /* assign classes */ + unsigned value = 0; + ntk.foreach_fanin( n, [&]( auto const f ) { + if ( ntk.value( ntk.get_node( f ) ) ) + { + if ( value ) + choice_ntk.add_choice( value, ntk.value( ntk.get_node( f ) ) ); + else + value = ntk.value( ntk.get_node( f ) ); + } + } ); + + /* propagate classes */ + if ( ntk.visited( n ) != ntk.trav_id() && !ntk.value( n ) ) + ntk.set_value( n, value ); + } ); + } + + void backward_compatibility( Ntk& ntk, choice_view& choice_ntk, fanout_view& fntk ) + { + std::vector topo_order; + topo_order.reserve( ntk.size() ); + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + return; + + topo_order.push_back( n ); + } ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + /* assign classes */ + unsigned value = 0; + fntk.foreach_fanout( *it, [&]( auto const f ) { + if ( ntk.value( f ) ) + { + if ( value ) + choice_ntk.add_choice( value, ntk.value( f ) ); + else + value = ntk.value( f ); + } + } ); + + /* propagate classes */ + if ( ntk.visited( *it ) != ntk.trav_id() && !ntk.value( *it ) ) + ntk.set_value( *it, value ); + } + } + + classes_t create_classes( choice_view& choice_ntk ) + { + classes_t classes; + choice_ntk.foreach_node( [&]( auto const n ) { + if ( choice_ntk.is_pi( n ) || choice_ntk.is_constant( n ) ) + return; + /* create unique classes */ + if ( choice_ntk.value( n ) == n ) + { + std::vector comp_class; + choice_ntk.foreach_choice( n, [&]( auto const& f ) { + comp_class.push_back( f ); + choice_ntk.set_value( f, 0 ); + return true; + } ); + classes.push_back( comp_class ); + } + } ); + + std::stable_sort( classes.begin(), classes.end(), [&]( std::vector const& a, std::vector const& b ) { return a.size() > b.size(); } ); + + return classes; + } + + uint32_t rec_fetch_root( Ntk& ntk, node const n ) + { + uint32_t value; + + if ( ntk.visited( n ) != ntk.trav_id() ) + return ntk.value( n ); + + ntk.foreach_fanin( n, [&]( auto const f ) { + auto g = ntk.get_node( f ); + if ( !ntk.is_buf( g ) || ntk.fanout_size( g ) == 1 ) + value = ntk.value( n ); + else + value = rec_fetch_root( ntk, g ); + } ); + + return value; + } + + void rec_update_root( Ntk& ntk, node const n, uint32_t const update ) + { + if ( !ntk.is_buf( n ) || ntk.fanout_size( n ) == 1 ) + return; + + ntk.set_value( n, update ); + + if ( ntk.visited( n ) != ntk.trav_id() ) + return; + + ntk.foreach_fanin( n, [&]( auto const f ) { + rec_update_root( ntk, ntk.get_node( f ), update ); + } ); + } + + void select_buffers( Ntk& ntk ) + { + ntk.incr_trav_id(); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_buf( n ) && ntk.fanout_size( n ) == 1 ) + ntk.set_visited( n, ntk.trav_id() ); + } ); + } + + Ntk create_supersplitters() + { + Ntk res; + node_map old2new( _ntk ); + + old2new[_ntk.get_constant( false )] = res.get_constant( false ); + if ( _ntk.get_node( _ntk.get_constant( true ) ) != _ntk.get_node( _ntk.get_constant( false ) ) ) + { + old2new[_ntk.get_constant( true )] = res.get_constant( true ); + } + _ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( _ntk.is_pi( n ) || _ntk.is_constant( n ) ) + return; + + std::vector children; + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ _ntk.is_complemented( f ) ); + } ); + + signal f; + if ( _ntk.is_buf( n ) ) + { + uint32_t supersplitter = res.value( res.get_node( children[0] ) ); + if ( !supersplitter ) + { + bool is_complemented = res.is_complemented( children[0] ); + f = res.create_buf( children[0] ^ is_complemented ); + res.set_value( res.get_node( children[0] ), res.node_to_index( res.get_node( f ) ) ); + f = f ^ is_complemented; + } + else + { + f = res.make_signal( res.index_to_node( supersplitter ) ) ^ res.is_complemented( children[0] ); + } + } + else + { + f = res.clone_node( _ntk, n, children ); + } + old2new[n] = f; + } ); + + _ntk.foreach_po( [&]( auto const& f ) { + if ( _ntk.is_complemented( f ) ) + res.create_po( res.create_not( old2new[f] ) ); + else + res.create_po( old2new[f] ); + } ); + + return res; + } + +private: + Ntk& _ntk; + aqfp_retiming_params const& _ps; + aqfp_retiming_stats& _st; +}; + +} /* namespace detail */ + +/*! \brief AQFP retiming. + * + * This function applies a retiming-based approach + * for splitters and buffers minimization. + * + * \param ntk Buffered network + * \param ps AQFP retiming params + */ +template +Ntk aqfp_retiming( Ntk& ntk, aqfp_retiming_params const& ps = {}, aqfp_retiming_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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "NtkDest does not implement the is_complemented method" ); + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network type" ); + static_assert( has_is_buf_v, "BufNtk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "BufNtk does not implement the create_buf method" ); + + aqfp_retiming_stats st; + + detail::aqfp_retiming_impl p( ntk, ps, st ); + auto res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp b/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp new file mode 100644 index 0000000..3251aa8 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/buffer_insertion.hpp @@ -0,0 +1,1980 @@ +/* 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 buffer_insertion.hpp + \brief Insert buffers and splitters for the AQFP technology + + \author Siang-Yun (Sonia) Lee + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "aqfp_assumptions.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for (AQFP) buffer insertion. + */ +struct buffer_insertion_params +{ + /*! \brief Technology assumptions. */ + aqfp_assumptions_realistic assume; + + /*! \brief The scheduling strategy to get the initial depth assignment. + * - `provided` = An initial level assignment is given in the constructor, thus + * no scheduling is performed. It is the user's responsibility to ensure that + * the provided assignment is legal. + * - `ASAP` = Classical As-Soon-As-Possible scheduling + * - `ASAP_depth` = As-Soon-As-Possible scheduling with depth optimality + * - `ALAP` = ASAP (to obtain depth) followed by As-Late-As-Possible scheduling + * - `ALAP_depth` = As-Late-As-Possible scheduling with depth optimality + * - `better` = ASAP followed by ALAP, then count buffers for both assignments + * and choose the better one + * - `better_depth` = ALAP_depth followed by ASAP_depth, then count buffers for both assignments + * and choose the better one + */ + enum scheduling_policy + { + provided, + ASAP, + ASAP_depth, + ALAP, + ALAP_depth, + better, + better_depth + } scheduling = ASAP; + + /*! \brief The level of optimization effort. + * - `none` = No optimization + * - `one_pass` = Try to form a chunk starting from each gate, once + * for all gates + * - `until_sat` = Iterate over all gates until no more beneficial + * chunk movement can be found + * - `optimal` = Use an SMT solver to find the global optimal + */ + enum + { + none, + one_pass, + until_sat, + optimal + } optimization_effort = none; + + /*! \brief The maximum size of a chunk. */ + uint32_t max_chunk_size{ 100u }; +}; + +/*! \brief Insert buffers and splitters for the AQFP technology. + * + * In the AQFP technology, (1) logic gates can only have one fanout. If more than one + * fanout is needed, a splitter has to be inserted in between, which also + * takes one clocking phase (counted towards the network depth). (2) All fanins of + * a logic gate have to arrive at the same time (be at the same level). If one + * fanin path is shorter, buffers have to be inserted to balance it. + * Buffers and splitters are essentially the same component in this technology. + * + * With a given level assignment to all gates in the network, the minimum number of + * buffers needed is determined. This class implements algorithms to count such + * "irredundant buffers" and to insert them to obtain a buffered network. Moreover, + * as buffer optimization is essentially a problem of obtaining a good level assignment, + * this class also implements algorithms to obtain an initial, legal assignment using + * scheduling algorithms and to further adjust and optimize it. + * + * This class provides two easy-to-use top-level functions which wrap all the above steps + * together: `run` and `dry_run`. In addition, the following interfaces are kept for + * more fine-grained usage: + * - Query the current level assignment (`level`, `depth`) + * - Count irredundant buffers based on the current level assignment (`count_buffers`, + * `num_buffers`) + * - Optimize buffer count by scheduling (`schedule`, `ASAP`, `ALAP`) and by adjusting + * the level assignment with chunked movement (`optimize`) + * - Dump the resulting network into a network type which provides representation for + * buffers (`dump_buffered_network`) + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mig_network mig = ... + + buffer_insertion_params ps; + ps.scheduling = buffer_insertion_params::ALAP; + ps.optimization_effort = buffer_insertion_params::one_pass; + + buffer_insertion buffering( mig, ps ); + buffered_mig_network buffered_mig; + auto const num_buffers = buffering.run( buffered_mig ); + + std::cout << num_buffers << std::endl; + assert( verify_aqfp_buffer( buffered_mig, ps.assume ) ); + write_verilog( buffered_mig, "buffered.v" ); + \endverbatim + * + * **Required network functions:** + * - `foreach_node` + * - `foreach_gate` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_fanin` + * - `is_pi` + * - `is_constant` + * - `get_node` + * - `fanout_size` + * - `size` + * - `set_visited` + * - `set_value` + * + */ +template +class buffer_insertion +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit buffer_insertion( Ntk const& ntk, buffer_insertion_params const& ps = {} ) + : _ntk( ntk ), _ps( ps ), _levels( _ntk ), _po_levels( _ntk.num_pos(), 0u ), _timeframes( _ntk ), _fanouts( _ntk ), _num_buffers( _ntk ) + { + static_assert( !is_buffered_network_type_v, "Ntk is already buffered" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + + assert( _ps.scheduling != buffer_insertion_params::provided ); + + // checks for assumptions + assert( _ps.assume.ci_phases.size() > 0 ); + assert( _ps.assume.ignore_co_negation ); // consideration of CO negation is too complicated and neglected for now + } + + explicit buffer_insertion( Ntk const& ntk, node_map const& levels, std::vector const& po_levels, buffer_insertion_params const& ps = {} ) + : _ntk( ntk ), _ps( ps ), _levels( levels ), _po_levels( po_levels ), _timeframes( _ntk ), _fanouts( _ntk ), _num_buffers( _ntk ) + { + static_assert( !is_buffered_network_type_v, "Ntk is already buffered" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + + assert( _ps.scheduling == buffer_insertion_params::provided ); + assert( _po_levels.size() == _ntk.num_pos() ); + } + + /*! \brief Insert buffers and obtain a buffered network. + * + * \param bufntk An empty network of an appropriate buffered network type to + * to store the buffer-insertion result + * \return The number of buffers in the resulting network + */ + template + uint32_t run( BufNtk& bufntk ) + { + dry_run(); + dump_buffered_network( bufntk ); + return num_buffers(); + } + + /*! \brief Insert buffers and obtain a buffered network. + * + * It is suggested to write the `pi_levels` information into a dumped file + * for easier recovery of the scheduled phase assignment. + * + * \param bufntk An empty network of an appropriate buffered network type to + * to store the buffer-insertion result + * \param pi_lvls A vector which will store the PI level assignment (it is + * recommended to store this information together with the buffered network) + * \return The number of buffers in the resulting network + */ + template + uint32_t run( BufNtk& bufntk, std::vector& pi_lvls ) + { + dry_run(); + dump_buffered_network( bufntk ); + pi_lvls = pi_levels(); + return num_buffers(); + } + + /*! \brief Count the number of buffers without dumping the result into a buffered network. + * + * This function saves some runtime for dumping the resulting network and + * allows users to experiment on the algorithms with new network types whose + * corresponding buffered_network are not implemented yet. + * + * `pLevels` and `pPOLevels` can be used to create another `buffer_insertion` instance of + * the same state (current schedule), which also define a unique buffered network. (Set + * `ps.scheduling = provided` and `ps.optimization_effort = none`) + * + * \return The number of buffers in the resulting network + */ + uint32_t dry_run() + { + schedule(); + optimize(); + count_buffers(); + return num_buffers(); + } + +#pragma region Query + node_map const& levels() const + { + return _levels; + } + + /*! \brief Level of node `n` considering buffer/splitter insertion. */ + uint32_t level( node const& n ) const + { + assert( n < _ntk.size() ); + return _levels[n]; + } + + std::vector const& po_levels() const + { + return _po_levels; + } + + /*! \brief Level of the `idx`-th PO (imaginary dummy PO node, not counted in depth). */ + uint32_t po_level( uint32_t idx ) const + { + assert( idx < _ntk.num_pos() ); + return _po_levels[idx]; + } + + std::vector pi_levels() const + { + std::vector lvls; + _ntk.foreach_pi( [&]( auto n ){ + lvls.emplace_back( _levels[n] ); + } ); + return lvls; + } + + /*! \brief Network depth considering AQFP buffers/splitters. + * + * Should be equal to `max( po_level(i) - 1 )`. + * + * This is the number of phases from the previous-stage register to the + * next-stage register, including the depth of the previous-stage register + * (i.e., from one register input to the next register input). + */ + uint32_t depth() const + { + return _depth; + } + + /*! \brief The total number of buffers in the network under the current + * level assignment. */ + uint32_t num_buffers() const + { + assert( !_outdated && "Please call `count_buffers()` first." ); + + uint32_t count = 0u; + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + count += num_buffers( n ); + } ); + return count; + } + + /*! \brief The number of buffers between `n` and all of its fanouts under + * the current level assignment. */ + uint32_t num_buffers( node const& n ) const + { + assert( !_outdated && "Please call `count_buffers()` first." ); + return _num_buffers[n]; + } + + /*! \brief The chosen schedule is ASAP */ + bool is_scheduled_ASAP() const + { + return _is_scheduled_ASAP; + } +#pragma endregion + +#pragma region Count buffers + /*! \brief Count the number of buffers needed at the fanout of each gate + * according to the current level assignment. + * + * This function must be called after level (re-)assignment and before + * querying `num_buffers`. + */ + void count_buffers() + { + if ( _outdated ) + { + update_fanout_info(); + } + + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + _num_buffers[n] = count_buffers( n ); + } ); + } + +private: + uint32_t count_buffers( node const& n ) const + { + assert( !_outdated && "Please call `update_fanout_info()` first." ); + auto const& fo_infos = _fanouts[n]; + + if ( _ntk.fanout_size( n ) == 0u ) /* dangling */ + { + if ( !_ntk.is_pi( n ) ) + std::cerr << "[w] node " << n << " (which is not a PI) is dangling.\n"; + return 0u; + } + + if ( _ntk.fanout_size( n ) == 1u ) /* single fanout */ + { + assert( fo_infos.size() == 1u ); + return fo_infos.front().relative_depth - 1u; + } + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( n ) ) + { + if ( fo_infos.size() == 1u ) + { + assert( fo_infos.front().relative_depth == 1u ); + return 0u; + } + } + + assert( fo_infos.size() > 1u ); + auto it = fo_infos.begin(); + uint32_t count = it->num_edges - it->fanouts.size() - it->extrefs.size(); + uint32_t rd = it->relative_depth; + for ( ++it; it != fo_infos.end(); ++it ) + { + count += it->num_edges - it->fanouts.size() - it->extrefs.size() + it->relative_depth - rd - 1; + rd = it->relative_depth; + } + + return count; + } + + /* (Upper bound on) the additional depth caused by a balanced splitter tree at the output of node `n`. */ + uint32_t num_splitter_levels( node const& n ) const + { + assert( n < _ntk.size() ); + if ( _ntk.is_pi( n ) ) + { + if ( _ntk.fanout_size( n ) > _ps.assume.ci_capacity ) + return std::ceil( std::log( _ntk.fanout_size( n ) - _ps.assume.ci_capacity + 1 ) / std::log( _ps.assume.splitter_capacity ) ); + else + return 0u; + } + return std::ceil( std::log( _ntk.fanout_size( n ) ) / std::log( _ps.assume.splitter_capacity ) ); + } + + /* Update fanout_information of all nodes */ + void update_fanout_info() + { + _fanouts.reset(); + + _ntk.foreach_gate( [&]( auto const& n ) { + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + insert_fanout( ni, n ); + } ); + } ); + + _ntk.foreach_po( [&]( auto const& f, auto i ){ + insert_extref( _ntk.get_node( f ), i ); + } ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + count_edges( n ); + } ); + + _outdated = false; + } + + /* Update the fanout_information of a node */ + template + bool update_fanout_info( node const& n ) + { + std::vector fos; + std::vector extrefs; + for ( auto it = _fanouts[n].begin(); it != _fanouts[n].end(); ++it ) + { + if ( it->fanouts.size() ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + fos.push_back( *it2 ); + } + if ( it->extrefs.size() ) + { + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + extrefs.push_back( *it2 ); + } + } + + _fanouts[n].clear(); + for ( auto& fo : fos ) + insert_fanout( n, fo ); + for ( auto& po : extrefs ) + insert_extref( n, po ); + + return count_edges( n ); + } + + void insert_fanout( node const& n, node const& fanout ) + { + assert( _levels[fanout] > _levels[n] ); + auto const rd = _levels[fanout] - _levels[n]; + auto& fo_infos = _fanouts[n]; + for ( auto it = fo_infos.begin(); it != fo_infos.end(); ++it ) + { + if ( it->relative_depth == rd ) + { + it->fanouts.push_back( fanout ); + ++it->num_edges; + return; + } + else if ( it->relative_depth > rd ) + { + fo_infos.insert( it, { rd, { fanout }, {}, 1u } ); + return; + } + } + fo_infos.push_back( { rd, { fanout }, {}, 1u } ); + } + + void insert_extref( node const& n, uint32_t idx ) + { + assert( _po_levels[idx] > _levels[n] ); + auto const rd = _po_levels[idx] - _levels[n]; + auto& fo_infos = _fanouts[n]; + for ( auto it = fo_infos.begin(); it != fo_infos.end(); ++it ) + { + if ( it->relative_depth == rd ) + { + it->extrefs.push_back( idx ); + ++it->num_edges; + return; + } + else if ( it->relative_depth > rd ) + { + fo_infos.insert( it, { rd, {}, {idx}, 1u } ); + return; + } + } + fo_infos.push_back( { rd, {}, {idx}, 1u } ); + } + + template + bool count_edges( node const& n ) + { + auto& fo_infos = _fanouts[n]; + + if ( fo_infos.size() == 0u || ( fo_infos.size() == 1u && fo_infos.front().num_edges == 1u ) ) + { + return true; + } + + if ( _ntk.is_pi( n ) && _ps.assume.ci_capacity > 1 ) + { + if ( fo_infos.front().relative_depth > 1u ) + fo_infos.push_front( { 1u, {}, {}, 0u } ); + } + else + { + assert( fo_infos.front().relative_depth > 1u ); + fo_infos.push_front( { 1u, {}, {}, 0u } ); + } + + auto it = fo_infos.end(); + --it; + uint32_t splitters; + while ( it != fo_infos.begin() ) + { + splitters = num_splitters( it->num_edges ); + auto rd = it->relative_depth; + --it; + if ( it->relative_depth < rd - 1 && splitters > 1 ) + { + it = fo_infos.insert( ++it, { rd - 1, {}, {}, splitters } ); + } + else + { + it->num_edges += splitters; + } + } + + assert( fo_infos.front().relative_depth == 1u ); + if constexpr ( verify ) + { + return _ntk.is_pi( n ) ? fo_infos.front().num_edges <= _ps.assume.ci_capacity : fo_infos.front().num_edges == 1u; + } + else + { + assert( _ntk.is_pi( n ) ? fo_infos.front().num_edges <= _ps.assume.ci_capacity : fo_infos.front().num_edges == 1u ); + return true; + } + } + + /* Return the number of splitters needed in one level lower */ + uint32_t num_splitters( uint32_t const& num_fanouts ) const + { + return std::ceil( float( num_fanouts ) / float( _ps.assume.splitter_capacity ) ); + } +#pragma endregion + +#pragma region Initial level assignment +public: + void set_scheduling_policy( buffer_insertion_params::scheduling_policy p ) + { + _ps.scheduling = p; + } + + /*! \brief Obtain the initial level assignment using the specified scheduling policy */ + void schedule() + { + if ( _ps.scheduling == buffer_insertion_params::provided ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + assert( _po_levels[i] > _levels[f] ); + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + return; + } + + if ( _ps.scheduling == buffer_insertion_params::better_depth || _ps.scheduling == buffer_insertion_params::ASAP_depth || _ps.scheduling == buffer_insertion_params::ALAP_depth ) + { + fanout_view f_ntk{ _ntk }; + /* Optimum-depth ALAP scheduling */ + ALAP_depth( f_ntk ); + count_buffers(); + auto const num_buf_ALAP_depth = num_buffers(); + + if ( _ps.scheduling == buffer_insertion_params::ALAP_depth ) + return; + + /* Optimum-depth ALAP scheduling: no balanced trees */ + ASAP_depth( f_ntk, false ); + count_buffers(); + auto const num_buf_ASAP_depth = num_buffers(); + + if ( _ps.scheduling == buffer_insertion_params::ASAP_depth ) + return; + + /* Revert to optimum-depth ALAP scheduling if better */ + if ( num_buf_ALAP_depth < num_buf_ASAP_depth ) + { + ALAP_depth( f_ntk ); + } + return; + } + + ASAP(); + if ( _ps.scheduling == buffer_insertion_params::ALAP ) + { + ALAP(); + } + else if ( _ps.scheduling == buffer_insertion_params::better ) + { + count_buffers(); + auto num_buf_ASAP = num_buffers(); + ALAP(); + count_buffers(); + if ( num_buffers() > num_buf_ASAP ) + { + ASAP(); + } + } + } + + /*! \brief ASAP scheduling */ + void ASAP() + { + _depth = 0; + _levels.reset( 0 ); + _ntk.incr_trav_id(); + + _ntk.foreach_po( [&]( auto const& f, auto i ) { + auto const no = _ntk.get_node( f ); + _po_levels[i] = compute_levels_ASAP( no ) + num_splitter_levels( no ) + 1; + if ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases != 0 ) // phase alignment + { + _po_levels[i] += _ps.assume.num_phases - ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases ); + } + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = _depth + 1; + } ); + } + + /* dangling PIs */ + _ntk.foreach_pi( [&]( auto const& n ){ + if ( _ntk.visited( n ) != _ntk.trav_id() ) + _levels[n] = _ps.assume.ci_phases[0]; + } ); + + _outdated = true; + _is_scheduled_ASAP = true; + } + + /*! \brief ASAP optimal-depth scheduling + * + * ASAP_depth should follow right after ALAP_depth (i.e., initialization). + * + * \param try_regular tries to insert balanced trees when sufficient slack. + */ + void ASAP_depth( fanout_view const& f_ntk, bool try_regular ) + { + node_map mobility( _ntk, std::numeric_limits::max() ); + + if ( !_ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = 0; + } ); + } + + _ntk.foreach_pi( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) ) + { + mobility[n] = _levels[n] - _ps.assume.ci_phases[0]; + compute_mobility_ASAP( f_ntk, n, mobility, try_regular ); + } + } ); + + _ntk.foreach_gate( [&]( auto const& n ) { + compute_mobility_ASAP( f_ntk, n, mobility, try_regular ); + } ); + + if ( !_ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( _po_levels[i] == 0 ) + { + assert( _ntk.is_constant( _ntk.get_node( f ) ) ); + _po_levels[i] = 1; + } + else if ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases != 0 ) // phase alignment + { + _po_levels[i] += _ps.assume.num_phases - ( ( _po_levels[i] - 1 ) % _ps.assume.num_phases ); + } + _depth = std::max( _depth, _po_levels[i] - 1 ); + } ); + assert( _depth % _ps.assume.num_phases == 0 ); + } + + _outdated = true; + _is_scheduled_ASAP = true; + } + + /*! \brief ALAP scheduling. + * + * ALAP should follow right after ASAP (i.e., initialization) without other optimization in between. + */ + void ALAP() + { + assert( _depth % _ps.assume.num_phases == 0 ); + _levels.reset( 0 ); + _ntk.incr_trav_id(); + + _ntk.foreach_po( [&]( auto const& f, auto i ) { + _po_levels[i] = _depth + 1; + const auto n = _ntk.get_node( f ); + + if ( !_ntk.is_constant( n ) && _ntk.visited( n ) != _ntk.trav_id() ) + { + _levels[n] = _depth - num_splitter_levels( n ); + compute_levels_ALAP( n ); + } + } ); + + /* dangling PIs */ + _ntk.foreach_pi( [&]( auto const& n ){ + if ( _ntk.visited( n ) != _ntk.trav_id() ) + _levels[n] = _ps.assume.ci_phases[0]; + } ); + + _outdated = true; + _is_scheduled_ASAP = false; + } + + /*! \brief ALAP depth-optimal sheduling */ + void ALAP_depth( fanout_view const& f_ntk ) + { + _levels.reset( 0 ); + topo_view topo_ntk{ _ntk }; + + /* compute ALAP */ + _depth = std::numeric_limits::max() - 1; + uint32_t min_level = std::numeric_limits::max() - 1; + topo_ntk.foreach_node_reverse( [&]( auto const& n ) { + if ( !_ntk.is_constant( n ) && _ntk.fanout_size( n ) > 0 ) + { + compute_levels_ALAP_depth( f_ntk, n ); + min_level = std::min( min_level, _levels[n] ); + } + } ); + + /* move everything down by `delta` */ + uint32_t delta = min_level; + /* phase alignment for PO: depth % num_phases = 0 */ + if ( ( _depth - delta ) % _ps.assume.num_phases != 0 ) + { + delta -= _ps.assume.num_phases - ( ( _depth - delta ) % _ps.assume.num_phases ); + } + + /* level of the lowest PI >= ci_phases[0] */ + while ( min_level - delta < _ps.assume.ci_phases[0] ) + { + delta -= _ps.assume.num_phases; + } + /* move PIs down to an acceptable level */ + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_pi( [&]( auto const& n ) { + if ( _ntk.fanout_size( n ) == 0 ) + { + _levels[n] = _ps.assume.ci_phases[0]; + } + else if ( !_ntk.is_constant( n ) ) + { + _levels[n] = _levels[n] - delta; + for ( auto rit = _ps.assume.ci_phases.rbegin(); rit != _ps.assume.ci_phases.rend(); ++rit ) + { + if ( *rit <= _levels[n] ) + { + _levels[n] = *rit; + return; + } + } + assert( false ); + } + } ); + } + else + { + _ntk.foreach_pi( [&]( auto const& n ) { + if ( _ntk.fanout_size( n ) == 0 ) + { + _levels[n] = _ps.assume.ci_phases[0]; + } + else if ( !_ntk.is_constant( n ) ) + { + _levels[n] = _levels[n] - delta; + while ( !is_acceptable_ci_lvl( _levels[n] ) ) + { + assert( _levels[n] > 0 ); + --_levels[n]; + } + } + } ); + } + + _ntk.foreach_gate( [&]( auto const& n ) { + _levels[n] = _levels[n] - delta; + } ); + _depth -= delta; + assert( _depth % _ps.assume.num_phases == 0 ); + if ( _ps.assume.balance_cios ) + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + (void)f; + _po_levels[i] = _depth + 1; + } ); + } + else + { + _ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + _po_levels[i] = 1; + else + { + _po_levels[i] = _levels[f] + num_splitter_levels( _ntk.get_node( f ) ); + if ( _po_levels[i] % _ps.assume.num_phases > 0 ) + _po_levels[i] += _ps.assume.num_phases - ( _po_levels[i] % _ps.assume.num_phases ); + ++_po_levels[i]; + } + } ); + } + + _outdated = true; + _is_scheduled_ASAP = false; + } + +private: + uint32_t compute_levels_ASAP( node const& n ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + { + return _levels[n]; + } + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_constant( n ) ) + { + return _levels[n] = 0; + } + else if ( _ntk.is_pi( n ) ) + { + return _levels[n] = _ps.assume.ci_phases[0]; + } + + uint32_t level{ 0 }; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + auto fi_level = compute_levels_ASAP( ni ) + num_splitter_levels( ni ); + level = std::max( level, fi_level ); + } + } ); + + return _levels[n] = level + 1; + } + + bool is_acceptable_ci_lvl( uint32_t lvl ) const + { + if ( _ps.assume.balance_cios ) + { + for ( auto const& p : _ps.assume.ci_phases ) + { + if ( lvl == p ) + return true; + } + return false; + } + else + { + for ( auto const& p : _ps.assume.ci_phases ) + { + // for example, if num_phases = 4, ci_phases = {5}, + // then lvl = 1 will not be acceptable, but lvl = 5 or lvl = 9 will + if ( lvl % _ps.assume.num_phases == p % _ps.assume.num_phases && lvl >= p ) + return true; + } + return false; + } + } + + void compute_levels_ALAP( node const& n ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_pi( n ) ) + { + if ( _ps.assume.balance_cios ) + { + for ( auto rit = _ps.assume.ci_phases.rbegin(); rit != _ps.assume.ci_phases.rend(); ++rit ) + { + if ( *rit <= _levels[n] ) + { + _levels[n] = *rit; + return; + } + } + assert( false ); + } + else + { + while ( !is_acceptable_ci_lvl( _levels[n] ) ) + { + assert( _levels[n] > 0 ); + --_levels[n]; + } + } + return; + } + + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + assert( _levels[n] > num_splitter_levels( ni ) ); + auto fi_level = _levels[n] - num_splitter_levels( ni ) - 1; + if ( _ntk.visited( ni ) != _ntk.trav_id() || _levels[ni] > fi_level ) + { + _levels[ni] = fi_level; + compute_levels_ALAP( ni ); + } + } + } ); + } + + void compute_levels_ALAP_depth( fanout_view const& ntk, node const& n ) + { + std::vector level_assignment; + level_assignment.reserve( ntk.fanout_size( n ) ); + + /* if node is a PO, add levels */ + for ( auto i = ntk.fanout( n ).size(); i < ntk.fanout_size( n ); ++i ) + level_assignment.push_back( _depth + 1 ); + + /* get fanout levels */ + ntk.foreach_fanout( n, [&]( auto const& f ) { + level_assignment.push_back( _levels[f] ); + } ); + + /* sort by descending order of levels */ + std::stable_sort( level_assignment.begin(), level_assignment.end(), std::greater() ); + + /* simulate splitter tree reconstruction */ + uint32_t nodes_in_level = 0; + uint32_t last_level = level_assignment.front(); + for ( int const l : level_assignment ) + { + if ( l == last_level ) + { + ++nodes_in_level; + } + else + { + /* update splitters */ + for ( auto i = 0; ( i < last_level - l ) && ( nodes_in_level != 1 ); ++i ) + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + + ++nodes_in_level; + last_level = l; + } + } + + /* search for a feasible level for node n */ + --last_level; + if ( _ntk.is_pi( n ) ) + { + while ( nodes_in_level > _ps.assume.ci_capacity ) + { + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + --last_level; + } + } + else + { + while ( nodes_in_level > 1 ) + { + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + --last_level; + } + } + + _levels[n] = last_level; + } + + void compute_mobility_ASAP( fanout_view const& ntk, node const& n, node_map& mobility, bool try_regular ) + { + assert( mobility[n] <= _levels[n] ); + /* commit ASAP scheduling */ + uint32_t level_n = _levels[n] - mobility[n]; + _levels[n] = level_n; + + /* try to fit a balanced tree */ + if ( try_regular ) + { + uint32_t fo_level = num_splitter_levels( n ); + bool valid = true; + ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( level_n + fo_level + 1 > _levels[f] ) + valid = false; + return valid; + } ); + + if ( valid ) + { + ntk.foreach_fanout( n, [&]( auto const& f ) { + mobility[f] = std::min( mobility[f], _levels[f] - level_n - fo_level - 1 ); + } ); + return; + } + } + + /* keep current splitter structure, selecting the mobility based on the buffers */ + std::vector> level_assignment; + level_assignment.reserve( _ntk.fanout_size( n ) ); + + /* if node is a PO, add levels */ + if ( ntk.fanout( n ).size() < ntk.fanout_size( n ) ) + { + ntk.foreach_po( [&]( auto const& f, auto i ){ + if ( ntk.get_node( f ) == n ) + level_assignment.push_back( { i, _depth + 1, 0 } ); + } ); + assert( level_assignment.size() == ntk.fanout_size( n ) - ntk.fanout( n ).size() ); + } + + /* get fanout levels */ + ntk.foreach_fanout( n, [&]( auto const& f ) { + level_assignment.push_back( { ntk.node_to_index( f ), _levels[f], 0 } ); + } ); + + /* dangling PI */ + if ( level_assignment.empty() ) + { + return; + } + + /* sort by descending order of levels */ + std::stable_sort( level_assignment.begin(), level_assignment.end(), []( auto const& a, auto const& b ) { + return a[1] > b[1]; + } ); + + /* simulate splitter tree reconstruction */ + uint32_t nodes_in_level = 0; + uint32_t nodes_in_last_level = _ps.assume.splitter_capacity; + uint32_t last_level = level_assignment.front()[1]; + for ( auto i = 0; i < level_assignment.size(); ++i ) + { + uint32_t l = level_assignment[i][1]; + if ( l == last_level ) + { + ++nodes_in_level; + } + else + { + /* update splitters */ + uint32_t mobility_update = 0; + for ( auto j = 0; j < last_level - l; ++j ) + { + if ( nodes_in_level == 1 ) + { + ++mobility_update; + } + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + } + + if ( mobility_update ) + { + for ( auto j = 0; j < i; ++j ) + level_assignment[j][2] += mobility_update; + } + + ++nodes_in_level; + last_level = l; + } + } + + /* search a feasible level for node n */ + uint32_t mobility_update = 0; + for ( auto i = level_n + 1; i < last_level; ++i ) + { + if ( nodes_in_level == 1 || ( _ntk.is_pi( n ) && nodes_in_level <= _ps.assume.ci_capacity ) ) + ++mobility_update; + nodes_in_level = std::ceil( float( nodes_in_level ) / float( _ps.assume.splitter_capacity ) ); + } + + /* update mobilities */ + for ( auto const& v : level_assignment ) + { + if ( v[1] != _depth + 1 ) + { + mobility[v[0]] = std::min( mobility[v[0]], v[2] + mobility_update ); + } + } + + /* update po_level, if possible */ + if ( !_ps.assume.balance_cios ) + { + for ( auto const& v : level_assignment ) + { + if ( v[1] == _depth + 1 ) + { + _po_levels[v[0]] = std::max( _po_levels[v[0]], _depth + 1 - v[2] - mobility_update ); + } + else + { + break; + } + } + } + } +#pragma endregion + +#pragma region Dump buffered network +public: + /*! \brief Dump buffered network + * + * After level assignment, (optimization), and buffer counting, this method + * can be called to dump the resulting buffered network. + */ + template + void dump_buffered_network( BufNtk& bufntk ) const + { + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network type" ); + static_assert( has_is_buf_v, "BufNtk does not implement the is_buf method" ); + static_assert( has_create_buf_v, "BufNtk does not implement the create_buf method" ); + assert( !_outdated && "Please call `count_buffers()` first." ); + + using buf_signal = typename BufNtk::signal; + using fanout_tree = std::vector>; + + node_map node_to_signal( _ntk ); + node_map buffers( _ntk ); + + /* constants */ + node_to_signal[_ntk.get_constant( false )] = bufntk.get_constant( false ); + buffers[_ntk.get_constant( false )].emplace_back( 1, bufntk.get_constant( false ) ); + if ( _ntk.get_node( _ntk.get_constant( false ) ) != _ntk.get_node( _ntk.get_constant( true ) ) ) + { + node_to_signal[_ntk.get_constant( true )] = bufntk.get_constant( true ); + buffers[_ntk.get_constant( true )].emplace_back( 1, bufntk.get_constant( true ) ); + } + + /* PIs */ + _ntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = bufntk.create_pi(); + create_buffer_chain( bufntk, buffers, n, node_to_signal[n] ); + } ); + + /* gates: assume topological order */ + _ntk.foreach_gate( [&]( auto const& n ) { + std::vector children; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + buf_signal s; + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + s = node_to_signal[fi]; + else + s = get_buffer_at_relative_depth( bufntk, buffers[fi], _levels[n] - _levels[fi] - 1 ); + children.push_back( _ntk.is_complemented( fi ) ? !s : s ); + } ); + node_to_signal[n] = bufntk.clone_node( _ntk, n, children ); + create_buffer_chain( bufntk, buffers, n, node_to_signal[n] ); + } ); + + /* POs */ + _ntk.foreach_po( [&]( auto const& f, auto i ) { + buf_signal s; + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + s = node_to_signal[f]; + else + s = get_buffer_at_relative_depth( bufntk, buffers[f], _po_levels[i] - _levels[f] - 1 ); + assert( _ps.assume.ignore_co_negation ); + bufntk.create_po( _ntk.is_complemented( f ) ? !s : s ); + } ); + + assert( bufntk.size() - bufntk.num_pis() - bufntk.num_gates() - 1 == num_buffers() ); + } + +private: + template + void create_buffer_chain( BufNtk& bufntk, Buffers& buffers, node const& n, typename BufNtk::signal const& s ) const + { + if ( _ntk.fanout_size( n ) == 0 ) + return; /* dangling */ + + assert( _fanouts[n].size() > 0u ); + buffers[n].resize( _fanouts[n].back().relative_depth ); + auto& fot = buffers[n]; + fot[0].push_back( s ); + for ( auto i = 1u; i < fot.size(); ++i ) + { + fot[i].push_back( bufntk.create_buf( fot[i-1].back() ) ); + } + } + + template + typename BufNtk::signal get_buffer_at_relative_depth( BufNtk& bufntk, FOT& fot, uint32_t rd ) const + { + typename BufNtk::signal b = fot[rd].back(); + if ( rd == 0 && bufntk.is_pi( bufntk.get_node( b ) ) ) + { + assert( bufntk.fanout_size( bufntk.get_node( b ) ) < _ps.assume.ci_capacity ); + return b; + } + if ( bufntk.fanout_size( bufntk.get_node( b ) ) == _ps.assume.splitter_capacity ) + { + assert( rd > 0 ); + typename BufNtk::signal b_lower = get_buffer_at_relative_depth( bufntk, fot, rd - 1 ); + b = bufntk.create_buf( b_lower ); + fot[rd].push_back( b ); + } + return b; + } +#pragma endregion + +#pragma region Post-dump optimization +public: +template +uint32_t remove_buffer_chains( BufNtk& ntk ) const +{ + static_assert( is_buffered_network_type_v, "BufNtk is not a buffered network" ); + + uint32_t max_chain = 0; + ntk.incr_trav_id(); + ntk.foreach_po( [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), 0, max_chain ); + } ); + return max_chain; +} + +private: +template +std::pair remove_buffer_chains_rec( BufNtk& ntk, typename BufNtk::node n, typename BufNtk::node parent, uint32_t& max_chain ) const +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return std::make_pair( 0, n ); + ntk.set_visited( n, ntk.trav_id() ); + if ( ntk.is_pi( n ) ) + return std::make_pair( 0, n ); + + if ( ntk.is_buf( n ) ) + { + // splitter + if ( ntk.fanout_size( n ) > 1 ) + { + ntk.foreach_fanin( n, [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + } ); + return std::make_pair( 0, n ); + } + + // single-output buffer: can be part of a chain to be removed + std::pair ret; + ntk.foreach_fanin( n, [&]( auto f ){ + auto [count, origin] = remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + if ( count % _ps.assume.num_phases == _ps.assume.num_phases - 1 ) + { + // TODO: take care of complementation + if ( parent != 0 ) + { + ntk.replace_in_node( parent, n, ntk.make_signal( origin ) ); + ntk.take_out_node( n ); + } + else + { + ntk.replace_in_outputs( n, ntk.make_signal( origin ) ); + ntk.take_out_node( n ); + } + max_chain = std::max( count + 1, max_chain ); + } + ret = std::make_pair( count + 1, origin ); + } ); + return ret; + } + + // gate + ntk.foreach_fanin( n, [&]( auto f ){ + remove_buffer_chains_rec( ntk, ntk.get_node( f ), n, max_chain ); + } ); + return std::make_pair( 0, n ); +} +#pragma endregion + +public: + /*! \brief Optimize with chunked movement using the specified optimization policy. */ + void optimize() + { + if ( _ps.optimization_effort == buffer_insertion_params::none ) + { + return; + } + //else if ( _ps.optimization_effort == buffer_insertion_params::optimal ) + //{ + // if constexpr ( has_get_network_name_v ) + // optimize_with_smt( _ntk.get_network_name() ); + // else + // optimize_with_smt( "" ); + // return; + //} + + if ( _outdated ) + { + update_fanout_info(); + } + + bool updated; + do + { + updated = find_and_move_chunks(); + } while ( updated && _ps.optimization_effort == buffer_insertion_params::until_sat ); + single_gate_movement(); + } + +#pragma region Chunked movement +private: + struct io_interface + { + node c; // chunk node + node o; // outside node + }; + + struct po_interface + { + node c; // chunk node + uint32_t o; // PO index + }; + + struct chunk + { + uint32_t id; + std::vector members{}; + std::vector input_interfaces{}; + std::vector output_interfaces{}; + std::vector po_interfaces{}; + int32_t slack{ std::numeric_limits::max() }; + int32_t benefits{ 0 }; + }; + + bool is_ignored( node const& n ) const + { + return _ntk.is_constant( n ); + } + + bool is_fixed( node const& n ) const + { + return _ps.assume.balance_cios && _ps.assume.ci_phases.size() == 1 && _ntk.is_pi( n ); + } + + bool find_and_move_chunks() + { + bool updated = false; + count_buffers(); + uint32_t num_buffers_before = num_buffers(); + _start_id = _ntk.trav_id(); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( is_ignored( n ) || is_fixed( n ) || _ntk.visited( n ) > _start_id /* belongs to a chunk */ ) + { + return true; + } + + _ntk.incr_trav_id(); + chunk c{ _ntk.trav_id() }; + recruit( n, c ); + if ( c.members.size() > _ps.max_chunk_size ) + { + return true; /* skip */ + } + cleanup_interfaces( c ); + + auto moved = analyze_chunk_down( c ); + if ( !moved ) + moved = analyze_chunk_up( c ); + updated |= moved; + return true; + } ); + + count_buffers(); + assert( num_buffers() <= num_buffers_before ); + return updated && num_buffers() < num_buffers_before; + } + + void single_gate_movement() + { + _ntk.foreach_node( [&]( auto const& n ) { + if ( is_ignored( n ) || is_fixed( n ) ) + return; + + _ntk.incr_trav_id(); + chunk c{ _ntk.trav_id() }; + c.members.emplace_back( n ); + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !is_ignored( ni ) ) + c.input_interfaces.push_back( { n, ni } ); + } ); + auto const& fanout_info = _fanouts[n]; + for ( auto it = fanout_info.begin(); it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + c.output_interfaces.push_back( { n, *it2 } ); + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + c.po_interfaces.push_back( { n, *it2 } ); + } + + if ( !analyze_chunk_down( c ) ) + analyze_chunk_up( c ); + } ); + } + + void recruit( node const& n, chunk& c ) + { + if ( _ntk.visited( n ) == c.id ) + return; + + assert( _ntk.visited( n ) <= _start_id ); + assert( !is_fixed( n ) ); + assert( !is_ignored( n ) ); + + _ntk.set_visited( n, c.id ); + c.members.emplace_back( n ); + recruit_fanins( n, c ); + recruit_fanouts( n, c ); + } + + void recruit_fanins( node const& n, chunk& c ) + { + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !is_ignored( ni ) && _ntk.visited( ni ) != c.id ) + { + if ( is_fixed( ni ) ) + c.input_interfaces.push_back( { n, ni } ); + else if ( are_close( ni, n ) ) + recruit( ni, c ); + else + c.input_interfaces.push_back( { n, ni } ); + } + } ); + } + + void recruit_fanouts( node const& n, chunk& c ) + { + auto const& fanout_info = _fanouts[n]; + if ( fanout_info.size() == 0 ) /* dangling */ + return; + + auto it = fanout_info.begin(); + if ( _ntk.fanout_size( n ) == 1 ) /* single fanout */ + { + assert( fanout_info.size() == 1 ); + if ( it->fanouts.size() == 1 ) /* single gate fanout */ + { + if ( it->relative_depth == 1 ) + recruit( it->fanouts.front(), c ); + else + c.output_interfaces.push_back( { n, it->fanouts.front() } ); + } + else /* single PO fanout */ + { + assert( it->extrefs.size() == 1 ); + c.po_interfaces.push_back( { n, it->extrefs.front() } ); + } + return; + } + + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->extrefs.begin(); it2 != it->extrefs.end(); ++it2 ) + c.po_interfaces.push_back( { n, *it2 } ); + } + it = fanout_info.begin(); + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( n ) ) + { + if ( it->relative_depth == 1 ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + recruit( *it2, c ); + it++; + } + if ( it->relative_depth == 2 && fanout_info.front().num_edges == _ps.assume.ci_capacity ) + { + assert( fanout_info.front().relative_depth == 1 ); + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + recruit( *it2, c ); + it++; + } + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( _ntk.visited( *it2 ) != c.id ) + c.output_interfaces.push_back( { n, *it2 } ); + } + } + return; + } + + for ( ; it != fanout_info.end(); ++it ) + { + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( it->relative_depth == 2 ) + recruit( *it2, c ); + else if ( _ntk.visited( *it2 ) != c.id ) + c.output_interfaces.push_back( { n, *it2 } ); + } + } + } + + bool are_close( node const& ni, node const& n ) + { + auto const& fanout_info = _fanouts[ni]; + + if ( _ps.assume.ci_capacity > 1 && _ntk.is_pi( ni ) ) + { + auto const& front_fanouts = fanout_info.front().fanouts; + if ( fanout_info.front().relative_depth == 1 ) + { + if ( std::find( front_fanouts.begin(), front_fanouts.end(), n ) != front_fanouts.end() ) + return true; + if ( fanout_info.front().num_edges < _ps.assume.ci_capacity ) + return false; + } + else if ( _ntk.fanout_size( ni ) <= _ps.assume.ci_capacity ) + return false; + assert( fanout_info.size() > 1 ); + } + + if ( fanout_info.size() == 1 && fanout_info.front().relative_depth == 1 ) + { + assert( fanout_info.front().fanouts.front() == n ); + return true; + } + if ( fanout_info.size() > 1 ) + { + auto it = fanout_info.begin(); + it++; + if ( it->relative_depth > 2 ) + return false; + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( *it2 == n ) + return true; + } + } + return false; + } + + void cleanup_interfaces( chunk& c ) + { + for ( int i = 0; i < c.input_interfaces.size(); ++i ) + { + if ( _ntk.visited( c.input_interfaces[i].o ) == c.id ) + { + c.input_interfaces.erase( c.input_interfaces.begin() + i ); + --i; + } + } + for ( int i = 0; i < c.output_interfaces.size(); ++i ) + { + if ( _ntk.visited( c.output_interfaces[i].o ) == c.id ) + { + c.output_interfaces.erase( c.output_interfaces.begin() + i ); + --i; + } + } + } + + bool analyze_chunk_down( chunk c ) + { + count_buffers(); + auto buffers_before = num_buffers(); + + std::set marked_oi; + for ( auto oi : c.output_interfaces ) + { + if ( marked_oi.find( oi.c ) == marked_oi.end() ) + { + marked_oi.insert( oi.c ); + --c.benefits; + } + } + + for ( auto ii : c.input_interfaces ) + { + auto const rd = _levels[ii.c] - _levels[ii.o]; + auto const lowest = lowest_spot( ii.o ); + if ( rd <= lowest ) + { + c.slack = 0; + break; + } + c.slack = std::min( c.slack, int32_t( rd - lowest ) ); + pseudo_move( ii.o, ii.c, rd, lowest ); + if ( _fanouts[ii.o].back().relative_depth == rd && _fanouts[ii.o].back().num_edges == 0 ) // `ii.c` is the last highest fanout of `ii.o` + { + ++c.benefits; + } + } + + if ( c.po_interfaces.size() > 0 ) + { + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + c.slack -= c.slack % _ps.assume.num_phases; + } + else + { + for ( auto poi : c.po_interfaces ) + { + if ( marked_oi.find( poi.c ) == marked_oi.end() ) + --c.benefits; + } + } + } + + std::vector pi_members; + for ( auto m : c.members ) + { + if ( _ntk.is_pi( m ) ) + { + pi_members.emplace_back( m ); + c.slack = std::min( c.slack, int32_t( _levels[m] ) ); + } + } + if ( pi_members.size() > 0 ) + { + while ( c.slack > 0 ) + { + bool ok = true; + for ( auto m : pi_members ) + { + if ( _levels[m] < c.slack || !is_acceptable_ci_lvl( _levels[m] - c.slack ) ) + { + ok = false; + break; + } + } + if ( !ok ) + --c.slack; + else + break; + } + } + + if ( c.benefits > 0 && c.slack > 0 ) + { + bool legal = true; + + for ( auto m : c.members ) + _levels[m] -= c.slack; + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + for ( auto poi : c.po_interfaces ) + _po_levels[poi.o] -= c.slack; + } + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + legal &= update_fanout_info( ii.o ); + + _outdated = true; + if ( legal ) + count_buffers(); + if ( !legal || num_buffers() >= buffers_before ) + { + /* UNDO */ + for ( auto m : c.members ) + _levels[m] += c.slack; + if ( !_ps.assume.balance_cios && c.slack >= _ps.assume.num_phases ) + { + for ( auto poi : c.po_interfaces ) + _po_levels[poi.o] += c.slack; + } + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + + _start_id = _ntk.trav_id(); + return true; + } + else + { + /* reset fanout_infos of input_interfaces because num_edges may be modified by pseudo_move */ + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + } + + /* relative_depth of the lowest available spot in the fanout tree of n */ + uint32_t lowest_spot( node const& n ) const + { + auto const& fanout_info = _fanouts[n]; + assert( fanout_info.size() ); + + auto it = fanout_info.begin(); + uint32_t rd_prev = 1; + uint32_t num_splitters_prev = 1; + if ( _ntk.is_pi( n ) && _ps.assume.ci_capacity > 1 ) + { + if ( it->num_edges <= _ps.assume.ci_capacity ) + return 1; + else + num_splitters_prev = _ps.assume.ci_capacity - it->fanouts.size() - it->extrefs.size(); + } + else if ( fanout_info.size() == 1 ) // single fanout + { + return 1; + } + + ++it; // skip the first splitter at rd=1 + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth > rd_prev + 1 ) // level skip => must not full + { + return rd_prev + 1; + } + else if ( it->num_edges == _ps.assume.splitter_capacity * num_splitters_prev ) // full layer + { + num_splitters_prev = it->num_edges - it->fanouts.size() - it->extrefs.size(); + rd_prev = it->relative_depth; + } + else + { + return it->relative_depth; + } + } + // all full + return fanout_info.back().relative_depth + 1; + } + + /* move `no`, which is a fanout of `n`, from `from_rd` to `to_rd` */ + void pseudo_move( node const& n, node const& no, uint32_t from_rd, uint32_t to_rd ) + { + assert( from_rd > to_rd ); + auto& fanout_info = _fanouts[n]; + auto it = fanout_info.begin(); + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth == to_rd ) + { + ++it->num_edges; + it->fanouts.push_back( no ); + break; + } + else if ( it->relative_depth > to_rd ) + { + fanout_info.insert( it, {to_rd, {no}, {}, 2} ); + break; + } + } + for ( ; it != fanout_info.end(); ++it ) + { + if ( it->relative_depth == from_rd ) + { + --it->num_edges; + for ( auto it2 = it->fanouts.begin(); it2 != it->fanouts.end(); ++it2 ) + { + if ( *it2 == no ) + { + it->fanouts.erase( it2 ); + return; + } + } + assert( false ); + } + } + assert( false ); + } + + bool analyze_chunk_up( chunk c ) + { + for ( auto ii : c.input_interfaces ) + { + if ( _fanouts[ii.o].back().relative_depth == _levels[ii.c] - _levels[ii.o] ) // is highest fanout + --c.benefits; + } + + std::set marked_oi; + for ( auto oi : c.output_interfaces ) + { + if ( marked_oi.find( oi.c ) == marked_oi.end() ) + { + marked_oi.insert( oi.c ); + ++c.benefits; + } + auto const& fanout_info = _fanouts[oi.c]; + if ( fanout_info.size() == 1 ) /* single fanout */ + c.slack = std::min( c.slack, int32_t( fanout_info.front().relative_depth - 1 ) ); + else + c.slack = std::min( c.slack, int32_t( _levels[oi.o] - _levels[oi.c] - 2 ) ); + } + + std::vector po_to_move; + if ( c.po_interfaces.size() > 0 ) + { + for ( auto poi : c.po_interfaces ) + { + if ( _levels[poi.c] + num_splitter_levels( poi.c ) + c.slack >= _po_levels[poi.o] ) + { + if ( _ps.assume.balance_cios ) + c.slack = std::min( c.slack, int32_t( _po_levels[poi.o] - _levels[poi.c] - num_splitter_levels( poi.c ) - 1 ) ); + else + { + c.slack = std::min( c.slack, int32_t( _depth + 1 - _po_levels[poi.o] ) ); + po_to_move.emplace_back( poi.o ); + } + } + else + { + if ( marked_oi.find( poi.c ) == marked_oi.end() ) + ++c.benefits; + } + } + } + + if ( c.benefits <= 0 || c.slack <= 0 ) + return false; + + std::vector pi_members; + for ( auto m : c.members ) + { + if ( _ntk.is_pi( m ) ) + pi_members.emplace_back( m ); + } + if ( pi_members.size() > 0 ) + { + while ( c.slack > 0 ) + { + bool ok = true; + for ( auto m : pi_members ) + { + if ( !is_acceptable_ci_lvl( _levels[m] + c.slack ) ) + { + ok = false; + break; + } + } + if ( !ok ) + --c.slack; + else + break; + } + } + if ( po_to_move.size() > 0 ) + c.slack -= c.slack % _ps.assume.num_phases; + + if ( c.benefits > 0 && c.slack > 0 ) + { + count_buffers(); + bool legal = true; + auto buffers_before = num_buffers(); + + for ( auto m : c.members ) + _levels[m] += c.slack; + for ( auto po : po_to_move ) + _po_levels[po] += c.slack; + for ( auto m : c.members ) + legal &= update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + legal &= update_fanout_info( ii.o ); + + _outdated = true; + if ( legal ) + count_buffers(); + if ( !legal || num_buffers() >= buffers_before ) + { + /* UNDO */ + for ( auto m : c.members ) + _levels[m] -= c.slack; + for ( auto po : po_to_move ) + _po_levels[po] -= c.slack; + for ( auto m : c.members ) + update_fanout_info( m ); + for ( auto ii : c.input_interfaces ) + update_fanout_info( ii.o ); + _outdated = true; + return false; + } + + _start_id = _ntk.trav_id(); + return true; + } + else + { + return false; + } + } +#pragma endregion + +#pragma region Global optimal by SMT +private: +//#include "optimal_buffer_insertion.hpp" +#pragma endregion + +private: + struct fanout_information + { + uint32_t relative_depth{ 0u }; + std::list fanouts; + std::list extrefs; // IDs of POs (as in `_ntk.foreach_po`) + uint32_t num_edges{ 0u }; + }; + using fanouts_by_level = std::list; + + Ntk const& _ntk; + buffer_insertion_params _ps; + bool _outdated{ true }; + bool _is_scheduled_ASAP{ true }; + + /* The following data structures uniquely define the state (i.e. schedule) of the algorithm/flow. + The rest (`_fanouts` and `_num_buffers`) are computed from these by calling `count_buffers()`. */ + node_map _levels; + std::vector _po_levels; // imaginary node, must be at `num_phases * k + 1` + uint32_t _depth{ 0u }; + + /* Guarantees on `_fanouts` (when not `_outdated`): + * - Sum of `_fanouts[n][l].fanouts.length() + _fanouts[n][l].extrefs.length()` over all `l`s + * should be equal to `ntk.fanout_size( n )`. + * - If having only one fanout: `_fanouts[n].size() == 1`. + * - If having multiple fanouts: `_fanouts[n]` must have at least two elements, + * and the first element must have `relative_depth == 1` and `num_edges == 1`. + * - If `ci_capacity > 1`, `_fanouts[PI].size()` may be 1. + */ + node_map _fanouts; + node_map _num_buffers; + + node_map, Ntk> _timeframes; // only for SMT; the most extreme min/max + uint32_t _start_id; // for chunked movement +}; /* buffer_insertion */ + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/buffer_verification.hpp b/include/mockturtle/algorithms/aqfp/buffer_verification.hpp new file mode 100644 index 0000000..ca899c8 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/buffer_verification.hpp @@ -0,0 +1,292 @@ +/* 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 buffer_verification.hpp + \brief Verify buffered networks according to AQFP constraints + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/depth_view.hpp" +#include "aqfp_assumptions.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +void schedule_fanin_cone( Ntk& ntk, typename Ntk::node const& n, uint32_t l ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + ntk.set_visited( n, ntk.trav_id() ); + ntk.set_level( n, l ); + + ntk.foreach_fanin( n, [&]( auto const& fi ) { + schedule_fanin_cone( ntk, ntk.get_node( fi ), l - 1 ); + } ); +} + +template +uint32_t recompute_level( Ntk& ntk, typename Ntk::node const& n ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) + return ntk.level( n ); + ntk.set_visited( n, ntk.trav_id() ); + + uint32_t max_fi_level{ 0u }; + ntk.foreach_fanin( n, [&]( auto const& fi ) { + max_fi_level = std::max( max_fi_level, recompute_level( ntk, ntk.get_node( fi ) ) ); + } ); + ntk.set_level( n, max_fi_level + 1 ); + return max_fi_level + 1; +} + +} // namespace detail + +/*! \brief Find a reasonable level assignment for a buffered network given PI levels. + * + * \param ntk Buffered network + * \param pi_levels Levels of PIs + * \return Level assignment to all nodes + */ +template +node_map schedule_buffered_network_with_PI_levels( Ntk const& ntk, std::vector const& pi_levels ) +{ + assert( pi_levels.size() == ntk.num_pis() ); + + using node = typename Ntk::node; + node_map levels( ntk ); + depth_view dv{ ntk }; + + ntk.incr_trav_id(); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); + ntk.foreach_pi( [&]( auto const& n, auto i ) { + ntk.set_visited( n, ntk.trav_id() ); + dv.set_level( n, pi_levels[i] ); + } ); + + ntk.foreach_po( [&]( auto const& f ){ + detail::recompute_level( dv, ntk.get_node( f ) ); + }); + + ntk.foreach_node( [&]( auto const& n ) { + levels[n] = dv.level( n ); + } ); + + return levels; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param levels Level assignment for all nodes + * \return Whether `ntk` is path-balanced and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, aqfp_assumptions_legacy const& ps, node_map const& levels ) +{ + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + bool legal = true; + + /* fanout branching */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) ) + return true; + if ( !ps.branch_pis && ntk.is_pi( n ) ) + return true; + + if ( ntk.is_buf( n ) ) + legal &= ( ntk.fanout_size( n ) <= ps.splitter_capacity ); + else /* logic gate */ + legal &= ( ntk.fanout_size( n ) <= 1 ); + + return true; + } ); + + /* path balancing */ + ntk.foreach_node( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto ni = ntk.get_node( fi ); + if ( !ntk.is_constant( ni ) && ( ps.balance_pis || !ntk.is_pi( ni ) ) ) + legal &= ( levels[ni] == levels[n] - 1 ); + assert( legal ); + } ); + } ); + + if ( ps.balance_pis ) + { + ntk.foreach_pi( [&]( auto const& n ) { + legal &= ( levels[n] == 0 ); + } ); + } + + if ( ps.balance_pos ) + { + uint32_t depth{ 0u }; + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ( ps.balance_pis || !ntk.is_pi( n ) ) ) + { + if ( depth == 0u ) + depth = levels[n]; + else + legal &= ( levels[n] == depth ); + } + } ); + } + + return legal; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param levels Level assignment for all nodes + * \return Whether `ntk` is path-balanced and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, aqfp_assumptions_realistic const& ps, node_map const& levels ) +{ + static_assert( is_buffered_network_type_v, "Ntk is not a buffered network" ); + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + bool legal = true; + + /* fanout branching */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) ) + return; + if ( ntk.is_pi( n ) ) + { + legal &= ( ntk.fanout_size( n ) <= ps.ci_capacity ); + } + else if ( ntk.is_buf( n ) ) + { + legal &= ( ntk.fanout_size( n ) <= ps.splitter_capacity ); + } + else /* logic gate */ + { + legal &= ( ntk.fanout_size( n ) <= 1 ); + } + assert( legal ); + } ); + + /* path balancing */ + ntk.foreach_node( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto ni = ntk.get_node( fi ); + if ( !ntk.is_constant( ni ) ) + legal &= ( levels[ni] == levels[n] - 1 ); + assert( legal ); + } ); + } ); + + if ( ps.balance_cios ) + { + auto const check_pi_fn = [&]( uint32_t level ){ + for ( auto const& p : ps.ci_phases ) + { + if ( level == p ) + return true; + } + return false; + }; + + ntk.foreach_pi( [&]( auto const& n ) { + legal &= check_pi_fn( levels[n] ); + assert( legal ); + } ); + + uint32_t depth{ 0u }; + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) ) + { + if ( depth == 0u ) + depth = levels[n]; + else + legal &= ( levels[n] == depth ); + assert( legal ); + } + } ); + legal &= ( depth % ps.num_phases == 0 ); + assert( legal ); + } + else + { + auto const check_pi_fn = [&]( uint32_t level ){ + for ( auto const& p : ps.ci_phases ) + { + if ( level >= p && ( level - p ) % ps.num_phases == 0 ) + return true; + } + return false; + }; + + ntk.foreach_pi( [&]( auto const& n ) { + legal &= check_pi_fn( levels[n] ); + assert( legal ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + auto n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) ) + { + legal &= ( levels[n] % ps.num_phases == 0 ); + assert( legal ); + } + } ); + } + + // TODO: max_phase_skip + + return legal; +} + +/*! \brief Verify a buffered network according to AQFP assumptions with provided PI level assignment. + * + * \param ntk Buffered network + * \param ps AQFP assumptions + * \param pi_levels Levels of PIs + * \return Whether `ntk` is path-balanced, phase-aligned, and properly-branched + */ +template +bool verify_aqfp_buffer( Ntk const& ntk, Asmp const& ps, std::vector const& pi_levels ) +{ + auto const levels = schedule_buffered_network_with_PI_levels( ntk, pi_levels ); + return verify_aqfp_buffer( ntk, ps, levels ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/dag.hpp b/include/mockturtle/algorithms/aqfp/detail/dag.hpp new file mode 100644 index 0000000..e35bc9d --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/dag.hpp @@ -0,0 +1,159 @@ +/* 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 dag.hpp + \brief AQFP DAG data structure + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Represents a single-output connected Partial DAG or DAG. + * + * A partial DAG is a network with majority gates where some gates may have unconnected fanin slots. + * A DAG is a network with majority gates obtained from a partial DAG by specifying which + * unconnected fanin slots connect to the same primary input. + * Optionally, a DAG may designate which fanin slots are connected the constant 0. + * Unlike logic networks elsewhere in mockturtle, gates are numbered from 0 starting from the top gate. + */ +template +struct aqfp_dag +{ + using node_type = NodeT; + + std::vector> nodes; // fanins of nodes + std::vector input_slots; // identifiers of the input slots (bundles of fanins where the inputs will be connected) + NodeT zero_input = 0; // id of the input slot that is connected to constant 0 + + aqfp_dag( const std::vector>& nodes = {}, const std::vector& input_slots = {}, node_type zero_input = {} ) + : nodes( nodes ), input_slots( input_slots ), zero_input( zero_input ) {} + + aqfp_dag( const std::string& str ) + { + decode_from_string( str ); + } + + /*! \brief Compare to logical networks for equality. */ + bool operator==( const aqfp_dag& rhs ) const + { + if ( nodes.size() != rhs.nodes.size() ) + return false; + + for ( auto i = 0u; i < nodes.size(); i++ ) + { + auto x1 = nodes[i]; + auto x2 = rhs.nodes[i]; + std::stable_sort( x1.begin(), x1.end() ); + std::stable_sort( x2.begin(), x2.end() ); + if ( x1 != x2 ) + return false; + } + + auto y1 = input_slots; + auto y2 = rhs.input_slots; + std::stable_sort( y1.begin(), y1.end() ); + std::stable_sort( y2.begin(), y2.end() ); + if ( y1 != y2 ) + return false; + + if ( zero_input != rhs.zero_input ) + return false; + + return true; + } + + /*! \brief Return the number of majority gates. */ + uint32_t num_gates() const + { + return nodes.size() - input_slots.size(); + } + + /*! \brief Decode a string representation of a DAG into a DAG. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + void decode_from_string( const std::string& str ) + { + std::istringstream iss( str ); + auto ng = 0u; + auto ni = 0u; + auto zi = 0u; + + iss >> ng >> ni >> zi; + zero_input = zi; + + std::vector level( ng + ni, 0u ); + for ( auto i = 0u; i < ng; i++ ) + { + auto nf = 0u; + iss >> nf; + + nodes.push_back( {} ); + + for ( auto j = 0u; j < nf; j++ ) + { + auto t = 0u; + iss >> t; + nodes[i].push_back( t ); + } + } + + for ( auto i = 0u; i < ni; i++ ) + { + nodes.push_back( {} ); + input_slots.push_back( nodes.size() - 1 ); + } + } + + /*! \brief Encode a DAG as a string. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + std::string encode_as_string() const + { + std::stringstream ss; + ss << num_gates() << " " << input_slots.size() << " " << zero_input; + for ( auto i = 0u; i < num_gates(); i++ ) + { + ss << fmt::format( " {} {}", nodes[i].size(), fmt::join( nodes[i], " " ) ); + } + + return ss.str(); + } +}; + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp b/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp new file mode 100644 index 0000000..7c9de61 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/dag_cost.hpp @@ -0,0 +1,508 @@ +/* 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 dag_cost.hpp + \brief Cost computing functions for AQFP DAG structures + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include "../../../properties/aqfpcost.hpp" + +namespace mockturtle +{ + +/*! \brief Cost function for dag structures that compute the gate cost. */ +template +class dag_gate_cost +{ +public: + dag_gate_cost( const std::unordered_map& gate_costs ) : gate_costs( gate_costs ) {} + + double operator()( const Ntk& net ) + { + double res = 0.0; + + for ( const auto& node : net.nodes ) + { + if ( !node.empty() ) + res += gate_costs.at( node.size() ); + } + + return res; + } + +private: + std::unordered_map gate_costs; +}; + +/*! \brief Cost function for dag structures that compute the gate and path balancing cost. */ +template +class dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using depth_config_t = uint64_t; + + dag_aqfp_cost( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : simp_cc( gate_costs ), fanout_cc( splitters ) {} + + /*! \brief Compute cost assuming all primary inputs are at the same depth. */ + double operator()( const Ntk& orig_net ) + { + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + + // perform depth bounded search + double cost = IMPOSSIBLE; + + auto lastlev = *( std::max_element( minlev.begin(), minlev.end() ) ); + while ( true ) + { + compute_max_levels( net, fanout, maxlev, lastlev ); + + // fix levels for the root and the inputs + maxlev[0] = 0; + for ( auto f : net.input_slots ) + { + if ( f != net.zero_input ) + { + minlev[f] = lastlev; + } + } + + // check different level configurations for the other gates and compute the cost for buffers and splitters + cost = compute_best_cost( 1u, 0.0 ); + + if ( cost < IMPOSSIBLE ) + { + break; + } + + lastlev++; + } + + // add the gate costs + cost += simp_cc( net ); + + return cost; + } + +protected: + dag_gate_cost simp_cc; + fanout_net_cost fanout_cc; + + Ntk net; + std::vector> fanout; + std::vector minlev; + std::vector maxlev; + std::vector curlev; + + template + void compute_fanouts( const Ntk& net, FanOutT& fanout ) + { + for ( auto i = 0u; i < net.nodes.size(); i++ ) + { + for ( auto&& f : net.nodes[i] ) + { + if ( net.zero_input != f ) + { + fanout[f].push_back( i ); + } + } + } + } + + template + void compute_min_levels( const Ntk& net, const FanOutT& fanout, MinLevT& minlev ) + { + for ( auto i = 0u; i < net.nodes.size(); i++ ) + { + if ( fanout[i].size() == 0u ) + { + minlev[i] = 0u; + } + else + { + auto critical_fo = *( std::max_element( fanout[i].begin(), fanout[i].end(), + [&]( auto x, auto y ) { return ( minlev[x] < minlev[y] ); } ) ); + minlev[i] = 1 + minlev[critical_fo]; + if ( fanout[i].size() > 1 ) + { + minlev[i]++; + } + } + } + } + + template + void compute_max_levels( const Ntk& net, const FanOutT& fanout, MaxLevT& maxlev, uint32_t lastlev ) + { + for ( auto& f : net.input_slots ) + { + if ( f != net.zero_input ) + { + maxlev[f] = lastlev; + } + else + { + maxlev[f] = std::numeric_limits::max(); + } + } + + for ( auto i = net.num_gates(); i > 0u; i-- ) + { + maxlev[i - 1] = std::numeric_limits::max(); + for ( auto f : net.nodes[i - 1] ) + { + auto t = maxlev[f] - 1; + if ( fanout[f].size() > 1 ) + { + t--; + } + if ( t < maxlev[i - 1] ) + { + maxlev[i - 1] = t; + } + } + } + } + + double cost_for_node_if_in_level( uint32_t lev, std::vector fanouts ) + { + std::vector rellev; + for ( auto fo : fanouts ) + { + rellev.push_back( lev - curlev[fo] ); + } + std::stable_sort( rellev.begin(), rellev.end() ); + + return fanout_cc( rellev ); + } + + double compute_best_cost( uint32_t current_gid, double cost_so_far ) + { + if ( net.num_gates() == current_gid ) + { + for ( auto f : net.input_slots ) + { + if ( ( f == net.zero_input ) || fanout[f].empty() ) + { + continue; + } + + cost_so_far += cost_for_node_if_in_level( maxlev[f], fanout[f] ); + + if ( cost_so_far >= IMPOSSIBLE ) + { + return IMPOSSIBLE; + } + } + + return cost_so_far; + } + + auto result = IMPOSSIBLE; + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + auto temp = compute_best_cost( current_gid + 1, cost_so_far + cost ); + if ( temp < result ) + { + result = temp; + } + } + + return result; + } +}; + +/*! \brief Compute cost together with the depth assignment to nodes for a given input depth configuration. */ +template +class dag_aqfp_cost_and_depths : public dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using dag_aqfp_cost::simp_cc; + using dag_aqfp_cost::fanout_cc; + using dag_aqfp_cost::net; + using dag_aqfp_cost::fanout; + using dag_aqfp_cost::minlev; + using dag_aqfp_cost::maxlev; + using dag_aqfp_cost::curlev; + using dag_aqfp_cost::compute_fanouts; + using dag_aqfp_cost::compute_min_levels; + using dag_aqfp_cost::compute_max_levels; + using dag_aqfp_cost::cost_for_node_if_in_level; + + dag_aqfp_cost_and_depths( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : dag_aqfp_cost( gate_costs, splitters ) {} + + std::pair> operator()( const Ntk& orig_net, const std::vector& input_depths ) + { + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + compute_max_levels( net, fanout, maxlev, input_depths ); + + uint32_t ind = 0u; + + // fix levels for the root and the inputs + maxlev[0] = 0; + ind = 0u; + for ( auto f : net.input_slots ) + { + if ( f != net.zero_input ) + { + minlev[f] = input_depths[ind++]; + } + } + + // check different level configurations for the other gates and compute the cost for buffers and splitters + auto [cost, levels] = compute_best_cost_and_levels( 1u, 0.0 ); + + // add the gate costs + cost += simp_cc( net ); + + return { cost, levels }; + } + +private: + template + void compute_max_levels( const Ntk& net, const FanOutT& fanout, MaxLevT& maxlev, std::vector input_depths ) + { + uint32_t ind = 0u; + for ( auto& f : net.input_slots ) + { + if ( f != net.zero_input ) + { + maxlev[f] = input_depths[ind++]; + } + else + { + maxlev[f] = std::numeric_limits::max(); + } + } + + for ( auto i = net.num_gates(); i > 0u; i-- ) + { + maxlev[i - 1] = std::numeric_limits::max(); + for ( auto f : net.nodes[i - 1] ) + { + auto t = maxlev[f] - 1; + if ( fanout[f].size() > 1 ) + { + t--; + } + if ( t < maxlev[i - 1] ) + { + maxlev[i - 1] = t; + } + } + } + } + + std::tuple> compute_best_cost_and_levels( uint32_t current_gid, double cost_so_far ) + { + if ( net.num_gates() == current_gid ) + { + for ( auto f : net.input_slots ) + { + if ( ( f == net.zero_input ) || fanout[f].empty() ) + { + continue; + } + + curlev[f] = maxlev[f]; + cost_so_far += cost_for_node_if_in_level( maxlev[f], fanout[f] ); + + if ( cost_so_far >= IMPOSSIBLE ) + { + return { IMPOSSIBLE, {} }; + } + } + + return { cost_so_far, curlev }; + } + + double res_cost = IMPOSSIBLE; + std::vector res_lev = {}; + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + auto [temp_cost, temp_lev] = compute_best_cost_and_levels( current_gid + 1, cost_so_far + cost ); + if ( temp_cost < res_cost ) + { + res_cost = temp_cost; + res_lev = temp_lev; + } + } + + return { res_cost, res_lev }; + } +}; + +/*! \brief Compute costs for different input depth configurations. */ +template +class dag_aqfp_cost_all_configs : public dag_aqfp_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + using depth_config_t = uint64_t; // each byte encodes a depth of a primary input + + using dag_aqfp_cost::simp_cc; + using dag_aqfp_cost::fanout_cc; + using dag_aqfp_cost::net; + using dag_aqfp_cost::fanout; + using dag_aqfp_cost::minlev; + using dag_aqfp_cost::maxlev; + using dag_aqfp_cost::curlev; + using dag_aqfp_cost::compute_fanouts; + using dag_aqfp_cost::compute_min_levels; + using dag_aqfp_cost::compute_max_levels; + using dag_aqfp_cost::cost_for_node_if_in_level; + + dag_aqfp_cost_all_configs( const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : dag_aqfp_cost( gate_costs, splitters ) {} + + std::unordered_map operator()( const Ntk& orig_net ) + { + std::unordered_map config_cost; + net = orig_net; + fanout = std::vector>( net.nodes.size() ); + minlev = std::vector( net.nodes.size() ); + maxlev = std::vector( net.nodes.size() ); + curlev = std::vector( net.nodes.size() ); + + compute_fanouts( net, fanout ); + compute_min_levels( net, fanout, minlev ); + + auto lastlev = *( std::max_element( minlev.begin(), minlev.end() ) ); + while ( true ) + { + compute_max_levels( net, fanout, maxlev, lastlev ); + + // fix levels for the root and the inputs + maxlev[0] = 0; + + // check different level configurations for the other gates and compute the cost for buffers and splitters + compute_best_costs_for_all_configs( 1u, 0.0, config_cost ); + + if ( config_cost.size() > 0 ) + { + break; + } + + lastlev++; + } + + double cost_for_gates = simp_cc( net ); + + for ( auto it = config_cost.begin(); it != config_cost.end(); it++ ) + { + it->second += cost_for_gates; + } + + return config_cost; + } + +private: + void compute_best_costs_for_all_configs( uint32_t current_gid, double cost_so_far, std::unordered_map& config_cost ) + { + if ( net.nodes.size() == current_gid ) + { + depth_config_t depth_config = 0u; + uint32_t ind = 0; + for ( auto f : net.input_slots ) + { + if ( f == net.zero_input ) + { + continue; + } + + depth_config |= ( curlev[f] << ( 8 * ind ) ); + ind++; + } + + if ( !config_cost.count( depth_config ) ) + { + config_cost[depth_config] = IMPOSSIBLE; + } + + config_cost[depth_config] = std::min( config_cost[depth_config], cost_so_far ); + return; + } + + if ( net.zero_input == (typename Ntk::node_type)current_gid ) + { + compute_best_costs_for_all_configs( current_gid + 1, cost_so_far, config_cost ); + return; + } + + for ( auto lev = minlev[current_gid]; lev <= maxlev[current_gid]; lev++ ) + { + auto cost = cost_for_node_if_in_level( lev, fanout[current_gid] ); + if ( cost >= IMPOSSIBLE ) + { + continue; + } + curlev[current_gid] = lev; + compute_best_costs_for_all_configs( current_gid + 1, cost_so_far + cost, config_cost ); + } + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp b/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp new file mode 100644 index 0000000..4ad384e --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/dag_gen.hpp @@ -0,0 +1,389 @@ +/* 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 dag_gen.hpp + \brief AQFP DAG generation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dag.hpp" +#include "dag_util.hpp" +#include "partial_dag.hpp" + +namespace mockturtle +{ + +using part = std::multiset; +using partition = std::multiset; +using partition_set = std::set; + +struct dag_generator_params +{ + + uint32_t max_gates; // max number of gates allowed + uint32_t max_levels; // max number of gate levels + uint32_t max_num_in; // max number of primary input slots (including the constant) + uint32_t max_num_fanout; // max number of fanouts per gate + uint32_t max_width; // max number of gates in any given level + + std::vector allowed_num_fanins; // the types of allowed majority gates + std::unordered_map max_gates_of_fanin; // max number of gates allowed for each type + + uint32_t verbose; + + dag_generator_params() : max_gates( std::numeric_limits::max() ), + max_levels( std::numeric_limits::max() ), + max_num_in( std::numeric_limits::max() ), + max_num_fanout( std::numeric_limits::max() ), + max_width( std::numeric_limits::max() ), + allowed_num_fanins( { 3u } ), + max_gates_of_fanin( { { 3u, std::numeric_limits::max() } } ), + verbose( 0u ) {} +}; + +/*! \brief Generate all DAGs derived from a given partial DAG. */ +template +class dags_from_partial_dag +{ + using PartialNtk = aqfp_partial_dag; + using Ntk = aqfp_dag; + +public: + dags_from_partial_dag( uint32_t max_num_in, uint32_t max_num_fanout ) : max_num_in( max_num_in ), max_num_fanout( max_num_fanout ) {} + + std::vector operator()( const PartialNtk& net ) + { + std::vector leaves = net.last_layer_leaves; + leaves.insert( leaves.end(), net.other_leaves.begin(), net.other_leaves.end() ); + std::stable_sort( leaves.begin(), leaves.end() ); + + auto max_counts = net.max_equal_fanins(); + + auto partitions = partition_gen( leaves, max_counts, max_num_in + 1, max_num_fanout ); + + std::vector result; + for ( auto p : partitions ) + { + auto new_net = get_dag_for_partition( net, p ); + + if ( net.input_slots.size() <= max_num_in ) + { + result.push_back( new_net ); + } + + int i = 0; + for ( auto it = p.begin(); it != p.end(); ) + { + auto temp_net = new_net; + temp_net.zero_input = new_net.input_slots[i]; + result.push_back( temp_net ); + auto c = p.count( *it ); + i += c; + std::advance( it, c ); + } + } + + return result; + } + +private: + uint32_t max_num_in; + uint32_t max_num_fanout; + detail::partition_generator partition_gen; + + /** + * \brief Compute the DAG obtained from partial DAG `orig` by combining slots + * according to partitions `p`. + */ + Ntk get_dag_for_partition( const PartialNtk& orig, const partition& p ) + { + auto net = orig.copy_without_leaves(); + std::for_each( p.begin(), p.end(), [&net]( const auto& q ) { net.add_leaf_node( q ); } ); + return std::move( net ); + } +}; + +#if !__clang__ || __clang_major__ > 10 + +/*! \brief Generate all DAGs satisfying the parameters. */ +template +class dag_generator +{ + using PartialNtk = aqfp_partial_dag; + +public: + dag_generator( const dag_generator_params& params, uint32_t num_threads = 1u ) : params( params ), num_threads( num_threads ) {} + + template + void for_each_dag( Fn&& callback ) + { + if ( params.verbose > 0u ) + { + std::cerr << fmt::format( "Generating partial dags.\n" ); + } + + generate_all_partial_dags(); + + if ( params.verbose > 0u ) + { + std::cerr << fmt::format( "Generated {} partial dags.\n", partial_dags.size() ); + std::cerr << fmt::format( "Generating dags in {} threads...\n", num_threads ); + } + + std::vector threads; + std::mutex mu; + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + dags_from_partial_dag dag_from_pdag( params.max_num_in, params.max_num_fanout ); + while ( true ) + { + mu.lock(); + if ( partial_dags.empty() ) + { + mu.unlock(); + return; + } + auto p = partial_dags.front(); + partial_dags.pop(); + mu.unlock(); + auto dags = dag_from_pdag( p ); + for ( const auto& dag : dags ) + { + callback( dag, id ); + } + } + }, + i ); + } + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads[i].join(); + } + } + +private: + dag_generator_params params; + uint32_t num_threads; + + std::queue partial_dags; + + detail::partition_generator partition_gen; + detail::partition_extender partition_ext; + detail::sublist_generator sublist_gen; + + void generate_all_partial_dags() + { + std::stack stk; + + for ( auto&& fin : params.allowed_num_fanins ) + { + if ( params.max_gates_of_fanin.at( fin ) > 0 ) + { + auto root = PartialNtk::get_root( fin ); + stk.push( root ); + } + } + + while ( !stk.empty() ) + { + auto res = stk.top(); + stk.pop(); + + partial_dags.push( res ); + + if ( params.max_levels > res.num_levels ) + { + auto ext = get_layer_extension( res ); + for ( const auto& e : ext ) + { + stk.push( e ); + } + } + } + } + + /*! \brief Extend the current aqfp logical network by one more level. */ + std::vector get_layer_extension( const PartialNtk& net ) + { + std::vector result; + + auto max_counts = net.max_equal_fanins(); + + auto last_options = sublist_gen( net.last_layer_leaves ); + auto other_options = sublist_gen( net.other_leaves ); + + auto last_counts = detail::get_frequencies( net.last_layer_leaves ); + auto other_counts = detail::get_frequencies( net.other_leaves ); + + auto net_without_leaves = net.copy_without_leaves(); + + /* Consider all different ways of choosing a non-empty subset of last layer slots */ + for ( auto&& last : last_options ) + { + if ( last.empty() ) + continue; + + /* Remaining slots in the last layer */ + auto last_counts_cpy = last_counts; + for ( auto&& e : last ) + { + last_counts_cpy[e]--; + } + + /* Consider all different ways of choosing a subset of other layer slots */ + for ( auto&& other : other_options ) + { + + /* Remaining slots in the other layers */ + auto other_counts_cpy = other_counts; + for ( auto&& e : other ) + { + other_counts_cpy[e]--; + } + + /* Compute the new set of other leaves for all resulting partial DAGs */ + std::vector other_leaves_new; + for ( auto it = last_counts_cpy.begin(); it != last_counts_cpy.end(); it++ ) + { + for ( auto i = 0u; i < it->second; i++ ) + { + other_leaves_new.push_back( it->first ); + } + } + for ( auto it = other_counts_cpy.begin(); it != other_counts_cpy.end(); it++ ) + { + for ( auto i = 0u; i < it->second; i++ ) + { + other_leaves_new.push_back( it->first ); + } + } + + if ( params.max_gates == 0 || params.max_gates > net_without_leaves.num_gates() ) + { + auto max_gates = params.max_gates > 0u ? params.max_gates - net_without_leaves.num_gates() : 0u; + auto last_layers_partitions = partition_gen( last, max_counts, max_gates, params.max_num_fanout ); + + for ( auto p : last_layers_partitions ) + { + auto extensions = partition_ext( other, p, max_counts, params.max_num_fanout ); + for ( auto q : extensions ) + { + auto temp = get_next_partial_dags( net_without_leaves, q, other_leaves_new ); + for ( auto&& r : temp ) + { + r.num_levels++; + result.push_back( r ); + } + } + } + } + } + } + + return result; + } + + /*! \brief Compute the partial DAGs obtained by combining the slots of `orig` as indicated by + * partitioning 'p'. + */ + std::vector get_next_partial_dags( const PartialNtk& orig, const partition& p, const std::vector& other_leaves ) + { + auto max_allowed_of_fanin = params.max_gates_of_fanin; + for ( auto it = orig.num_gates_of_fanin.begin(); it != orig.num_gates_of_fanin.end(); it++ ) + { + assert( max_allowed_of_fanin[it->first] >= it->second ); + max_allowed_of_fanin[it->first] -= it->second; + } + + std::vector q( p.begin(), p.end() ); + + /* For each part in partition 'p', consider gates of different number of fanins to connect. */ + auto res = add_node_recur( orig, q, 0, max_allowed_of_fanin ); + + for ( auto&& net : res ) + { + net.other_leaves = other_leaves; + } + + return res; + } + + /*! \brief Recursively consider gates with different number of fanins for different parts in partition `p`. */ + std::vector add_node_recur( const PartialNtk& orig, const std::vector& p, uint32_t ind, std::unordered_map& max_allowed_of_fanin ) + { + if ( ind == p.size() ) + { + return { orig }; + } + + std::vector res; + + /* Decide what fanin gate to use for part in partition 'p' at index 'ind'. */ + for ( auto&& fin : params.allowed_num_fanins ) + { + if ( max_allowed_of_fanin[fin] == 0 ) + { + continue; + } + max_allowed_of_fanin[fin]--; + + auto temp = add_node_recur( orig, p, ind + 1, max_allowed_of_fanin ); + + for ( const auto& t : temp ) + { + auto net = t.copy_with_last_layer_leaves(); + net.add_internal_node( fin, p[ind], true ); + res.push_back( net ); + } + + max_allowed_of_fanin[fin]++; + } + + return res; + } +}; + +#endif + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp b/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp new file mode 100644 index 0000000..ec4b95c --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/dag_util.hpp @@ -0,0 +1,316 @@ +/* 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 dag_util.hpp + \brief Utilities for DAG generation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../../utils/hash_functions.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +/*! \brief Computes and returns the frequency map for a given collection of elements. + * Use std::map instead of std::unordered_map because we use it as a key in a hash-table so the order is important to compute the hash + */ +template +inline std::map get_frequencies( const std::vector& elems ) +{ + std::map elem_counts; + std::for_each( elems.begin(), elems.end(), [&elem_counts]( auto e ) { elem_counts[e]++; } ); + return elem_counts; +} + +template +class partition_generator +{ + using part = std::multiset; + using partition = std::multiset; + using partition_set = std::set; + + using inner_cache_key_t = std::vector; + using inner_cache_t = std::unordered_map>; + + using outer_cache_key_t = std::tuple, uint32_t, uint32_t>; + using outer_cache_t = std::unordered_map>; + +public: + /*! \brief Computes and returns a set of partitions for a given list of elements + * such that no part contains any element `e` more than `max_counts[e]` times. + */ + partition_set operator()( + std::vector elems, + const std::vector& max_counts = {}, + uint32_t max_parts = 0, + uint32_t max_part_size = 0 ) + { + _elems = elems; + _max_counts = max_counts; + _max_parts = max_parts; + _max_part_size = max_part_size; + + const outer_cache_key_t key = { _max_counts, _max_parts, _max_part_size }; + partition_cache = outer_cache.insert( { key, inner_cache_t() } ).first; + + return get_all_partitions(); + } + +private: + outer_cache_t outer_cache; + + typename outer_cache_t::iterator partition_cache; + std::vector _elems; + std::vector _max_counts; + uint32_t _max_parts; + uint32_t _max_part_size; + + partition_set get_all_partitions() + { + if ( _elems.size() == 0 ) + { + return { {} }; // return the empty partition. + } + + inner_cache_key_t key = _elems; + if ( partition_cache->second.count( key ) ) + { + return partition_cache->second.at( key ); + } + + partition_set result; + + auto last = _elems.back(); + _elems.pop_back(); + + auto temp = get_all_partitions(); + + for ( auto&& t : temp ) + { + partition cpy; + + // take 'last' in its own partition + cpy = t; + + if ( _max_parts == 0u || _max_parts > cpy.size() ) + { + cpy.insert( { last } ); + result.insert( cpy ); + } + + // add 'last' to one of the existing partitions + for ( auto it = t.begin(); it != t.end(); ) + { + if ( _max_counts.empty() || it->count( last ) < _max_counts[last] ) + { + + if ( _max_part_size == 0 || _max_part_size > it->size() ) + { + cpy = t; + auto elem_it = cpy.find( *it ); + auto cpy_elem = *elem_it; + cpy_elem.insert( last ); + cpy.erase( elem_it ); + cpy.insert( cpy_elem ); + result.insert( cpy ); + } + } + + std::advance( it, t.count( *it ) ); + } + } + + return ( partition_cache->second[key] = result ); + } +}; + +template +class partition_extender +{ + using part = std::multiset; + using partition = std::multiset; + using partition_set = std::set; + + using inner_cache_key_t = std::vector; + using inner_cache_t = std::unordered_map>; + + using outer_cache_key_t = std::tuple, uint32_t>; + using outer_cache_t = std::map; + +public: + /*! \brief Compute a list of different partitions that can be obtained by adding elements + * in `elems` to the parts of `base` such that no part contains any element `e` more than + * `max_counts[e]` times + */ + partition_set operator()( std::vector elems, partition base, const std::vector& max_counts, uint32_t max_part_size = 0 ) + { + _elems = elems; + _base = base; + _max_counts = max_counts; + _max_part_size = max_part_size; + + const outer_cache_key_t key = { _base, _max_counts, _max_part_size }; + partition_cache = outer_cache.insert( { key, inner_cache_t() } ).first; + + return extend_partitions(); + } + +private: + outer_cache_t outer_cache; + + typename outer_cache_t::iterator partition_cache; + std::vector _elems; + partition _base; + std::vector _max_counts; + uint32_t _max_part_size; + + partition_set extend_partitions() + { + if ( _elems.size() == 0 ) + { + return { _base }; + } + + inner_cache_key_t key = _elems; + if ( partition_cache->second.count( key ) ) + { + return partition_cache->second.at( key ); + } + + partition_set result; + + auto last = _elems.back(); + _elems.pop_back(); + + auto temp = extend_partitions(); + for ( auto&& t : temp ) + { + partition cpy; + + for ( auto it = t.begin(); it != t.end(); ) + { + if ( it->count( last ) < _max_counts.at( last ) ) + { + + if ( _max_part_size == 0 || _max_part_size > it->size() ) + { + cpy = t; + auto elem_it = cpy.find( *it ); + auto cpy_elem = *elem_it; + cpy_elem.insert( last ); + cpy.erase( elem_it ); + cpy.insert( cpy_elem ); + result.insert( cpy ); + } + } + + std::advance( it, t.count( *it ) ); + } + } + + return ( partition_cache->second[key] = result ); + } +}; + +template +struct sublist_generator +{ + using sub_list_cache_key_t = std::map; + +public: + /** + * \brief Given a list of elements `elems`, generate all sub lists of those elements. + * Ex: if `elems` = [1, 2, 2, 3], this will generate the following lists: + * [0], [1], [1, 2], [1, 2, 2], [1, 2, 2, 3], [1, 2, 3], [1, 3], [2], [2, 2], [2, 2, 3], [2, 3], and [3]. + */ + std::set> operator()( std::vector elems ) + { + elem_counts = get_frequencies( elems ); + return get_sub_lists_recur(); + } + +private: + std::unordered_map>, hash> sub_list_cache; + std::map elem_counts; + + std::set> get_sub_lists_recur() + { + if ( elem_counts.size() == 0u ) + { + return { {} }; + } + + sub_list_cache_key_t key = elem_counts; + if ( !sub_list_cache.count( key ) ) + { + auto last = std::prev( elem_counts.end() ); + auto last_elem = last->first; + auto last_count = last->second; + elem_counts.erase( last ); + + std::set> result; + + std::vector t; + for ( auto i = last_count; i > 0; --i ) + { + t.push_back( last_elem ); + result.insert( t ); // insert a copy of t, and note that t is already sorted. + } + + auto temp = get_sub_lists_recur(); + + for ( std::vector t : temp ) + { + result.insert( t ); + for ( auto i = last_count; i > 0; --i ) + { + t.push_back( last_elem ); + std::stable_sort( t.begin(), t.end() ); + result.insert( t ); + } + } + + sub_list_cache[key] = result; + } + + return sub_list_cache[key]; + } +}; + +} // namespace detail + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp b/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp new file mode 100644 index 0000000..7676d1f --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/db_builder.hpp @@ -0,0 +1,319 @@ +/* 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 db_builder.hpp + \brief Builder class for AQFP DAG database + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../aqfp_db.hpp" +#include "dag.hpp" +#include "dag_cost.hpp" +#include "npn_cache.hpp" + +namespace mockturtle +{ + +/*! \brief A class to help the generation of AQFP database in an incremental manner. */ +template> +class aqfp_db_builder +{ + using db_type = aqfp_db; + using replacement = typename db_type::replacement; + +public: + aqfp_db_builder( + const std::unordered_map& gate_costs = { { 3u, 6.0 }, { 5u, 10.0 } }, + const std::unordered_map& splitters = { { 1u, 2.0 }, { 4u, 2.0 } } ) + : gate_costs( gate_costs ), splitters( splitters ), cc( gate_costs, splitters ) {} + + db_type build() + { + return db_type( db, gate_costs, splitters ); + } + + /*! \brief Update the database with a rusult for network `net`. */ + void update( const Ntk& ntk, const std::unordered_map& cost_config, uint32_t N = 4u ) + { + std::vector input_tt = { 0x0000UL, 0xaaaaUL, 0xccccUL, 0xf0f0UL, 0xff00UL }; + auto fs = all_functions_from_dag( input_tt, ntk ); + + std::unordered_set npn; + for ( auto f : fs ) + { + auto tmp = npndb( f ); + + auto& npntt = std::get<0>( tmp ); + auto& npnperm = std::get<2>( tmp ); + + if ( npn.count( npntt ) ) // already processed + { + continue; + } + npn.insert( npntt ); + + std::vector revperm( N ); + for ( uint32_t i = 0; i < N; i++ ) + { + revperm[npnperm[i]] = i; + } + + for ( auto it = cost_config.begin(); it != cost_config.end(); it++ ) + { + auto& lvl_cfg = it->first; + auto& cost = it->second; + std::vector new_levels = { level_of_input( lvl_cfg, npnperm[0] ), + level_of_input( lvl_cfg, npnperm[1] ), + level_of_input( lvl_cfg, npnperm[2] ), + level_of_input( lvl_cfg, npnperm[3] ) }; + assert( new_levels.size() == N ); + auto new_lvl_cfg = lvl_cfg_from_vec( new_levels ); + + if ( !db[npntt].count( new_lvl_cfg ) || db[npntt][new_lvl_cfg].cost > cost ) + { + db[npntt][new_lvl_cfg] = { cost, ntk, new_levels, revperm }; + } + } + } + } + + /*! Filter database configurations that are "covered" by other configurations. */ + void remove_redundant( bool verbose = false ) + { + for ( auto i = db.begin(); i != db.end(); i++ ) + { + auto& npn = i->first; + auto& configs = i->second; + + std::map good_configs; + for ( auto it = configs.begin(); it != configs.end(); it++ ) + { + bool ok = true; + + uint32_t N = it->second.input_levels.size(); + + for ( auto jt = configs.begin(); jt != configs.end(); jt++ ) + { + if ( jt->first == it->first ) + { + continue; + } + + /* check whether there exist input-wise smaller level config */ + bool jt_smaller_to_it = true; + for ( auto j = 0; j < N; j++ ) + { + if ( level_of_input( jt->first, j ) > level_of_input( it->first, j ) ) + { + jt_smaller_to_it = false; + break; + } + } + if ( jt_smaller_to_it ) + { + double extra_buff_cost = 0; + for ( auto j = 0; j < N; j++ ) + { + extra_buff_cost += splitters.at( 1u ) * ( level_of_input( it->first, j ) - level_of_input( jt->first, j ) ); + } + if ( extra_buff_cost + jt->second.cost <= it->second.cost ) + { + if ( verbose ) + { + std::cerr << fmt::format( "[aqfp_db] configuration {:04x} already covered by {:04x} [{} {} {}].\n", + it->first, jt->first, it->second.cost, jt->second.cost, extra_buff_cost ); + } + ok = false; + break; + } + } + } + if ( ok ) + { + good_configs[it->first] = it->second; + } + } + db[npn] = good_configs; + } + } + + /*! \brief Save database to the output stream `os`. */ + void save_db_to_file( std::ostream& os, bool hardcode = false ) + { + if ( !hardcode ) + { + /* output the database in plain-text encoding */ + + /* number of functions */ + os << fmt::format( "{}\n", db.size() ); + for ( auto i = db.begin(); i != db.end(); i++ ) + { + /* npn class */ + auto& k = i->first; + + auto& m = i->second; + os << fmt::format( "{:04x}\n", k ); + + /* number of entries for the npn class */ + os << fmt::format( "{}\n", m.size() ); + for ( auto it = m.begin(); it != m.end(); it++ ) + { + auto& lvl_cfg = it->first; + auto& r = it->second; + + os << fmt::format( "{:08x}\n", lvl_cfg ); + os << fmt::format( "{}\n", r.cost ); + os << fmt::format( "{}\n", r.ntk.encode_as_string() ); + os << fmt::format( "{}\n", fmt::join( r.input_perm, " " ) ); + } + } + + return; + } + + /* output the database encoded as an initializer list */ + + os << "{\n"; + for ( auto i = db.begin(); i != db.end(); i++ ) + { + os << fmt::format( "\t{{ 0x{:04x}, {{\n", i->first ); + + for ( auto j = i->second.begin(); j != i->second.end(); j++ ) + { + os << fmt::format( "\t\t\t\t{{ 0x{:08x}, ", j->first ); + os << fmt::format( "{{ {}, {{ \"{}\" }}, {{ {} }}, {{ {} }} }} }},\n", + j->second.cost, + j->second.ntk.encode_as_string(), + fmt::join( j->second.input_levels, ", " ), + fmt::join( j->second.input_perm, ", " ) ); + } + + os << "\t\t}\n\t},\n"; + } + os << "}\n"; + } + + /*! \brief Load database from input stream `is`. */ + void load_db( std::istream& is ) + { + aqfp_db::load_db( is, db ); + } + +private: + std::unordered_map gate_costs; + std::unordered_map splitters; + std::unordered_map> db; + dag_aqfp_cost_and_depths cc; + npn_cache npndb; + + /*! \brief Compute all functions synthesizable from `net` if input slots are assigned the truthtables in `input_tt`. */ + std::unordered_set all_functions_from_dag( const std::vector& input_tt, const Ntk& net ) + { + // static_assert( N == 4u, "Template parameter N must be equal to 4 in the current implementation" ); + + uint32_t num_inputs = net.input_slots.size(); + if ( net.zero_input != 0 ) + { + num_inputs--; + } + + std::unordered_set res; + + std::vector tt( net.nodes.size(), input_tt[0] ); + auto input_ind = 1u; + + auto tmp_input_slots = net.input_slots; + std::stable_sort( tmp_input_slots.begin(), tmp_input_slots.end() ); + assert( tmp_input_slots == net.input_slots ); + + for ( auto i : net.input_slots ) + { + if ( i != (int)net.zero_input ) + { + tt[i] = input_tt[input_ind++]; + } + } + + auto shift = 0u; + for ( auto i = 0u; i < net.num_gates(); i++ ) + { + shift += ( net.nodes[i].size() - 1 ); + } + + const auto n_gates = net.num_gates(); + + for ( auto inv_config_itr = 0ul; inv_config_itr < ( 1ul << shift ); inv_config_itr++ ) + { + auto inv_config = inv_config_itr; + + for ( auto i = n_gates; i > 0; i-- ) + { + const auto& n = net.nodes[i - 1]; + + const auto n_fanin = n.size(); + const auto shift = n_fanin - 1; + const auto mask = ( 1 << shift ) - 1; + const auto ith_gate_config = ( inv_config & mask ); + + // only consider half the inverter configurations, the other half is covered by output inversion + if ( n_fanin == 3u ) + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + tt[n[2]] ); + } + else + { + tt[i - 1] = bitwise_majority( + ( ith_gate_config & 1 ) ? ~tt[n[0]] : tt[n[0]], + ( ith_gate_config & 2 ) ? ~tt[n[1]] : tt[n[1]], + ( ith_gate_config & 4 ) ? ~tt[n[2]] : tt[n[2]], + ( ith_gate_config & 8 ) ? ~tt[n[3]] : tt[n[3]], + tt[n[4]] ); + } + inv_config >>= shift; + } + + res.insert( tt[0] & 0xffff ); + } + + return res; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp b/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp new file mode 100644 index 0000000..adfa9b3 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/db_utils.hpp @@ -0,0 +1,278 @@ +/* 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 db_utils.hpp + \brief Utility functions for creating the DAGs, costs, and final AQFP databases + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "dag.hpp" +#include "dag_cost.hpp" +#include "dag_gen.hpp" +#include "db_builder.hpp" + +namespace mockturtle +{ + +inline void generate_aqfp_dags( const mockturtle::dag_generator_params& params, const std::string& file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector os; + for ( auto i = 0u; i < num_threads; i++ ) + { + auto file_path = fmt::format( "{}_{:02d}.txt", file_prefix, i ); + os.emplace_back( file_path ); + assert( os[i].is_open() ); + } + + auto gen = mockturtle::dag_generator( params, num_threads ); + + std::atomic count = 0u; + std::vector> counts_inp( 6u ); + + gen.for_each_dag( [&]( const auto& net, uint32_t thread_id ) { + counts_inp[net.input_slots.size()]++; + + os[thread_id] << fmt::format( "{}\n", net.encode_as_string() ); + + if ( (++count) % 100000 == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs generated {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } } ); + + for ( auto& file : os ) + { + file.close(); + } + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs generated {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); + + std::cerr << fmt::format( "Number of DAGs of different input counts: [3 -> {}, 4 -> {}, 5 -> {}]\n", counts_inp[3u], counts_inp[4u], counts_inp[5u] ); +} + +inline void compute_aqfp_dag_costs( const std::unordered_map& gate_costs, const std::unordered_map& splitters, + const std::string& dag_file_prefix, const std::string& cost_file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector threads; + + std::atomic count = 0u; + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + std::ifstream is( fmt::format( "{}_{:02d}.txt", dag_file_prefix, id ) ); + assert( is.is_open() ); + + std::ofstream os( fmt::format( "{}_{:02d}.txt", cost_file_prefix, id ) ); + assert( os.is_open() ); + mockturtle::dag_aqfp_cost_all_configs> cc( gate_costs, splitters ); + + std::string temp; + while ( getline( is, temp ) ) + { + if ( temp.length() > 0 ) + { + mockturtle::aqfp_dag<> net( temp ); + auto costs = cc( net ); + + os << costs.size() << std::endl; + for ( auto it = costs.begin(); it != costs.end(); it++ ) + { + os << fmt::format( "{:08x} {}\n", it->first, it->second ); + } + + if ( ( ++count ) % 100000u == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } + } + } + + is.close(); + os.close(); + }, + i ); + } + + for ( auto& t : threads ) + { + t.join(); + } + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); +} + +inline void generate_aqfp_db( const std::unordered_map& gate_costs, const std::unordered_map& splitters, + const std::string& dag_file_prefix, const std::string& cost_file_prefix, const std::string& db_file_prefix, uint32_t num_threads ) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + + std::vector threads; + + std::atomic count = 0u; + + for ( auto i = 0u; i < num_threads; i++ ) + { + threads.emplace_back( + [&]( auto id ) { + std::ifstream ds( fmt::format( "{}_{:02d}.txt", dag_file_prefix, id ) ); + std::ifstream cs( fmt::format( "{}_{:02d}.txt", cost_file_prefix, id ) ); + + mockturtle::aqfp_db_builder<> db( gate_costs, splitters ); + uint64_t local_count = 0u; + + std::string dag; + while ( std::getline( ds, dag ) ) + { + uint32_t num_configs; + cs >> num_configs; + + std::unordered_map configs; + + std::string config_str; + uint64_t config; + double cost; + + for ( auto j = 0u; j < num_configs; j++ ) + { + cs >> config_str; + cs >> cost; + config = std::stoul( config_str, 0, 16 ); + configs[config] = cost; + } + + mockturtle::aqfp_dag<> ntk( dag ); + if ( ntk.input_slots.size() < 5u || ( ntk.input_slots.size() == 5u && ntk.zero_input != 0 ) ) + { + db.update( ntk, configs ); + } + + if ( ( ++count ) % 10000 == 0u ) + { + auto t1 = std::chrono::high_resolution_clock::now(); + auto d1 = std::chrono::duration_cast( t1 - t0 ); + + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime so far in seconds {:9.3f}\n", count, d1.count() / 1000.0 ); + } + + if ( ( ++local_count ) % 10000 == 0u ) + { + db.remove_redundant(); + + std::ofstream os_tmp( fmt::format( "{}_{:02d}.txt", db_file_prefix, id ) ); + assert( os_tmp.is_open() ); + db.save_db_to_file( os_tmp ); + os_tmp.close(); + } + } + + db.remove_redundant(); + + std::ofstream os( fmt::format( "{}_{:02d}.txt", db_file_prefix, id ) ); + assert( os.is_open() ); + db.save_db_to_file( os ); + os.close(); + }, + i ); + } + + for ( auto& t : threads ) + { + t.join(); + } + + mockturtle::aqfp_db_builder<> db( gate_costs, splitters ); + for ( auto i = 0u; i < num_threads; i++ ) + { + std::ifstream is( fmt::format( "{}_{:02d}.txt", db_file_prefix, i ) ); + assert( is.is_open() ); + db.load_db( is ); + is.close(); + } + + db.remove_redundant(); + + std::ofstream os_final( fmt::format( "{}.txt", db_file_prefix ) ); + assert( os_final.is_open() ); + db.save_db_to_file( os_final ); + os_final.close(); + + std::ofstream os_final_init_list( fmt::format( "{}_as_initializer_list.txt", db_file_prefix ) ); + assert( os_final_init_list.is_open() ); + db.save_db_to_file( os_final_init_list, true ); + os_final_init_list.close(); + + auto t2 = std::chrono::high_resolution_clock::now(); + auto d2 = std::chrono::duration_cast( t2 - t0 ); + std::cerr << fmt::format( "Number of DAGs processed {:10d}\nTime elapsed in seconds {:9.3f}\n", count, d2.count() / 1000.0 ); +} + +inline void generate_aqfp_db( + const mockturtle::dag_generator_params& params, + const std::unordered_map& gate_costs, + const std::unordered_map& splitters, + const std::string& file_prefix, + uint32_t num_threads ) +{ + std::cerr << "Generating DAGs ...\n"; + auto dag_file_prefix = fmt::format( "{}_dags", file_prefix ); + generate_aqfp_dags( params, dag_file_prefix, num_threads ); + + std::cerr << "Computing costs ...\n"; + auto cost_file_prefix = fmt::format( "{}_costs", file_prefix ); + compute_aqfp_dag_costs( gate_costs, splitters, dag_file_prefix, cost_file_prefix, num_threads ); + + std::cerr << "Generating the database ...\n"; + auto db_file_prefix = fmt::format( "{}_db", file_prefix ); + generate_aqfp_db( gate_costs, splitters, dag_file_prefix, cost_file_prefix, db_file_prefix, num_threads ); + + std::cerr << "Generation completed!"; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp b/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp new file mode 100644 index 0000000..612fd96 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/npn_cache.hpp @@ -0,0 +1,89 @@ +/* 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 npn_cache.hpp + \brief Cached NPN class computation + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Cache for mapping an N-input truthtable to the corresponding NPN class and the associated NPN transformation. */ +class npn_cache +{ + using npn_info = std::tuple>; + +public: + npn_cache() : arr( 1ul << ( 1ul << 4u ) ), has( 1ul << ( 1ul << 4u ), false ) + { + } + + npn_info operator()( uint64_t tt, uint32_t num_inputs = 4u ) + { + if ( num_inputs == 4u ) + { + if ( has[tt] ) + { + return arr[tt]; + } + + kitty::dynamic_truth_table dtt( num_inputs ); + dtt._bits[0] = tt; + auto tmp = kitty::exact_npn_canonization( dtt ); + + has[tt] = true; + return ( arr[tt] = { std::get<0>( tmp )._bits[0] & 0xffff, std::get<1>( tmp ), std::get<2>( tmp ) } ); + } + + if ( cache.count( tt ) ) + { + return cache[tt]; + } + + kitty::dynamic_truth_table dtt( num_inputs ); + dtt._bits[0] = tt; + auto tmp = kitty::exact_npn_canonization( dtt ); + + return ( arr[tt] = { std::get<0>( tmp )._bits[0] & 0xffff, std::get<1>( tmp ), std::get<2>( tmp ) } ); + } + +private: + std::vector arr; + std::vector has; + + std::unordered_map cache; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp b/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp new file mode 100644 index 0000000..6c714ed --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/detail/partial_dag.hpp @@ -0,0 +1,243 @@ +/* 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 partial_dag.hpp + \brief AQFP partial DAG data structure + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include + +#include + +#include "dag.hpp" + +namespace mockturtle +{ + +/*! \brief A class for constructing DAG structures in an incremental manner. */ +template +struct aqfp_partial_dag : public aqfp_dag +{ + using node_type = NodeT; + + using aqfp_dag::nodes; + using aqfp_dag::input_slots; + using aqfp_dag::zero_input; + using aqfp_dag::num_gates; + + uint32_t num_levels = 0u; // current number of levels + + std::unordered_map num_gates_of_fanin; + std::vector node_num_fanin; // number of fanins of each node + std::vector last_layer_leaves; // remaining fanin slots in the last layer + std::vector other_leaves; // remaining fanin slots of the other layers + + aqfp_partial_dag( + const std::vector>& nodes = {}, + const std::vector& input_slots = {}, + node_type zero_input = {}, + uint32_t num_levels = 0u, + const std::unordered_map& num_gates_of_fanin = {}, + const std::vector& node_num_fanin = {}, + const std::vector& last_layer_leaves = {}, + const std::vector& other_leaves = {} ) + : aqfp_dag( nodes, input_slots, zero_input ), + num_levels( num_levels ), + num_gates_of_fanin( num_gates_of_fanin ), + node_num_fanin( node_num_fanin ), + last_layer_leaves( last_layer_leaves ), + other_leaves( other_leaves ) {} + + aqfp_partial_dag( const std::string& str ) + { + decode_from_string( str ); + } + + /*! \brief Decode a string representation of a DAG into a DAG. + * + * Format: ng ni zi k0 g0f0 g0f1 .. g0fk0 k1 g1f0 g1f1 .. g1fk1 .... + * ng := num gates, ni := num inputs, zi := zero input, ki := num fanin of i-th gate, gifj = j-th fanin of i-th gate + */ + void decode_from_string( const std::string& str ) + { + num_levels = 0; + + std::istringstream iss( str ); + auto ng = 0u; + auto ni = 0u; + auto zi = 0u; + + iss >> ng >> ni >> zi; + zero_input = zi; + + std::vector level( ng + ni, 0u ); + level[0u] = 1u; + for ( auto i = 0u; i < ng; i++ ) + { + auto nf = 0u; + iss >> nf; + + num_gates_of_fanin[nf]++; + node_num_fanin.push_back( nf ); + nodes.push_back( {} ); + + for ( auto j = 0u; j < nf; j++ ) + { + auto t = 0u; + iss >> t; + nodes[i].push_back( t ); + level[t] = std::max( level[t], level[i] + 1 ); + } + } + + num_levels = *std::max_element( level.begin(), level.end() ); + + for ( auto i = 0u; i < ni; i++ ) + { + nodes.push_back( {} ); + node_num_fanin.push_back( 0 ); + input_slots.push_back( nodes.size() - 1 ); + } + } + + /*! \brief Returns a map with maximum number of equal fanins that a gate may have without that gate being redundant. + * + * A 3-input majority gate may not have any equal fanins as it would simplify otherwise. + * A 5-input majoirty gate may have up to 2 equal fanins but if it had more, then it would simplify. + */ + std::vector max_equal_fanins() const + { + std::vector res( num_gates() ); + for ( auto i = 0u; i < num_gates(); i++ ) + { + res[i] = node_num_fanin[i] / 2; + } + return res; + } + + /*! \brief Add "fanin" as a fanin of "node". */ + void add_fanin( NodeT node, NodeT fanin ) + { + nodes[node].push_back( fanin ); + } + + /*! \brief Adds a new node with "num_fanin" fanins that is connected to "fanouts". + * + * Optionally, it can be specified as a last layer node if at least one of its fanouts previously belonged to + * the last layer. + */ + uint32_t add_internal_node( uint32_t num_fanin = 3u, const std::multiset& fanouts = {}, bool is_in_last_layer = true ) + { + uint32_t node_id = nodes.size(); + nodes.push_back( {} ); + + assert( node_id == node_num_fanin.size() ); + + num_gates_of_fanin[num_fanin]++; + node_num_fanin.push_back( num_fanin ); + + for ( auto&& fo : fanouts ) + { + add_fanin( fo, node_id ); + } + + if ( is_in_last_layer ) + { + for ( auto slot = 0u; slot < num_fanin; slot++ ) + { + last_layer_leaves.push_back( node_id ); + } + } + + return node_id; + } + + /*! \brief Adds a new input node connected to "fanouts". */ + uint32_t add_leaf_node( const std::multiset& fanouts = {} ) + { + auto input_slot = add_internal_node( 0u, fanouts, false ); + input_slots.push_back( input_slot ); + return input_slot; + } + + /*! \brief Make a copy with empty last_layer_leaves and other_leaves. */ + aqfp_partial_dag copy_without_leaves() const + { + aqfp_partial_dag res{ + nodes, + input_slots, + zero_input, + + num_levels, + + num_gates_of_fanin, + node_num_fanin, + {}, + {}, + }; + + return res; + } + + /*! \brief Make a copy with empty other_leaves. */ + aqfp_partial_dag copy_with_last_layer_leaves() const + { + aqfp_partial_dag res{ + nodes, + input_slots, + zero_input, + + num_levels, + + num_gates_of_fanin, + node_num_fanin, + last_layer_leaves, + {}, + }; + + return res; + } + + /*! \brief Create a aqfp_partial_dag with a single gate of a given number of fanins. */ + static aqfp_partial_dag get_root( uint32_t num_fanin ) + { + aqfp_partial_dag net; + + net.num_levels = 1u; + + std::multiset fanouts = {}; + net.add_internal_node( num_fanin, fanouts, true ); + + return net; + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp b/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp new file mode 100644 index 0000000..cb4ca49 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp @@ -0,0 +1,407 @@ +/* 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_algebraic_rewriting_splitters.hpp + \brief MIG algebraric rewriting with fanout size limitation + + \author Mathias Soeken + \author Eleonora Testa + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/stopwatch.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../mig_algebraic_rewriting.hpp" + +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class mig_algebraic_depth_rewriting_splitter_impl +{ +public: + mig_algebraic_depth_rewriting_splitter_impl( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps, mig_algebraic_depth_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + switch ( ps.strategy ) + { + case mig_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case mig_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case mig_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + mark_critical_paths(); + reduce_depth( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + if ( ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + auto index = 0; + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + + signal third; + if ( assoc ) + { + third = u; + } + else + third = x; + + auto h = ntk.create_maj( x, y, u ); + auto opt = ntk.create_maj( z, third, h ); + + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) // opt does not exists + { + if ( ( ntk.fanout_size( ntk.get_node( h ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( h ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( h ) ) == 2 ) ) // it would mean the depth is actually increased + { + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + } + + ntk.substitute_node( n, opt ); + ntk.update_levels(); + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto s1 = ntk.create_maj( ocs[0], ocs[1], ocs2[0] ); + + auto s2 = ntk.create_maj( ocs[0], ocs[1], ocs2[1] ); + if ( ntk.fanout_size( ntk.get_node( s2 ) ) < 1 ) + { + index = ntk.node_to_index( ntk.get_node( ocs[0] ) ); + if ( ( !ntk.is_pi( ntk.get_node( ocs[0] ) ) ) && ( index != 0 ) ) //&& ( ntk.is_on_critical_path( ntk.get_node( ocs[0] ) ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) < 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + } + + index = ntk.node_to_index( ntk.get_node( ocs[1] ) ); + if ( ( !ntk.is_pi( ntk.get_node( ocs[1] ) ) ) && ( index != 0 ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( ocs[1] ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) < 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + } + } + + auto opt = ntk.create_maj( ocs2[2], s1, s2 ); + + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s2 ) ); + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 17 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 2 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + if ( ntk.fanout_size( ntk.get_node( opt ) ) < 1 ) + ntk.take_out_node( ntk.get_node( opt ) ); + return true; + } + if ( ( ntk.fanout_size( ntk.get_node( opt ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( opt ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( opt ) ) == 1 ) ) // it would mean the depth is actually increased + { + if ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s1 ) ); + if ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) + ntk.take_out_node( ntk.get_node( s2 ) ); + return true; + } + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + mig_algebraic_depth_rewriting_params const& ps; + mig_algebraic_depth_rewriting_stats& st; +}; // namespace detail + +} // namespace detail + +/*! \brief Majority algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than MIGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void mig_algebraic_depth_rewriting_splitters( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps = {}, mig_algebraic_depth_rewriting_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_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + mig_algebraic_depth_rewriting_stats st; + detail::mig_algebraic_depth_rewriting_splitter_impl p( ntk, ps, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp b/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp new file mode 100644 index 0000000..5cf7224 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/mig_resub_splitters.hpp @@ -0,0 +1,581 @@ +/* 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_resub_splitters.hpp + \brief Modified MIG resubstitution to consider splitters in AQFP + + \author Heinz Riener + \author Eleonora Testa + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../networks/mig.hpp" +#include "../../utils/truth_table_utils.hpp" +#include "../resubstitution.hpp" + +#include + +namespace mockturtle +{ + +struct mig_resub_splitters_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for relevance resub */ + stopwatch<>::duration time_resubR{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted relevance resubsitutions */ + uint32_t num_divR_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: mig_resub_splitters_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] R-resub {:6d} ({:>5.2f} secs)\n", + num_divR_accepts, to_seconds( time_resubR ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} = {:6d} MAJ ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_divR_accepts + num_div1_accepts ) ); + } +}; /* mig_resub_splitters_stats */ + +template +struct mig_resub_splitters_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_resub_splitters_stats; + + struct unate_divisors + { + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + std::vector next_candidates; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + next_candidates.clear(); + } + }; + +public: + explicit mig_resub_splitters_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider relevance optimization */ + g = call_with_stopwatch( st.time_resubR, [&]() { + return resub_divR( root, required ); + } ); + if ( g ) + { + ++st.num_divR_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + if ( !ntk.is_pi( d ) ) + { + if ( ( ntk.fanout_size( d ) == 4 ) || ( ntk.fanout_size( d ) >= 16 ) || ( ntk.fanout_size( d ) == 1 ) ) // it would mean the depth is actually increased + continue; + auto fanout = ntk.fanout_size( root ); + if ( ( ntk.fanout_size( d ) < 4 ) && ( ntk.fanout_size( d ) + fanout > 4 ) ) + continue; + else if ( ( ntk.fanout_size( d ) < 16 ) && ( ntk.fanout_size( d ) > 4 ) && ( ntk.fanout_size( d ) + fanout > 16 ) ) + continue; + } + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + std::optional resub_divR( node const& root, uint32_t required ) + { + (void)required; + + std::vector fs; + ntk.foreach_fanin( root, [&]( const auto& f ) { + fs.emplace_back( f ); + } ); + + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const& d0 = divs.at( i ); + + if ( !ntk.is_pi( d0 ) ) + { + if ( ( ntk.fanout_size( d0 ) == 4 ) || ( ntk.fanout_size( d0 ) >= 16 ) || ( ntk.fanout_size( d0 ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + auto const& s = ntk.make_signal( d0 ); + auto const& tt = sim.get_tt( s ); + + if ( d0 == root ) + break; + + auto const tt0 = sim.get_tt( fs[0] ); + auto const tt1 = sim.get_tt( fs[1] ); + auto const tt2 = sim.get_tt( fs[2] ); + + if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ); + } + else if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( ~tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( ~tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( ~tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ); + } + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d0 = divs.at( i ); + if ( ntk.level( d0 ) > required - 1 ) + continue; + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const d1 = divs.at( j ); + if ( ntk.level( d1 ) > required - 1 ) + continue; + + auto const& tt_s0 = sim.get_tt( ntk.make_signal( d0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( d1 ) ); + + /* Boolean filtering rule for MAJ-3 */ + if ( kitty::ternary_majority( tt_s0, tt_s1, tt ) == tt ) + { + udivs.positive_divisors0.emplace_back( ntk.make_signal( d0 ) ); + udivs.positive_divisors1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt ) == tt ) + { + udivs.negative_divisors0.emplace_back( ntk.make_signal( d0 ) ); + udivs.negative_divisors1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d1 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d1 ) ); + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d0 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d0 ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors0.size(); ++i ) + { + auto const s0 = udivs.positive_divisors0.at( i ); + auto const s1 = udivs.positive_divisors1.at( i ); + + if ( !ntk.is_pi( ntk.get_node( s0 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s0 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + if ( !ntk.is_pi( ntk.get_node( s1 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + for ( auto j = i + 1; j < udivs.positive_divisors0.size(); ++j ) + { + auto s2 = udivs.positive_divisors0.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + { + continue; + } + else + return sim.get_phase( root ) ? !e : e; + } + + s2 = udivs.positive_divisors1.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + } + } + + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors0.size(); ++i ) + { + auto const s0 = udivs.negative_divisors0.at( i ); + if ( !ntk.is_pi( ntk.get_node( s0 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s0 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s0 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + auto const s1 = udivs.negative_divisors1.at( i ); + if ( !ntk.is_pi( ntk.get_node( s1 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s1 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s1 ) ) == 1 ) ) // it would mean the depth is actually increased + continue; + } + + for ( auto j = i + 1; j < udivs.negative_divisors0.size(); ++j ) + { + auto s2 = udivs.negative_divisors0.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( !a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + + s2 = udivs.negative_divisors1.at( j ); + if ( !ntk.is_pi( ntk.get_node( s2 ) ) ) + { + if ( ( ntk.fanout_size( ntk.get_node( s2 ) ) == 4 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) >= 16 ) || ( ntk.fanout_size( ntk.get_node( s2 ) ) == 1 ) ) + continue; + } + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ) == tt ) + { + // ++st.num_div1_maj_accepts; + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto e = ntk.create_maj( !a, b, c ); + if ( ( ntk.fanout_size( ntk.get_node( e ) ) == 2 ) || ( ntk.fanout_size( ntk.get_node( e ) ) == 5 ) || ( ntk.fanout_size( ntk.get_node( e ) ) > 16 ) ) + continue; + else + return sim.get_phase( root ) ? !e : e; + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; +}; /* mig_resub_functor */ + +template +bool substitute_and_update_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + ntk.substitute_node( n, g ); + ntk.update_levels(); + ntk.update_fanout(); + return true; +}; + +template +void mig_resubstitution_splitters( depth_view& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + resub_view_t resub_view{ ntk }; + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resub_splitters_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run( substitute_and_update_fn ); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resub_splitters_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run( substitute_and_update_fn ); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp b/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp new file mode 100644 index 0000000..859f681 --- /dev/null +++ b/include/mockturtle/algorithms/aqfp/optimal_buffer_insertion.hpp @@ -0,0 +1,563 @@ +/* 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 optimal_buffer_insertion.hpp + \brief Global-optimal buffer insertion for AQFP using SMT + + \author Siang-Yun (Sonia) Lee +*/ + +// NOTE: This file is included inside the class `mockturtle::buffer_insertion` +// It should not be included anywhere else. + +#pragma region Compute timeframe for SMT solving + /*! \brief Compute the earliest and latest possible timeframe by eager ASAP and ALAP */ + uint32_t compute_timeframe( uint32_t max_depth ) + { + // TODO: Consider max_depth % _ps.assume.num_phases == 0 constraint + _timeframes.reset( std::make_pair( 0, 0 ) ); + uint32_t min_depth{ 0 }; + + _ntk.incr_trav_id(); + _ntk.foreach_po( [&]( auto const& f ) { + auto const no = _ntk.get_node( f ); + auto clevel = compute_levels_ASAP_eager( no ) + ( _ntk.fanout_size( no ) > 1 ? 1 : 0 ); + min_depth = std::max( min_depth, clevel ); + } ); + + _ntk.incr_trav_id(); + _ntk.foreach_po( [&]( auto const& f ) { + const auto n = _ntk.get_node( f ); + if ( !_ntk.is_constant( n ) && _ntk.visited( n ) != _ntk.trav_id() ) + { + _timeframes[n].second = max_depth - ( _ntk.fanout_size( n ) > 1 ? 1 : 0 ); + compute_levels_ALAP_eager( n ); + } + } ); + + return min_depth; + } + + uint32_t compute_levels_ASAP_eager( node const& n ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + { + return _timeframes[n].first; + } + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.is_constant( n ) ) + { + return _timeframes[n].first = 0; + } + if ( _ntk.is_pi( n ) ) + { + return _timeframes[n].first = _ps.assume.ci_phases[0]; + } + + uint32_t level{ 0 }; + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + level = std::max( level, compute_levels_ASAP_eager( ni ) + ( _ntk.fanout_size( ni ) > 1 ? 1 : 0 ) ); + } + } ); + + return _timeframes[n].first = level + 1; + } + + void compute_levels_ALAP_eager( node const& n ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + + _ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto const ni = _ntk.get_node( fi ); + if ( !_ntk.is_constant( ni ) ) + { + if ( _ps.assume.balance_cios && _ntk.is_pi( ni ) ) + { + assert( _timeframes[n].second > _ps.assume.ci_phases[0] ); + _timeframes[ni].second = _ps.assume.ci_phases[0]; + } + else + { + assert( _timeframes[n].second > num_splitter_levels( ni ) ); + auto fi_level = _timeframes[n].second - ( _ntk.fanout_size( ni ) > 1 ? 2 : 1 ); + if ( _ntk.visited( ni ) != _ntk.trav_id() || _timeframes[ni].second > fi_level ) + { + _timeframes[ni].second = fi_level; + compute_levels_ALAP_eager( ni ); + } + } + } + } ); + } +#pragma + +#if __GNUC__ == 7 + +void optimize_with_smt( std::string name = "" ) +{} + +#else + +void optimize_with_smt( std::string name = "" ) +{ + std::ofstream os( "model_" + name + ".smt2", std::ofstream::out ); + dump_smt_model( os ); + os.close(); + std::string command = "z3 -v:1 model_" + name + ".smt2 &> sol_" + name + ".txt"; + std::system( command.c_str() ); + parse_z3_result( "sol_" + name + ".txt" ); + return; +} + +// ILP formulation +void dump_smt_model( std::ostream& os = std::cout ) +{ + os << "(set-logic QF_LIA)\n"; + /* hard assumptions to bound the number of variables */ + uint32_t const max_depth = depth(); // + 3; + uint32_t const max_relative_depth = max_depth; + count_buffers(); + uint32_t const upper_bound = num_buffers(); + uint32_t const min_depth = compute_timeframe( max_depth ); + + /* depth variable */ + if ( _ps.assume.balance_pos ) /* depth is a variable */ + { + os << fmt::format( "(declare-const depth Int)\n" ); + assert( min_depth <= max_depth ); + os << fmt::format( "(assert (<= depth {}))\n", max_depth ); + os << fmt::format( "(assert (>= depth {}))\n", min_depth ); + } + + /* declare variables for the level of each node */ + std::vector level_vars; + if ( _ps.assume.branch_pis && !_ps.assume.balance_pis ) + { + /* Only if we branch but not balance PIs, they are free variables as gates */ + _ntk.foreach_pi( [&]( auto const& n ) { + level_vars.emplace_back( fmt::format( "l{}", n ) ); + os << fmt::format( "(declare-const l{} Int)\n", n ); + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= l{} depth))\n", n ); + assert( _timeframes[n].second <= max_depth ); + os << fmt::format( "(assert (<= l{} {}))\n", n, _timeframes[n].second ); + os << fmt::format( "(assert (>= l{} 0))\n", n ); + } ); + } + + _ntk.foreach_gate( [&]( auto const& n ) { + level_vars.emplace_back( fmt::format( "l{}", n ) ); + os << fmt::format( "(declare-const l{} Int)\n", n ); + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= l{} depth))\n", n ); + assert( _timeframes[n].first >= 1 && _timeframes[n].second <= max_depth && _timeframes[n].first <= _timeframes[n].second ); + os << fmt::format( "(assert (>= l{} {}))\n", n, _timeframes[n].first ); + os << fmt::format( "(assert (<= l{} {}))\n", n, _timeframes[n].second ); + } ); + + /* constraints for each gate's fanout */ + std::vector bufs; + if ( _ps.assume.branch_pis ) + { + /* If PIs are balanced, they are always at level 0 so we don't have variables for them */ + if ( _ps.assume.balance_pis ) + { + _ntk.foreach_pi( [&]( auto const& n ) { + smt_constraints_balanced_pis( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + } + else + { + _ntk.foreach_pi( [&]( auto const& n ) { + smt_constraints( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + } + } + _ntk.foreach_gate( [&]( auto const& n ) { + smt_constraints( os, n, max_depth ); + bufs.emplace_back( fmt::format( "bufs{}", n ) ); + } ); + + os << "\n(declare-const total Int)\n"; + os << fmt::format( "(assert (= total (+ {})))\n", fmt::join( bufs, " " ) ); + os << fmt::format( "(assert (<= total {}))\n", upper_bound ); + os << "(minimize total)\n(check-sat)\n"; + if ( _ps.assume.balance_pos ) + os << "(get-value (total depth))\n"; + else + os << "(get-value (total))\n"; + os << fmt::format( "(get-value ({}))\n(exit)\n", fmt::join( level_vars, " " ) ); +} + +void smt_constraints_balanced_pis( std::ostream& os, node const& n, uint32_t const& max_depth ) +{ + os << fmt::format( "\n;constraints for node {}\n", n ); + os << fmt::format( "(declare-const bufs{} Int)\n", n ); + /* special cases */ + if ( _ntk.fanout_size( n ) == 0 ) /* dangling */ + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + return; + } + else if ( _ntk.fanout_size( n ) == 1 ) + { + /* single fanout: only sequencing constraint */ + if ( _external_ref_count[n] > 0 ) + { + if ( _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} depth))\n", n ); + } + else + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + } + } + else + { + node const& no = _fanouts[n].front().fanouts.front(); + os << fmt::format( "(assert (= bufs{} (- l{} 1)))\n", n, no ); + } + return; + } + else if ( _ntk.fanout_size( n ) <= _ps.assume.splitter_capacity ) + { + /* only one splitter is needed */ + + /* sequencing */ + std::vector fos; + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} 2))\n", no ); + fos.emplace_back( no ); + } ); + + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} depth))\n", n ); + } + else + { + if ( fos.size() == 0 ) /* not balance PO; have multiple PO refs */ + { + os << fmt::format( "(assert (= bufs{} 1))\n", n ); + } + else if ( fos.size() == 1 ) /* not balance PO; have one gate fanout and PO ref(s) */ + { + os << fmt::format( "(assert (= bufs{} (- l{} 1)))\n", n, fos[0] ); + } + else if ( fos.size() >= 2 ) + { + os << fmt::format( "(declare-const g{}maxfo Int)\n", n ); /* the max level of fanouts */ + + /* bound it -- just to hint the solver; should not matter if we have optimization objective */ + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= g{}maxfo depth))\n", n ); + else + os << fmt::format( "(assert (<= g{}maxfo {}))\n", n, max_depth ); + + /* taking the max (lower bounded by the max) */ + for ( auto const& no : fos ) + os << fmt::format( "(assert (>= g{}maxfo l{}))\n", n, no ); + + os << fmt::format( "(assert (= bufs{} (- g{}maxfo 1)))\n", n, n ); + } + } + return; + } + + /* sequencing & range constraints */ + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} 2))\n", no ); + } ); + + /* PO branching */ + if ( _external_ref_count[n] > 0 ) + { + os << fmt::format( "(assert (>= depth {}))\n", num_splitter_levels( n ) ); + } + + /* max possible relative depth */ + uint32_t highest_fanout = 0; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + highest_fanout = max_depth + 1; + } + else + { + foreach_fanout( n, [&]( auto const& no ) { + highest_fanout = std::max( highest_fanout, _timeframes[no].second ); + } ); + } + uint32_t max_relative_depth = highest_fanout; + + uint32_t l = max_relative_depth; + bool add_po_refs = false; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + add_po_refs = true; + os << fmt::format( "(assert (<= depth {}))\n", l - 1 ); + } + + /* initial case */ + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= l{} {}) 1 0)", no, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= depth {}) {} 0)", l - 1, _external_ref_count[n] ); + os << ")))\n"; + + /* general case */ + for ( --l; l > 0; --l ) + { + os << fmt::format( "(declare-const g{}b{} Int)\n", n, l ); // buffers at relative depth l + /* g{n}b{l} = ceil( g{n}e{l+1} / s_b ) --> s_b * ( g{n}b{l} - 1 ) < g{n}e{l+1} <= s_b * g{n}b{l} */ + os << fmt::format( "(assert (< (* {} (- g{}b{} 1)) g{}e{}))\n", _ps.assume.splitter_capacity, n, l, n, l + 1 ); + os << fmt::format( "(assert (<= g{}e{} (* {} g{}b{})))\n", n, l + 1, _ps.assume.splitter_capacity, n, l ); + + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= l{} {}) 1 0)", no, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= depth {}) {} 0)", l - 1, _external_ref_count[n] ); + os << fmt::format( " g{}b{})))\n", n, l ); + } + + /* end of loop */ + os << fmt::format( "(assert (= g{}e1 1))\n", n ); // legal + os << fmt::format( "(assert (= bufs{} (+", n ); + for ( l = max_relative_depth - 1; l > 0; --l ) + { + os << fmt::format( " g{}b{}", n, l ); + } + os << ")))\n"; +} + +void smt_constraints( std::ostream& os, node const& n, uint32_t const& max_depth ) +{ + os << fmt::format( "\n;constraints for node {}\n", n ); + os << fmt::format( "(declare-const bufs{} Int)\n", n ); + /* special cases */ + if ( _ntk.fanout_size( n ) == 0 ) /* dangling */ + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + return; + } + else if ( _ntk.fanout_size( n ) == 1 ) + { + /* single fanout: only sequencing constraint */ + if ( _external_ref_count[n] > 0 ) + { + if ( _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (= bufs{} (- depth l{})))\n", n, n ); + } + else + { + os << fmt::format( "(assert (= bufs{} 0))\n", n ); + } + } + else + { + node const& no = _fanouts[n].front().fanouts.front(); + os << fmt::format( "(assert (> l{} l{}))\n", no, n ); + os << fmt::format( "(assert (= bufs{} (- l{} l{} 1)))\n", n, no, n ); + } + return; + } + else if ( _ntk.fanout_size( n ) <= _ps.assume.splitter_capacity ) + { + /* only one splitter is needed */ + + /* sequencing */ + std::vector fos; + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (>= l{} (+ l{} 2)))\n", no, n ); + fos.emplace_back( no ); + } ); + + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (> depth l{}))\n", n ); + os << fmt::format( "(assert (= bufs{} (- depth l{})))\n", n, n ); + } + else + { + if ( fos.size() == 0 ) /* not balance PO; have multiple PO refs */ + { + os << fmt::format( "(assert (= bufs{} 1))\n", n ); + } + else if ( fos.size() == 1 ) /* not balance PO; have one gate fanout and PO ref(s) */ + { + os << fmt::format( "(assert (= bufs{} (- l{} l{} 1)))\n", n, fos[0], n ); + } + else if ( fos.size() >= 2 ) + { + os << fmt::format( "(declare-const g{}maxfo Int)\n", n ); /* the max level of fanouts */ + + /* bound it -- just to hint the solver; should not matter if we have optimization objective */ + if ( _ps.assume.balance_pos ) + os << fmt::format( "(assert (<= g{}maxfo depth))\n", n ); + else + os << fmt::format( "(assert (<= g{}maxfo {}))\n", n, max_depth ); + + /* taking the max (lower bounded by the max) */ + for ( auto const& no : fos ) + os << fmt::format( "(assert (>= g{}maxfo l{}))\n", n, no ); + + os << fmt::format( "(assert (= bufs{} (- g{}maxfo l{} 1)))\n", n, n, n ); + } + } + return; + } + + /* max possible relative depth */ + uint32_t highest_fanout = 0; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + highest_fanout = max_depth + 1; + } + else + { + foreach_fanout( n, [&]( auto const& no ) { + highest_fanout = std::max( highest_fanout, _timeframes[no].second ); + } ); + } + uint32_t max_relative_depth = highest_fanout - _timeframes[n].first; + + /* sequencing & range constraints */ + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( "(assert (<= l{} (+ l{} {})))\n", no, n, max_relative_depth ); + os << fmt::format( "(assert (>= l{} (+ l{} 2)))\n", no, n ); + } ); + + /* PO branching */ + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + os << fmt::format( "(assert (>= depth (+ l{} {})))\n", n, num_splitter_levels( n ) ); + } + + uint32_t l = max_relative_depth; + bool add_po_refs = false; + if ( _external_ref_count[n] > 0 && _ps.assume.balance_pos ) + { + add_po_refs = true; + os << fmt::format( "(assert (<= depth (+ l{} {})))\n", n, l - 1 ); + } + + /* initial case */ + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= (- l{} l{}) {}) 1 0)", no, n, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= (+ l{} {}) depth) {} 0)", n, l - 1, _external_ref_count[n] ); + os << ")))\n"; + + /* general case */ + for ( --l; l > 0; --l ) + { + os << fmt::format( "(declare-const g{}b{} Int)\n", n, l ); // buffers at relative depth l + /* g{n}b{l} = ceil( g{n}e{l+1} / s_b ) --> s_b * ( g{n}b{l} - 1 ) < g{n}e{l+1} <= s_b * g{n}b{l} */ + os << fmt::format( "(assert (< (* {} (- g{}b{} 1)) g{}e{}))\n", _ps.assume.splitter_capacity, n, l, n, l + 1 ); + os << fmt::format( "(assert (<= g{}e{} (* {} g{}b{})))\n", n, l + 1, _ps.assume.splitter_capacity, n, l ); + + os << fmt::format( "(declare-const g{}e{} Int)\n", n, l ); // edges at relative depth l + os << fmt::format( "(assert (= g{}e{} (+", n, l ); + foreach_fanout( n, [&]( auto const& no ) { + os << fmt::format( " (ite (= (- l{} l{}) {}) 1 0)", no, n, l ); + } ); + if ( add_po_refs ) + os << fmt::format( " (ite (= (+ l{} {}) depth) {} 0)", n, l - 1, _external_ref_count[n] ); + os << fmt::format( " g{}b{})))\n", n, l ); + } + + /* end of loop */ + os << fmt::format( "(assert (= g{}e1 1))\n", n ); // legal + os << fmt::format( "(assert (= bufs{} (+", n ); + for ( l = max_relative_depth - 1; l > 0; --l ) + { + os << fmt::format( " g{}b{}", n, l ); + } + os << ")))\n"; +} + +template +void foreach_fanout( node const& n, Fn&& fn ) +{ + for ( auto it1 = _fanouts[n].begin(); it1 != _fanouts[n].end(); ++it1 ) + { + for ( auto it2 = it1->fanouts.begin(); it2 != it1->fanouts.end(); ++it2 ) + { + fn( *it2 ); + } + } +} + +void parse_z3_result( std::string filename ) +{ + std::ifstream fin( filename, std::ifstream::in ); + assert( fin.is_open() ); + + /* parsing */ + std::string line; + do + { + std::getline( fin, line ); /* first line: "sat" */ + } while ( line != "sat" ); + assert( line == "sat" ); + std::getline( fin, line ); /* second line: "((total <>)" */ + uint32_t total = std::stoi( line.substr( 8, line.find_first_of( ')' ) - 8 ) ); + std::cout << "[i] total = " << total; + if ( _ps.assume.balance_pos ) + { + std::getline( fin, line ); /* third line: " (depth <>))" */ + uint32_t depth = std::stoi( line.substr( 8, line.find_first_of( ')' ) - 8 ) ); + std::cout << ", depth = " << depth; + } + std::cout << "\n"; + + while ( std::getline( fin, line ) ) /* remaining lines: "((l<> <>)" or " (l<> <>)" or " (l<> <>))" */ + { + line = line.substr( line.find( 'l' ) + 1 ); + uint32_t n = std::stoi( line.substr( 0, line.find( ' ' ) ) ); + line = line.substr( line.find( ' ' ) + 1 ); + _levels[n] = std::stoi( line.substr( 0, line.find_first_of( ')' ) ) ); + } + adjust_depth(); +} + +#endif \ No newline at end of file diff --git a/include/mockturtle/algorithms/balancing.hpp b/include/mockturtle/algorithms/balancing.hpp new file mode 100644 index 0000000..0ba4c92 --- /dev/null +++ b/include/mockturtle/algorithms/balancing.hpp @@ -0,0 +1,446 @@ +/* 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 balancing.hpp + \brief Cut-based depth-optimization + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/depth_view.hpp" +#include "../views/mapping_view.hpp" +#include "../views/topo_view.hpp" +#include "balancing/esop_balancing.hpp" +#include "balancing/sop_balancing.hpp" +#include "balancing/utils.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "lut_mapper.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for balancing. + */ +struct balancing_params +{ + /*! \brief Cut enumeration params. */ + cut_enumeration_params cut_enumeration_ps; + + /*! \brief Optimize only on critical path. */ + bool only_on_critical_path{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for balancing. + */ +struct balancing_stats +{ + /*! \brief Total run-time. */ + stopwatch<>::duration time_total{}; + + /*! \brief Cut enumeration run-time. */ + cut_enumeration_stats cut_enumeration_st; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] Cut enumeration stats\n" ); + cut_enumeration_st.report(); + } +}; + +namespace detail +{ + +template +struct balancing_impl +{ + balancing_impl( Ntk const& ntk, rebalancing_function_t const& rebalancing_fn, balancing_params const& ps, balancing_stats& st ) + : ntk_( ntk ), + rebalancing_fn_( rebalancing_fn ), + ps_( ps ), + st_( st ) + { + } + + Ntk run() + { + Ntk dest; + node_map, Ntk> old_to_new( ntk_ ); + + /* input arrival times and mapping */ + old_to_new[ntk_.get_constant( false )] = { dest.get_constant( false ), 0u }; + if ( ntk_.get_node( ntk_.get_constant( false ) ) != ntk_.get_node( ntk_.get_constant( true ) ) ) + { + old_to_new[ntk_.get_constant( true )] = { dest.get_constant( true ), 0u }; + } + ntk_.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = { dest.create_pi(), 0u }; + } ); + + std::shared_ptr> depth_ntk; + if ( ps_.only_on_critical_path ) + { + depth_ntk = std::make_shared>( ntk_ ); + } + + stopwatch<> t( st_.time_total ); + const auto cuts = cut_enumeration( ntk_, ps_.cut_enumeration_ps, &st_.cut_enumeration_st ); + + uint32_t current_level{}; + const auto size = ntk_.size(); + progress_bar pbar{ ntk_.size(), "balancing |{0}| node = {1:>4} / " + std::to_string( size ) + " current level = {2}", ps_.progress }; + topo_view{ ntk_ }.foreach_node( [&]( auto const& n, auto index ) { + pbar( index, index, current_level ); + + if ( ntk_.is_constant( n ) || ntk_.is_pi( n ) ) + { + return; + } + + if ( ps_.only_on_critical_path && !depth_ntk->is_on_critical_path( n ) ) + { + std::vector> children; + ntk_.foreach_fanin( n, [&]( auto const& f ) { + const auto f_best = old_to_new[f].f; + children.push_back( ntk_.is_complemented( f ) ? dest.create_not( f_best ) : f_best ); + } ); + old_to_new[n] = { dest.clone_node( ntk_, n, children ), depth_ntk->level( n ) }; + return; + } + + arrival_time_pair best{ {}, std::numeric_limits::max() }; + uint32_t best_size{}; + for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) + { + if ( cut->size() == 1u || kitty::is_const0( cuts.truth_table( *cut ) ) ) + { + continue; + } + + std::vector> arrival_times( cut->size() ); + std::transform( cut->begin(), cut->end(), arrival_times.begin(), [&]( auto leaf ) { return old_to_new[ntk_.index_to_node( leaf )]; } ); + + rebalancing_fn_( dest, cuts.truth_table( *cut ), arrival_times, best.level, best_size, [&]( arrival_time_pair const& cand, uint32_t cand_size ) { + if ( cand.level < best.level || ( cand.level == best.level && cand_size < best_size ) ) + { + best = cand; + best_size = cand_size; + } + } ); + } + old_to_new[n] = best; + current_level = std::max( current_level, best.level ); + } ); + + ntk_.foreach_po( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_po( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + + return cleanup_dangling( dest ); + } + +private: + Ntk const& ntk_; + rebalancing_function_t const& rebalancing_fn_; + balancing_params const& ps_; + balancing_stats& st_; +}; + +template +struct balancing_decomp_impl +{ + balancing_decomp_impl( mapping_view const& ntk, rebalancing_function_t const& rebalancing_fn ) + : ntk_( ntk ), + rebalancing_fn_( rebalancing_fn ) + { + } + + Ntk run() + { + Ntk dest; + node_map, Ntk> old_to_new( ntk_ ); + + /* input arrival times and mapping */ + old_to_new[ntk_.get_constant( false )] = { dest.get_constant( false ), 0u }; + if ( ntk_.get_node( ntk_.get_constant( false ) ) != ntk_.get_node( ntk_.get_constant( true ) ) ) + { + old_to_new[ntk_.get_constant( true )] = { dest.get_constant( true ), 0u }; + } + + ntk_.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = { dest.create_pi(), 0u }; + } ); + + if constexpr ( has_foreach_ro_v ) + { + ntk_.foreach_ro( [&]( auto const& n ) { + old_to_new[n] = { dest.create_ro(), 0u }; + } ); + } + + topo_view{ ntk_ }.foreach_node( [&]( auto const& n, auto index ) { + if ( ntk_.is_constant( n ) || ntk_.is_ci( n ) ) + { + return; + } + + if ( !ntk_.is_cell_root( n ) ) + { + return; + } + + std::vector> arrival_times; + ntk_.foreach_cell_fanin( n, [&]( auto const& leaf, uint32_t ctr ) { + arrival_times.push_back( old_to_new[ntk_.index_to_node( leaf )] ); + } ); + + kitty::dynamic_truth_table tt = ntk_.cell_function( n ); + + rebalancing_fn_( dest, tt, arrival_times, UINT32_MAX, UINT32_MAX, [&]( arrival_time_pair cand, uint32_t cand_size ) { + (void)cand_size; + old_to_new[n] = cand; + } ); + } ); + + ntk_.foreach_po( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_po( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk_.foreach_ri( [&]( auto const& f ) { + const auto s = old_to_new[f].f; + dest.create_ri( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); + } ); + } + + return dest; + } + +private: + mapping_view const& ntk_; + rebalancing_function_t const& rebalancing_fn_; +}; + +} // namespace detail + +/*! Balancing of a logic network + * + * This function implements a dynamic-programming and cut-enumeration based + * balancing algorithm. It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + * The template parameter `CostFn` is only used to compute the critical paths, + * when the `only_on_critical_path` parameter is assigned true. Note that the + * size for rewriting candidates is computed by the rebalancing function and + * may not correspond to the cost given by CostFn. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const auto aig = ...; + + sop_rebalancing balance_fn; + balancing_params ps; + ps.cut_enumeration_ps.cut_size = 6u; + const auto balanced_aig = balancing( aig, {balance_fn}, ps ); + \endverbatim + */ +template> +Ntk balancing( Ntk const& ntk, rebalancing_function_t const& rebalancing_fn = {}, balancing_params const& ps = {}, balancing_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + balancing_stats st; + const auto dest = detail::balancing_impl{ ntk, rebalancing_fn, ps, st }.run(); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +/*! \brief SOP balancing of a logic network + * + * This function implements an LUT-based SOP balancing algorithm. + * It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + */ +template +Ntk sop_balancing( Ntk const& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + lut_map_params mps; + mps = ps; + mps.sop_balancing = true; + mps.esop_balancing = false; + mps.verbose = false; + lut_map_stats st; + + /* perform SOP-driven mapping */ + mapping_view map_ntk{ ntk }; + lut_map_inplace( map_ntk, mps, &st ); + + /* decompose mapping */ + sop_rebalancing balance_fn; + balance_fn.both_phases_ = true; + const auto dest = call_with_stopwatch( st.time_total, [&]() { + return detail::balancing_decomp_impl{ map_ntk, balance_fn }.run(); + } ); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +/*! \brief ESOP balancing of a logic network + * + * This function implements an LUT-based ESOP balancing algorithm. + * It returns a new network of the same type and performs + * generic balancing by providing a rebalancing function. + * + */ +template +Ntk esop_balancing( Ntk const& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + + lut_map_params mps; + mps = ps; + mps.esop_balancing = true; + mps.sop_balancing = false; + mps.verbose = false; + lut_map_stats st; + + /* perform ESOP-driven mapping */ + mapping_view map_ntk{ ntk }; + lut_map_inplace( map_ntk, mps, &st ); + + /* decompose mapping */ + esop_rebalancing balance_fn; + balance_fn.both_phases = true; + const auto dest = call_with_stopwatch( st.time_total, [&]() { + return detail::balancing_decomp_impl{ map_ntk, balance_fn }.run(); + } ); + + if ( pst ) + { + *pst = st; + } + if ( ps.verbose ) + { + st.report(); + } + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/balancing/esop_balancing.hpp b/include/mockturtle/algorithms/balancing/esop_balancing.hpp new file mode 100644 index 0000000..af717c8 --- /dev/null +++ b/include/mockturtle/algorithms/balancing/esop_balancing.hpp @@ -0,0 +1,305 @@ +/* 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 esop_balancing.hpp + \brief ESOP-based balancing engine for `balancing` algorithm + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../balancing.hpp" +#include "../exorcism.hpp" +#include "utils.hpp" + +namespace mockturtle +{ + +template +struct esop_rebalancing +{ + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, std::vector> const& inputs, uint32_t best_level, uint32_t best_cost, rebalancing_function_callback_t const& callback ) const + { + bool inverted = false; + auto [and_terms, max_level, num_and_gates] = create_function( dest, function, inputs, inverted ); + + /* Try with MUX decomposition */ + if ( mux_optimization ) + { + const auto index = std::distance( inputs.begin(), std::max_element( inputs.begin(), inputs.end(), []( auto const& a1, auto const& a2 ) { return a1.level < a2.level; } ) ); + + bool inverted0 = false, inverted1 = false; + auto [and_terms0, max_level0, num_and_gates0] = create_function( dest, kitty::cofactor0( function, static_cast( index ) ), inputs, inverted0 ); + auto [and_terms1, max_level1, num_and_gates1] = create_function( dest, kitty::cofactor1( function, static_cast( index ) ), inputs, inverted1 ); + + const auto max_level_mux = std::max( max_level0, max_level1 ) + 1u; + + if ( max_level_mux < max_level && max_level_mux < best_level ) + { + callback( { dest.create_ite( inputs[index].f, + balanced_xor_tree( dest, and_terms1 ).f ^ inverted1, + balanced_xor_tree( dest, and_terms0 ).f ^ inverted0 ), + max_level_mux }, + num_and_gates0 + num_and_gates1 + 1u ); + return; + } + } + + arrival_time_pair cand = balanced_xor_tree( dest, and_terms ); + if ( cand.level < best_level || ( cand.level == best_level && num_and_gates < best_cost ) ) + { + cand.f = cand.f ^ inverted; + callback( cand, num_and_gates ); + } + } + +private: + std::tuple, uint32_t, uint32_t> create_function( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + if ( spp_optimization ) + { + return create_function_from_spp( dest, func, arrival_times, inverted ); + } + else + { + return create_function_from_esop( dest, func, arrival_times, inverted ); + } + } + + std::tuple, uint32_t, uint32_t> create_function_from_esop( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto esop = create_sop_form( func, inverted ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t max_level{}; + uint32_t num_and_gates{}; + for ( auto const& cube : esop ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + if ( product_queue.size() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + arrival_time_pair and_res = balanced_and_tree( dest, product_queue ); + and_terms.push( and_res ); + max_level = std::max( max_level, and_res.level ); + } + return { and_terms, max_level, num_and_gates }; + } + + std::tuple, uint32_t, uint32_t> create_function_from_spp( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto esop = create_sop_form( func, inverted ); + const auto [spp, sums] = kitty::simple_spp( esop, func.num_vars() ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t max_level{}; + uint32_t num_and_gates{}; + for ( auto const& cube : spp ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + for ( auto i = 0u; i < sums.size(); ++i ) + { + if ( cube.get_mask( func.num_vars() + i ) ) + { + std::vector> xor_terms; + uint32_t xor_level{}; + for ( auto j = 0u; j < func.num_vars(); ++j ) + { + if ( ( sums[i] >> j ) & 1 ) + { + const auto [f, l] = arrival_times[j]; + xor_terms.push_back( f ); + xor_level = std::max( xor_level, l ); + } + } + const auto f = dest.create_nary_xor( xor_terms ); + product_queue.push( { cube.get_bit( func.num_vars() + i ) ? f : dest.create_not( f ), xor_level } ); + } + } + if ( product_queue.size() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + arrival_time_pair and_res = balanced_and_tree( dest, product_queue ); + and_terms.push( and_res ); + max_level = std::max( max_level, and_res.level ); + } + return { and_terms, max_level, num_and_gates }; + } + + arrival_time_pair balanced_and_tree( Ntk& dest, arrival_time_queue& queue ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = dest.create_and( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + arrival_time_pair balanced_xor_tree( Ntk& dest, arrival_time_queue& queue ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = dest.create_xor( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + std::vector create_sop_form( kitty::dynamic_truth_table const& func, bool& inverted ) const + { + stopwatch<> t( time_sop ); + inverted = false; + + if ( auto it = sop_hash_.find( func ); it != sop_hash_.end() ) + { + sop_cache_hits++; + return it->second; + } + + if ( both_phases ) + { + if ( auto it = sop_hash_.find( ~func ); it != sop_hash_.end() ) + { + inverted = true; + sop_cache_hits++; + return it->second; + } + } + + sop_cache_misses++; + std::vector sop = mockturtle::exorcism( func ); + + if ( both_phases ) + { + std::vector n_sop = mockturtle::exorcism( ~func ); + + if ( n_sop.size() < sop.size() ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + } + } + + return sop_hash_[func] = sop; + } + +private: + mutable std::unordered_map, kitty::hash> sop_hash_; + +public: + bool both_phases{ false }; + bool spp_optimization{ false }; + bool mux_optimization{ false }; + +public: + mutable uint32_t sop_cache_hits{}; + mutable uint32_t sop_cache_misses{}; + + mutable stopwatch<>::duration time_sop{}; + mutable stopwatch<>::duration time_tree_balancing{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/balancing/sop_balancing.hpp b/include/mockturtle/algorithms/balancing/sop_balancing.hpp new file mode 100644 index 0000000..0b808ea --- /dev/null +++ b/include/mockturtle/algorithms/balancing/sop_balancing.hpp @@ -0,0 +1,201 @@ +/* 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 sop_balancing.hpp + \brief SOP-based balancing engine for `balancing` algorithm + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../balancing.hpp" +#include "utils.hpp" + +namespace mockturtle +{ + +/*! \brief SOP rebalancing function + * + * This class can be used together with the generic `balancing` function. It + * converts each cut function into an SOP and then performs weight-oriented + * tree balancing on the AND terms and the outer OR function. + */ +template +struct sop_rebalancing +{ + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, std::vector> const& inputs, uint32_t best_level, uint32_t best_cost, rebalancing_function_callback_t const& callback ) const + { + bool inverted = false; + auto [and_terms, num_and_gates] = create_function( dest, function, inputs, inverted ); + const auto num_gates = num_and_gates + ( and_terms.empty() ? 0u : static_cast( and_terms.size() ) - 1u ); + arrival_time_pair cand = balanced_tree( dest, and_terms, false ); + + if ( cand.level < best_level || ( cand.level == best_level && num_gates < best_cost ) ) + { + cand.f = cand.f ^ inverted; + callback( cand, num_gates ); + } + } + +private: + std::pair, uint32_t> create_function( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times, bool& inverted ) const + { + const auto sop = create_sop_form( func, inverted ); + + stopwatch<> t_tree( time_tree_balancing ); + arrival_time_queue and_terms; + uint32_t num_and_gates{}; + for ( auto const& cube : sop ) + { + arrival_time_queue product_queue; + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( cube.get_mask( i ) ) + { + const auto [f, l] = arrival_times[i]; + product_queue.push( { cube.get_bit( i ) ? f : dest.create_not( f ), l } ); + } + } + if ( !product_queue.empty() ) + { + num_and_gates += static_cast( product_queue.size() ) - 1u; + } + and_terms.push( balanced_tree( dest, product_queue ) ); + } + return { and_terms, num_and_gates }; + } + + arrival_time_pair balanced_tree( Ntk& dest, arrival_time_queue& queue, bool _and = true ) const + { + if ( queue.empty() ) + { + return { dest.get_constant( true ), 0u }; + } + + while ( queue.size() > 1u ) + { + auto [s1, l1] = queue.top(); + queue.pop(); + auto [s2, l2] = queue.top(); + queue.pop(); + const auto s = _and ? dest.create_and( s1, s2 ) : dest.create_or( s1, s2 ); + const auto l = std::max( l1, l2 ) + 1; + queue.push( { s, l } ); + } + return queue.top(); + } + + std::vector create_sop_form( kitty::dynamic_truth_table const& func, bool& inverted ) const + { + stopwatch<> t( time_sop ); + inverted = false; + + if ( auto it = sop_hash_.find( func ); it != sop_hash_.end() ) + { + sop_cache_hits++; + return it->second; + } + + if ( both_phases_ ) + { + if ( auto it = sop_hash_.find( ~func ); it != sop_hash_.end() ) + { + inverted = true; + sop_cache_hits++; + return it->second; + } + } + + sop_cache_misses++; + std::vector sop = kitty::isop( func ); + + if ( both_phases_ ) + { + std::vector n_sop = kitty::isop( ~func ); + + if ( n_sop.size() < sop.size() ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + inverted = true; + return sop_hash_[~func] = n_sop; + } + } + } + + return sop_hash_[func] = sop; + } + +private: + mutable std::unordered_map, kitty::hash> sop_hash_; + +public: + bool both_phases_{ false }; + +public: + mutable uint32_t sop_cache_hits{}; + mutable uint32_t sop_cache_misses{}; + + mutable stopwatch<>::duration time_sop{}; + mutable stopwatch<>::duration time_tree_balancing{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/balancing/utils.hpp b/include/mockturtle/algorithms/balancing/utils.hpp new file mode 100644 index 0000000..57b569f --- /dev/null +++ b/include/mockturtle/algorithms/balancing/utils.hpp @@ -0,0 +1,80 @@ +/* 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 utils.hpp + \brief Balancing data types + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../../traits.hpp" + +namespace mockturtle +{ + +template +struct arrival_time_pair +{ + signal f; + uint32_t level; +}; + +/*! \brief Callback function for `rebalancing_function_t`. + * + * This callback is used in the rebalancing function to announce a new candidate that + * could be used for replacement in the main balancing algorithm. Using a callback + * makes it possible to account for situations in which none, a single, or multiple + * candidates are generated. + * + * The callback returns a pair composed of the output signal of the replacement + * candidate and the level of the new candidate. Ideally, the rebalancing function + * should not call the callback with candidates that a worse level. + */ +template +using rebalancing_function_callback_t = std::function const&, uint32_t )>; + +template +using rebalancing_function_t = std::function> const&, uint32_t, uint32_t, rebalancing_function_callback_t const& )>; + +template +struct arrival_time_compare +{ + bool operator()( arrival_time_pair const& p1, arrival_time_pair const& p2 ) const + { + return p1.level > p2.level; + } +}; + +template +using arrival_time_queue = std::priority_queue, std::vector>, arrival_time_compare>; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/bi_decomposition.hpp b/include/mockturtle/algorithms/bi_decomposition.hpp new file mode 100644 index 0000000..2e5b512 --- /dev/null +++ b/include/mockturtle/algorithms/bi_decomposition.hpp @@ -0,0 +1,171 @@ +/* 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 bi_decomposition.hpp + \brief BI decomposition + + \author Eleonora Testa + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class bi_decomposition_impl +{ +public: + bi_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& dc, std::vector> const& children ) + : _ntk( ntk ), + remainder( func ), + dc_remainder( dc ), + pis( children ) {} + + signal run() + { + /* bi_decomposition */ + if ( kitty::is_const0( binary_and( remainder, dc_remainder ) ) ) + { + return _ntk.get_constant( false ); + } + else if ( kitty::is_const0( binary_and( ~remainder, dc_remainder ) ) ) + { + return _ntk.get_constant( true ); + } + else + { + for ( auto h = 0u; h < remainder.num_vars(); h++ ) + { + auto var = remainder.construct(); + kitty::create_nth_var( var, h ); + if ( binary_and( remainder, dc_remainder ) == binary_and( var, dc_remainder ) ) + { + return pis[h]; + } + else if ( binary_and( remainder, dc_remainder ) == binary_and( ~var, dc_remainder ) ) + { + return _ntk.create_not( pis[h] ); + } + } + } + + auto bi_dec = kitty::is_bi_decomposable_mc( remainder, dc_remainder ); + auto res = std::get<1>( bi_dec ); + + remainder = std::get<2>( bi_dec )[0]; + dc_remainder = std::get<2>( bi_dec )[1]; + const auto right = run(); + + remainder = std::get<2>( bi_dec )[2]; + dc_remainder = std::get<2>( bi_dec )[3]; + const auto left = run(); + + if ( res == kitty::bi_decomposition::and_ ) + { + return _ntk.create_and( left, right ); + } + else if ( res == kitty::bi_decomposition::or_ ) + { + return _ntk.create_or( left, right ); + } + else if ( res == kitty::bi_decomposition::weak_and_ ) + { + return _ntk.create_and( left, right ); + } + else if ( res == kitty::bi_decomposition::weak_or_ ) + { + return _ntk.create_or( left, right ); + } + else if ( res == kitty::bi_decomposition::xor_ ) + { + return _ntk.create_xor( left, right ); + } + else + { + assert( false ); + return signal(); + } + } + +private: + Ntk& _ntk; + kitty::dynamic_truth_table remainder; + kitty::dynamic_truth_table dc_remainder; + std::vector> pis; +}; + +} // namespace detail + +/*! \brief Bi decomposition + * + * This function applies bi-decomposition on a truth table inside the network. + * + * Note that the number of variables in `func` and `care` must be the same. + * The function will create a network composed on two-input gates with as many primary inputs as the number of + * variables in `func` and a single output. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_xor` + * - `create_not` + * + * \param func Function as truth table + * \param care Care set of the function (as truth table) + * \return An internal signal of the network + */ + +template +signal bi_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, kitty::dynamic_truth_table const& care, std::vector> const& children ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + + detail::bi_decomposition_impl impl( ntk, func, care, children ); + return impl.run(); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cell_window.hpp b/include/mockturtle/algorithms/cell_window.hpp new file mode 100644 index 0000000..6e855ab --- /dev/null +++ b/include/mockturtle/algorithms/cell_window.hpp @@ -0,0 +1,509 @@ +/* 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 cell_window.hpp + \brief Windowing in mapped network + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" + +template<> +struct std::hash> +{ + auto operator()( std::array const& key ) const + { + std::hash hasher; + return hasher( key[0] ) * 31 + hasher( key[1] ); + } +}; + +namespace mockturtle +{ + +namespace detail +{ + +template +class cell_window_storage +{ +public: + cell_window_storage( Ntk const& ntk ) : _cell_refs( ntk ), + _cell_parents( ntk ) + { + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + _num_constants++; + } + + _nodes.reserve( _max_gates >> 1 ); + _gates.reserve( _max_gates ); + } + + phmap::flat_hash_set> _nodes; /* cell roots in current window */ + phmap::flat_hash_set> _gates; /* gates in current window */ + phmap::flat_hash_set> _leaves; /* leaves of current window */ + phmap::flat_hash_set> _roots; /* roots of current window */ + + std::array _window_mask; + phmap::flat_hash_set> _window_hash; + + node_map _cell_refs; /* ref counts for cells */ + node_map>, Ntk> _cell_parents; /* parent cells */ + + std::vector> _index_to_node; + phmap::flat_hash_map, uint32_t> _node_to_index; + + uint32_t _num_constants{ 1u }; + uint32_t _max_gates{}; + bool _has_mapping{ true }; +}; + +} // namespace detail + +template +class cell_window : public Ntk +{ +public: + using storage = typename std::shared_ptr>; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + cell_window( Ntk const& ntk, uint32_t max_gates = 64 ) + : Ntk( ntk ), + _storage( std::make_shared>( ntk ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_cell_root_v, "Ntk does not implement the is_cell_root method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_cell_fanin_v, "Ntk does not implement the foreach_cell_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + + assert( Ntk::has_mapping() ); + _storage->_max_gates = max_gates; + } + + bool compute_window_for( node const& pivot ) + { + init_cell_refs(); + + // print_time<> pt; + assert( Ntk::is_cell_root( pivot ) ); + + // reset old window + _storage->_nodes.clear(); + _storage->_gates.clear(); + + std::vector gates; + gates.reserve( _storage->_max_gates ); + collect_mffc( pivot, gates ); + add_node( pivot, gates ); + + if ( gates.size() > _storage->_max_gates ) + { + assert( false ); + } + + std::optional next; + while ( ( next = find_next_pivot() ) ) + { + gates.clear(); + collect_mffc( *next, gates ); + + if ( _storage->_gates.size() + gates.size() > _storage->_max_gates ) + { + break; + } + add_node( *next, gates ); + } + + find_leaves_and_roots(); + set_indexes(); + + return _storage->_window_hash.insert( _storage->_window_mask ).second; + } + + uint32_t num_pis() const + { + return _storage->_leaves.size(); + } + + uint32_t num_pos() const + { + return _storage->_roots.size(); + } + + uint32_t num_gates() const + { + return _storage->_gates.size(); + } + + uint32_t num_cells() const + { + return _storage->_nodes.size(); + } + + uint32_t size() const + { + return _storage->_num_constants + _storage->_leaves.size() + _storage->_gates.size(); + } + + bool is_pi( node const& n ) const + { + return _storage->_leaves.count( n ); + } + + bool is_cell_root( node const& n ) const + { + return _storage->_nodes.count( n ); + } + + uint32_t node_to_index( node const& n ) const + { + return _storage->_node_to_index.at( n ); + } + + node index_to_node( uint32_t index ) const + { + return _storage->_index_to_node[index]; + } + + bool has_mapping() const + { + return _storage->_has_mapping; + } + + void clear_mapping() + { + _storage->_has_mapping = false; + for ( auto const& n : _storage->_nodes ) + { + Ntk::remove_from_mapping( n ); + } + _storage->_nodes.clear(); + } + + template + void add_to_mapping( node const& n, LeavesIterator begin, LeavesIterator end ) + { + _storage->_has_mapping = true; + _storage->_nodes.insert( n ); + Ntk::add_to_mapping( n, begin, end ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->_leaves.begin(), _storage->_leaves.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->_roots.begin(), _storage->_roots.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _storage->_gates.begin(), _storage->_gates.end(), fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _storage->_index_to_node.begin(), _storage->_index_to_node.end(), fn ); + } + +private: + void init_cell_refs() + { + _storage->_cell_refs.reset(); + _storage->_cell_parents.reset(); + + /* initial ref counts for cells */ + Ntk::foreach_gate( [&]( auto const& n ) { + if ( Ntk::is_cell_root( n ) ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + _storage->_cell_parents[n2].push_back( n ); + } ); + } } ); + Ntk::foreach_po( [&]( auto const& f ) { + _storage->_cell_refs[f]++; + } ); + } + + void collect_mffc( node const& pivot, std::vector& gates ) + { + Ntk::incr_trav_id(); + collect_gates( pivot, gates ); + const auto it = std::remove_if( gates.begin(), gates.end(), [&]( auto const& g ) { return _storage->_gates.count( g ); } ); + gates.erase( it, gates.end() ); + } + + void collect_gates( node const& pivot, std::vector& gates ) + { + assert( !Ntk::is_pi( pivot ) ); + + Ntk::set_visited( Ntk::get_node( Ntk::get_constant( false ) ), Ntk::trav_id() ); + Ntk::set_visited( Ntk::get_node( Ntk::get_constant( true ) ), Ntk::trav_id() ); + + Ntk::foreach_cell_fanin( pivot, [this]( auto const& n ) { + Ntk::set_visited( n, Ntk::trav_id() ); + } ); + + collect_gates_rec( pivot, gates ); + } + + void collect_gates_rec( node const& n, std::vector& gates ) + { + if ( Ntk::visited( n ) == Ntk::trav_id() ) + return; + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + return; + + Ntk::set_visited( n, Ntk::trav_id() ); + Ntk::foreach_fanin( n, [&]( auto const& f ) { + collect_gates_rec( Ntk::get_node( f ), gates ); + } ); + gates.push_back( n ); + } + + void add_node( node const& pivot, std::vector const& gates ) + { + /*std::cout << "add_node(" << pivot << ", { "; + for ( auto const& g : gates ) { + std::cout << g << " "; + } + std::cout << "})\n";*/ + _storage->_nodes.insert( pivot ); + std::copy( gates.begin(), gates.end(), std::insert_iterator( _storage->_gates, _storage->_gates.begin() ) ); + } + + std::optional find_next_pivot() + { + /* deref */ + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]--; + } ); + } + + std::vector candidates; + std::unordered_set inputs; + + do + { + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + if ( !_storage->_nodes.count( n2 ) && !Ntk::is_pi( n2 ) && !_storage->_cell_refs[n2] ) + { + candidates.push_back( n2 ); + inputs.insert( n2 ); + } + } ); + } + + if ( !candidates.empty() ) + { + const auto best = max_element_unary( + candidates.begin(), candidates.end(), + [&]( auto const& cand ) { + auto cnt{ 0 }; + this->foreach_cell_fanin( cand, [&]( auto const& n2 ) { + cnt += inputs.count( n2 ); + } ); + return cnt; + }, + -1 ); + candidates[0] = *best; + break; + } + + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + if ( !_storage->_nodes.count( n2 ) && !Ntk::is_pi( n2 ) ) + { + candidates.push_back( n2 ); + inputs.insert( n2 ); + } + } ); + } + + for ( auto const& n : _storage->_nodes ) + { + if ( _storage->_cell_refs[n] == 0 ) + continue; + if ( _storage->_cell_refs[n] >= 5 ) + continue; + if ( _storage->_cell_refs[n] == 1 && _storage->_cell_parents[n].size() == 1 && !_storage->_nodes.count( _storage->_cell_parents[n].front() ) ) + { + candidates.clear(); + candidates.push_back( _storage->_cell_parents[n].front() ); + break; + } + std::copy_if( _storage->_cell_parents[n].begin(), _storage->_cell_parents[n].end(), + std::back_inserter( candidates ), + [&]( auto const& g ) { + return !_storage->_nodes.count( g ); + } ); + } + + if ( !candidates.empty() ) + { + const auto best = max_element_unary( + candidates.begin(), candidates.end(), + [&]( auto const& cand ) { + auto cnt{ 0 }; + this->foreach_cell_fanin( cand, [&]( auto const& n2 ) { + cnt += inputs.count( n2 ); + } ); + return cnt; + }, + -1 ); + candidates[0] = *best; + } + } while ( false ); + + /* ref */ + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + } ); + } + + if ( candidates.empty() ) + { + return std::nullopt; + } + else + { + return candidates.front(); + } + } + + void find_leaves_and_roots() + { + _storage->_leaves.clear(); + _storage->_window_mask[0] = 0u; + for ( auto const& g : _storage->_gates ) + { + Ntk::foreach_fanin( g, [&]( auto const& f ) { + auto const child = Ntk::get_node( f ); + if ( !_storage->_gates.count( child ) ) + { + _storage->_leaves.insert( child ); + _storage->_window_mask[0] |= UINT64_C( 1 ) << ( Ntk::node_to_index( child ) % 64 ); + } + } ); + } + + _storage->_roots.clear(); + _storage->_window_mask[1] = 0u; + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]--; + } ); + } + for ( auto const& n : _storage->_nodes ) + { + if ( _storage->_cell_refs[n] ) + { + _storage->_roots.insert( Ntk::make_signal( n ) ); + _storage->_window_mask[1] |= UINT64_C( 1 ) << ( Ntk::node_to_index( n ) % 64 ); + } + } + for ( auto const& n : _storage->_nodes ) + { + Ntk::foreach_cell_fanin( n, [&]( auto const& n2 ) { + _storage->_cell_refs[n2]++; + } ); + } + } + + void set_indexes() + { + _storage->_index_to_node.resize( _storage->_num_constants + _storage->_leaves.size() + _storage->_gates.size() ); + _storage->_node_to_index.clear(); + + _storage->_node_to_index[_storage->_index_to_node[0] = Ntk::get_node( Ntk::get_constant( false ) )] = 0; + + if ( _storage->_num_constants == 2u ) + { + _storage->_node_to_index[_storage->_index_to_node[1] = Ntk::get_node( Ntk::get_constant( true ) )] = 1; + } + + auto idx = _storage->_num_constants; + for ( auto const& n : _storage->_leaves ) + { + _storage->_node_to_index[_storage->_index_to_node[idx] = n] = idx; + ++idx; + } + for ( auto const& n : _storage->_gates ) + { + _storage->_node_to_index[_storage->_index_to_node[idx] = n] = idx; + ++idx; + } + + assert( _storage->_index_to_node.size() == idx ); + } + +private: + std::shared_ptr> _storage; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/circuit_validator.hpp b/include/mockturtle/algorithms/circuit_validator.hpp new file mode 100644 index 0000000..45f0afc --- /dev/null +++ b/include/mockturtle/algorithms/circuit_validator.hpp @@ -0,0 +1,799 @@ +/* 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 circuit_validator.hpp + \brief Validate potential circuit optimization choices with SAT. + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../utils/index_list.hpp" +#include "../utils/node_map.hpp" +#include "cnf.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +struct validator_params +{ + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Whether to consider ODC, and how many levels. 0 = No consideration. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; + + /*! \brief Conflict limit of the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Seed for randomized solving. */ + uint32_t random_seed{ 0 }; +}; + +template +class circuit_validator +{ +public: + static constexpr bool use_odc_ = use_odc; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using add_clause_fn_t = std::function const& )>; + + enum gate_type + { + AND, + XOR, + MAJ, + MUX + }; + + explicit circuit_validator( Ntk const& ntk, validator_params const& ps = {} ) + : ntk( ntk ), ps( ps ), literals( ntk ), constructed( ntk ), num_invoke( 0u ), cex( ntk.num_pis() ) + { + 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_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + + if constexpr ( use_pushpop ) + { +#if defined( BILL_HAS_Z3 ) + static_assert( Solver == bill::solvers::z3 || Solver == bill::solvers::bsat2, "Solver does not support push/pop" ); +#else + static_assert( Solver == bill::solvers::bsat2, "Solver does not support push/pop" ); +#endif + } + if constexpr ( randomize ) + { +#if defined( BILL_HAS_Z3 ) + static_assert( Solver == bill::solvers::z3 || Solver == bill::solvers::bsat2, "Solver does not support set_random" ); +#else + static_assert( Solver == bill::solvers::bsat2, "Solver does not support set_random" ); +#endif + } + if constexpr ( use_odc ) + { + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + } + + add_event = ntk.events().register_add_event( [&]( node const& n ) { + (void)n; + literals.resize(); + } ); + + /* constants are mapped to var 0 */ + literals[ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + literals[ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + literals[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + restart(); + } + + ~circuit_validator() + { + ntk.events().release_add_event( add_event ); + } + + /*! \brief Set ODC levels */ + void set_odc_levels( uint32_t odc_levels ) + { + ps.odc_levels = odc_levels; + } + + /*! \brief Validate functional equivalence of signals `f` and `d`. */ + std::optional validate( signal const& f, signal const& d ) + { + if ( !constructed.has( d ) && !ntk.is_pi( ntk.get_node( d ) ) && !ntk.is_constant( ntk.get_node( d ) ) ) + { + construct( ntk.get_node( d ) ); + } + auto const res = validate( ntk.get_node( f ), lit_not_cond( literals[d], ntk.is_complemented( f ) ^ ntk.is_complemented( d ) ) ); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Validate functional equivalence of node `root` and signal `d`. */ + std::optional validate( node const& root, signal const& d ) + { + if ( !constructed.has( d ) && !ntk.is_pi( ntk.get_node( d ) ) && !ntk.is_constant( ntk.get_node( d ) ) ) + { + construct( ntk.get_node( d ) ); + } + auto const res = validate( root, lit_not_cond( literals[d], ntk.is_complemented( d ) ) ); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Validate functional equivalence of signal `f` with a circuit represented by an index_list. + * + * \param id_list The index_list representing a circuit. + * \param divs Existing nodes in the network, serving as PIs of `id_list`. + * \param inverted Whether to validate equivalence or inverse equivalence. + */ + template + std::optional validate( signal const& f, std::vector const& divs, index_list_type const& id_list, bool inverted = false ) + { + return validate( ntk.get_node( f ), divs.begin(), divs.end(), id_list, inverted ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate functional equivalence of node `root` with an index_list. */ + template + std::optional validate( node const& root, std::vector const& divs, index_list_type const& id_list, bool inverted = false ) + { + return validate( root, divs.begin(), divs.end(), id_list, inverted ); + } + + /*! \brief Validate functional equivalence of signal `f` with an index_list. */ + template + std::optional validate( signal const& f, iterator_type divs_begin, iterator_type divs_end, index_list_type const& id_list, bool inverted = false ) + { + return validate( ntk.get_node( f ), divs_begin, divs_end, id_list, inverted ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate functional equivalence of node `root` with an index_list. */ + template + std::optional validate( node const& root, iterator_type divs_begin, iterator_type divs_end, index_list_type const& id_list, bool inverted = false ) + { + static_assert( std::is_same_v || + std::is_same_v> || + std::is_same_v> || + std::is_same_v, "Unknown type of index list" ); + assert( uint64_t( std::distance( divs_begin, divs_end ) ) == id_list.num_pis() && "Size of the provided divisor list does not match number of PIs of the index list" ); + assert( id_list.num_pos() == 1u && "Index list must have exactly one PO" ); + + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::vector lits; + lits.reserve( id_list.num_pis() + id_list.num_gates() + 1 ); + lits.emplace_back( literals[ntk.get_constant( false )] ); + for ( auto it = divs_begin; it != divs_end; ++it ) + { + if ( !constructed.has( *it ) && !ntk.is_pi( *it ) && !ntk.is_constant( *it ) ) + { + construct( *it ); + } + lits.emplace_back( literals[*it] ); + } + + if constexpr ( use_pushpop ) + { + push(); + } + + if constexpr ( std::is_same_v> || std::is_same_v> ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + lits.emplace_back( add_clauses_for_2input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), std::nullopt, id_lit0 < id_lit1 ? AND : XOR ) ); + } ); + } + if constexpr ( std::is_same_v ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1, uint32_t id_lit2 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + uint32_t const node_pos2 = id_lit2 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + assert( node_pos2 < lits.size() ); + lits.emplace_back( add_clauses_for_3input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), lit_not_cond( lits[node_pos2], id_lit2 & 0x1 ), std::nullopt, MAJ ) ); + } ); + } + if constexpr ( std::is_same_v ) + { + id_list.foreach_gate( [&]( uint32_t id_lit0, uint32_t id_lit1, uint32_t id_lit2 ) { + uint32_t const node_pos0 = id_lit0 >> 1; + uint32_t const node_pos1 = id_lit1 >> 1; + uint32_t const node_pos2 = id_lit2 >> 1; + assert( node_pos0 < lits.size() ); + assert( node_pos1 < lits.size() ); + assert( node_pos2 < lits.size() ); + lits.emplace_back( add_clauses_for_3input_gate( lit_not_cond( lits[node_pos0], id_lit0 & 0x1 ), lit_not_cond( lits[node_pos1], id_lit1 & 0x1 ), lit_not_cond( lits[node_pos2], id_lit2 & 0x1 ), std::nullopt, MUX ) ); + } ); + } + + bill::lit_type lit_out; + id_list.foreach_po( [&]( uint32_t id_lit ) { + lit_out = lit_not_cond( lits[id_lit >> 1], ( id_lit & 0x1 ) ^ inverted ); + } ); + + auto const res = validate( root, lit_out ); + + if constexpr ( use_pushpop ) + { + pop(); + } + + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + + return res; + } + + /*! \brief Validate whether signal `f` is a constant of `value`. */ + std::optional validate( signal const& f, bool value ) + { + return validate( ntk.get_node( f ), value ^ ntk.is_complemented( f ) ); + } + + /*! \brief Validate whether node `root` is a constant of `value`. */ + std::optional validate( node const& root, bool value ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::optional res; + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + if constexpr ( use_pushpop ) + { + push(); + } + res = solve( { build_odc_window( root, ~literals[root] ), lit_not_cond( literals[root], value ) } ); + if constexpr ( use_pushpop ) + { + pop(); + } + } + else + { + res = solve( { lit_not_cond( literals[root], value ) } ); + } + } + else + { + res = solve( { lit_not_cond( literals[root], value ) } ); + } + + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return res; + } + + /*! \brief Generate pattern(s) for signal `f` to be `value`, optionally blocking several known patterns. + * + * Requires `use_pushpop = true`, which is only supported for `bsat2` and `z3`. If `bsat2` is used, + * and if the network has more than 2048 PIs, the `BUFFER_SIZE` in `lib/bill/sat/interface/abc_bsat2.hpp` + * has to be increased to at least `ntk.num_pis()`. + * + * If `block_patterns` and the returned vector are both empty, `f` is validated to be a constant of `!value`. + * + * \param block_patterns Patterns to be blocked in the solver. (Will not generate any of them.) + * \param num_patterns Number of patterns to be generated, if possible. (The size of the result may be smaller than this number, but never larger.) + */ + template> + std::vector> generate_pattern( signal const& f, bool value, std::vector> const& block_patterns = {}, uint32_t num_patterns = 1u ) + { + return generate_pattern( ntk.get_node( f ), value ^ ntk.is_complemented( f ), block_patterns, num_patterns ); + } + + /*! \brief Generate pattern(s) for node `root` to be `value`, optionally blocking several known patterns. */ + template> + std::vector> generate_pattern( node const& root, bool value, std::vector> const& block_patterns = {}, uint32_t num_patterns = 1u ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + push(); + + for ( auto const& pattern : block_patterns ) + { + block_pattern( pattern ); + } + + std::vector assumptions( { lit_not_cond( literals[root], !value ) } ); + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + assumptions.emplace_back( build_odc_window( root, ~literals[root] ) ); + } + } + + std::optional res; + std::vector> generated; + for ( auto i = 0u; i < num_patterns; ++i ) + { + res = solve( assumptions ); + + if ( !res || *res ) /* timeout or UNSAT */ + { + break; + } + else /* SAT */ + { + generated.emplace_back( cex ); + block_pattern( cex ); + } + } + + pop(); + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) + { + restart(); + } + return generated; + } + + /*! \brief Update CNF clauses. + * + * This function should be called when the function of one or more nodes + * has been modified (typically when utilizing ODCs). + */ + void update() + { + restart(); + } + +private: + void restart() + { + num_invoke = 0u; + solver.restart(); + if constexpr ( randomize ) + { + solver.set_random_phase( ps.random_seed ); + } + + constructed.reset(); + + solver.add_variables( ntk.num_pis() + 1 ); + solver.add_clause( { ~literals[ntk.get_constant( false )] } ); + + if constexpr ( has_EXCDC_interface_v ) + { + ntk.add_EXCDC_clauses( solver ); + } + + if constexpr ( has_EXODC_interface_v ) + { + if ( ps.odc_levels == -1 ) + { + po_lits_link.clear(); + typename Ntk::base_type oec_ntk; + ntk.build_oe_miter( oec_ntk ); + + std::vector po_lits; + ntk.foreach_po( [&]( auto const& f ) { + if ( !ntk.is_pi( ntk.get_node( f ) ) && !constructed.has( f ) ) + { + construct( ntk.get_node( f ) ); + } + po_lits.emplace_back( lit_not_cond( literals[f], ntk.is_complemented( f ) ) ); + po_lits_link.emplace_back( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + + /* OEC */ + assert( oec_ntk.num_pis() == ntk.num_pos() * 2 && oec_ntk.num_pos() == 1 ); + node_map oe_lits( oec_ntk ); + oe_lits[oec_ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( oec_ntk.get_node( oec_ntk.get_constant( false ) ) != oec_ntk.get_node( oec_ntk.get_constant( true ) ) ) + { + oe_lits[oec_ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + oec_ntk.foreach_pi( [&]( auto const& n, auto i ) { + oe_lits[n] = i < ntk.num_pos() ? po_lits[i] : po_lits_link[i - ntk.num_pos()]; + } ); + + oec_ntk.foreach_gate( [&]( auto const& n ){ + oe_lits[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + auto out_lits = generate_cnf( oec_ntk, add_clause_fn, oe_lits ); + solver.add_clause( {out_lits[0]} ); + } + } + } + + bill::lit_type construct( node const& n ) + { + assert( !constructed.has( n ) && !ntk.is_pi( n ) && !ntk.is_constant( n ) ); + if constexpr ( use_pushpop ) + { + if ( between_push_pop ) + { + tmp.emplace_back( n ); + } + } + + std::vector child_lits; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( !constructed.has( f ) && !ntk.is_pi( ntk.get_node( f ) ) && !ntk.is_constant( ntk.get_node( f ) ) ) + { + construct( ntk.get_node( f ) ); + } + child_lits.push_back( lit_not_cond( literals[f], ntk.is_complemented( f ) ) ); + } ); + bill::lit_type node_lit = literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + constructed[n] = true; + + if ( ntk.is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], add_clause_fn ); + } + else if ( ntk.is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], add_clause_fn ); + } + else if ( ntk.is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + else if ( ntk.is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + else if ( ntk.is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], add_clause_fn ); + } + return node_lit; + } + + void push() + { + solver.push(); + between_push_pop = true; + tmp.clear(); + } + + void pop() + { + solver.pop(); + for ( auto& n : tmp ) + { + constructed.erase( n ); + } + between_push_pop = false; + } + + bill::lit_type add_clauses_for_2input_gate( bill::lit_type a, bill::lit_type b, std::optional c = std::nullopt, gate_type type = AND ) + { + assert( type == AND || type == XOR ); + + auto nlit = c ? *c : bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( type == AND ) + { + detail::on_and( nlit, a, b, add_clause_fn ); + } + else if ( type == XOR ) + { + detail::on_xor( nlit, a, b, add_clause_fn ); + } + + return nlit; + } + + bill::lit_type add_clauses_for_3input_gate( bill::lit_type a, bill::lit_type b, bill::lit_type c, std::optional d = std::nullopt, gate_type type = MAJ ) + { + assert( type == MAJ || type == XOR || type == MUX ); + + auto nlit = d ? *d : bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( type == MAJ ) + { + detail::on_maj( nlit, a, b, c, add_clause_fn ); + } + else if ( type == XOR ) + { + detail::on_xor3( nlit, a, b, c, add_clause_fn ); + } + else if ( type == MUX ) + { + detail::on_ite( nlit, a, b, c, add_clause_fn ); + } + + return nlit; + } + + std::optional solve( std::vector assumptions ) + { + ++num_invoke; + auto const res = solver.solve( assumptions, ps.conflict_limit ); + + if ( res == bill::result::states::satisfiable ) + { + auto model = solver.get_model().model(); + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + cex.at( i ) = model.at( i + 1 ) == bill::lbool_type::true_; + } + + if constexpr ( has_EXCDC_interface_v ) + { + assert( !ntk.pattern_is_EXCDC( cex ) ); + } + return false; + } + else if ( res == bill::result::states::unsatisfiable ) + { + return true; + } + else + { + return std::nullopt; /* timeout or something wrong */ + } + } + + std::optional validate( node const& root, bill::lit_type const& lit ) + { + if ( !constructed.has( root ) && !ntk.is_pi( root ) && !ntk.is_constant( root ) ) + { + construct( root ); + } + + std::optional res; + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + if constexpr ( use_pushpop ) + { + push(); + } + res = solve( { build_odc_window( root, lit ) } ); + if constexpr ( use_pushpop ) + { + pop(); + } + } + else + { + auto nlit = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + solver.add_clause( { literals[root], lit, nlit } ); + solver.add_clause( { ~( literals[root] ), ~lit, nlit } ); + res = solve( { ~nlit } ); + } + } + else + { + auto nlit = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + solver.add_clause( { literals[root], lit, nlit } ); + solver.add_clause( { ~( literals[root] ), ~lit, nlit } ); + res = solve( { ~nlit } ); + } + + return res; + } + + void block_pattern( std::vector const& pattern ) + { + assert( pattern.size() == ntk.num_pis() ); + std::vector clause; + ntk.foreach_pi( [&]( auto const& n, auto i ) { + clause.emplace_back( lit_not_cond( literals[n], pattern[i] ) ); + } ); + solver.add_clause( clause ); + } + +private: + template> + bill::lit_type build_odc_window( node const& root, bill::lit_type const& lit ) + { + /* literals for the duplicated fanout cone */ + unordered_node_map lits( ntk ); + /* miter literals that should be empty */ + std::vector miter; + + lits[root] = lit; + ntk.incr_trav_id(); + make_lit_fanout_cone_rec( root, lits, miter, 1 ); + ntk.incr_trav_id(); + duplicate_fanout_cone_rec( root, lits, 1 ); + + if constexpr ( has_EXODC_interface_v ) + { + if ( ps.odc_levels == -1 ) + { + assert( miter.size() == 0 ); + auto assump = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + auto dup_po_lit = lit_not_cond( lits.has( ntk.get_node( f ) ) ? lits[f] : literals[f], ntk.is_complemented( f ) ); + solver.add_clause( {~assump, ~po_lits_link[i], dup_po_lit} ); + solver.add_clause( {~assump, po_lits_link[i], ~dup_po_lit} ); + } ); + return assump; + } + } + + /* miter for POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( !lits.has( ntk.get_node( f ) ) ) + return true; /* PO not in TFO, skip */ + add_miter_clauses( ntk.get_node( f ), lits, miter ); + return true; /* next */ + } ); + + assert( miter.size() > 0 && "no fanout node at distance odc_levels and there is no PO in TFO cone (possibly due to a dangling cone)" ); + auto assump = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + miter.emplace_back( ~assump ); + solver.add_clause( miter ); + return assump; + } + + template> + void duplicate_fanout_cone_rec( node const& n, unordered_node_map const& lits, int32_t level ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + return true; /* skip */ + ntk.set_visited( fo, ntk.trav_id() ); + + std::vector l_fi; + ntk.foreach_fanin( fo, [&]( auto const& fi ) { + if ( !constructed.has( fi ) && !ntk.is_pi( ntk.get_node( fi ) ) && !ntk.is_constant( ntk.get_node( fi ) ) ) + { + construct( ntk.get_node( fi ) ); + } + l_fi.emplace_back( lit_not_cond( lits.has( ntk.get_node( fi ) ) ? lits[fi] : literals[fi], ntk.is_complemented( fi ) ) ); + } ); + if ( l_fi.size() == 2u ) + { + assert( ntk.is_and( fo ) || ntk.is_xor( fo ) ); + add_clauses_for_2input_gate( l_fi[0], l_fi[1], lits[fo], ntk.is_and( fo ) ? AND : XOR ); + } + else + { + assert( l_fi.size() == 3u ); + assert( ntk.is_maj( fo ) || ntk.is_xor3( fo ) ); + add_clauses_for_3input_gate( l_fi[0], l_fi[1], l_fi[2], lits[fo], ntk.is_maj( fo ) ? MAJ : XOR ); + } + + if ( level == ps.odc_levels ) + return true; + + duplicate_fanout_cone_rec( fo, lits, level + 1 ); + return true; /* next */ + } ); + } + + template> + void make_lit_fanout_cone_rec( node const& n, unordered_node_map& lits, std::vector& miter, int32_t level ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + return true; /* skip */ + ntk.set_visited( fo, ntk.trav_id() ); + + if ( !constructed.has( fo ) ) + { + construct( fo ); + } + + lits[fo] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + + if ( level == ps.odc_levels ) + { + add_miter_clauses( fo, lits, miter ); + return true; + } + + make_lit_fanout_cone_rec( fo, lits, miter, level + 1 ); + return true; /* next */ + } ); + } + + template> + void add_miter_clauses( node const& n, unordered_node_map const& lits, std::vector& miter ) + { + assert( constructed.has( n ) && literals[n] != literals[ntk.get_constant( false )] ); + miter.emplace_back( add_clauses_for_2input_gate( literals[n], lits[n], std::nullopt, XOR ) ); + } + +private: + Ntk const& ntk; + + validator_params ps; + + node_map literals; + unordered_node_map constructed; + bill::solver solver; + add_clause_fn_t add_clause_fn = [&]( auto const& clause ) { solver.add_clause( clause ); }; + + static const uint32_t MIN_NUM_INVOKE = 20u; + uint32_t num_invoke; + + bool between_push_pop = false; + std::vector tmp; + + std::shared_ptr::add_event_type> add_event; + + std::vector po_lits_link; + +public: + std::vector cex; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/cleanup.hpp b/include/mockturtle/algorithms/cleanup.hpp new file mode 100644 index 0000000..766d677 --- /dev/null +++ b/include/mockturtle/algorithms/cleanup.hpp @@ -0,0 +1,657 @@ +/* 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 cleanup.hpp + \brief Cleans up networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/crossed.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +void cleanup_dangling_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto child, auto ) { + const auto f = old_to_new[child]; + if ( ntk.is_complemented( child ) ) + { + children.push_back( dest.create_not( f ) ); + } + else + { + children.push_back( f ); + } + } ); + + /* clone node */ + if constexpr ( std::is_same_v ) + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + else + { + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( node ) ) + { + old_to_new[node] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( node ) ) + { + old_to_new[node] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( node ) ) + { + old_to_new[node] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( node ) ) + { + old_to_new[node] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( node ) ) + { + old_to_new[node] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( node ) ) + { + old_to_new[node] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_nary_and_v ) + { + static_assert( has_create_nary_and_v, "NtkDest cannot create n-ary AND gates" ); + if ( ntk.is_nary_and( node ) ) + { + old_to_new[node] = dest.create_nary_and( children ); + break; + } + } + if constexpr ( has_is_nary_or_v ) + { + static_assert( has_create_nary_or_v, "NtkDest cannot create n-ary OR gates" ); + if ( ntk.is_nary_or( node ) ) + { + old_to_new[node] = dest.create_nary_or( children ); + break; + } + } + if constexpr ( has_is_nary_xor_v ) + { + static_assert( has_create_nary_xor_v, "NtkDest cannot create n-ary XOR gates" ); + if ( ntk.is_nary_xor( node ) ) + { + old_to_new[node] = dest.create_nary_xor( children ); + break; + } + } + if constexpr ( has_is_not_v ) + { + static_assert( has_create_not_v, "NtkDest cannot create NOT gates" ); + if ( ntk.is_not( node ) ) + { + old_to_new[node] = dest.create_not( children[0] ); + break; + } + } + if constexpr ( has_is_buf_v ) + { + static_assert( has_create_buf_v, "NtkDest cannot create buffers" ); + if ( ntk.is_buf( node ) ) + { + old_to_new[node] = dest.create_buf( children[0] ); + break; + } + } + if constexpr ( has_is_function_v ) + { + static_assert( has_create_node_v, "NtkDest cannot create arbitrary function gates" ); + old_to_new[node] = dest.create_node( children, ntk.node_function( node ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_dangling_with_crossings_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, node_map, NtkSrc>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto const& f ) { + if ( ntk.is_crossing( ntk.get_node( f ) ) ) + children.push_back( ntk.is_second( f ) ? dest.make_second( old_to_new[f] ) : old_to_new[f] ); + else + children.push_back( old_to_new[f] ); + } ); + + /* clone node */ + if ( ntk.is_crossing( node ) ) + { + assert( children.size() == 2 ); + old_to_new[node] = dest.create_crossing( children[0], children[1] ).first; + } + else + { + old_to_new[node] = dest.clone_node( ntk, node, children ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( node ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[node], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[node], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void cleanup_luts_impl( Ntk const& ntk, Ntk& dest, LeavesIterator begin, LeavesIterator end, node_map, Ntk>& old_to_new ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[node] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* iterate through nodes */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return; /* continue */ + + auto func = ntk.node_function( n ); + + /* constant propagation */ + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( dest.is_constant( old_to_new[f] ) ) + { + if ( dest.constant_value( old_to_new[f] ) != ntk.is_complemented( f ) ) + { + kitty::cofactor1_inplace( func, i ); + } + else + { + kitty::cofactor0_inplace( func, i ); + } + } + } ); + + const auto support = kitty::min_base_inplace( func ); + auto new_func = kitty::shrink_to( func, static_cast( support.size() ) ); + + std::vector> children; + if ( auto var = support.begin(); var != support.end() ) + { + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( *var == i ) + { + auto const& new_f = old_to_new[f]; + children.push_back( ntk.is_complemented( f ) ? dest.create_not( new_f ) : new_f ); + if ( ++var == support.end() ) + { + return false; + } + } + return true; + } ); + } + + if ( new_func.num_vars() == 0u ) + { + old_to_new[n] = dest.get_constant( !kitty::is_const0( new_func ) ); + } + else if ( new_func.num_vars() == 1u ) + { + old_to_new[n] = *( new_func.begin() ) == 0b10 ? children.front() : dest.create_not( children.front() ); + } + else + { + old_to_new[n] = dest.create_node( children, new_func ); + } + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( old_to_new[n], ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !old_to_new[n], ntk.get_name( !s ) ); + } + } + } ); +} + +template +void clone_inputs( NtkSrc const& ntk, NtkDest& dest, std::vector>& cis, bool remove_dangling_PIs = false ) +{ + /* network name */ + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + dest.set_network_name( ntk.get_network_name() ); + } + + /* PIs & PI names */ + ntk.foreach_pi( [&]( auto n ) { + if ( remove_dangling_PIs && ntk.fanout_size( n ) == 0 ) + { + cis.push_back( dest.get_constant( false ) ); + } + else + { + cis.push_back( dest.create_pi() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + } + } ); + + /* ROs & RO names & register information */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk.foreach_ro( [&]( auto const& n, auto i ) { + cis.push_back( dest.create_ro() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + dest.set_register( i, ntk.register_at( i ) ); + } ); + } +} + +template +void clone_outputs( NtkSrc const& ntk, NtkDest& dest, node_map, NtkSrc> const& old_to_new, bool remove_redundant_POs = false ) +{ + /* POs */ + ntk.foreach_po( [&]( auto const& po ) { + auto const f = old_to_new[po]; + auto const n = dest.get_node( f ); + if ( remove_redundant_POs && ( dest.is_pi( n ) || dest.is_constant( n ) ) ) + { + return; + } + dest.create_po( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( ntk.is_complemented( f ) ? dest.create_not( old_to_new[f] ) : old_to_new[f] ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } +} + +} // namespace detail + +template +std::vector> cleanup_dangling( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + + node_map, NtkSrc> old_to_new( ntk ); + detail::cleanup_dangling_impl( ntk, dest, begin, end, old_to_new ); + std::vector> fs; + + /* create outputs in the same order */ + ntk.foreach_po( [&]( auto po ) { + const auto f = old_to_new[po]; + fs.push_back( ntk.is_complemented( po ) ? dest.create_not( f ) : f ); + } ); + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto ri ) { + const auto f = old_to_new[ri]; + fs.push_back( ntk.is_complemented( ri ) ? dest.create_not( f ) : f ); + } ); + } + + return fs; +} + +/*! \brief Cleans up dangling nodes. + * + * This method reconstructs a network and omits all dangling nodes. If the flag + * `remove_dangling_PIs` is true, dangling PIs are also omitted. If the flag + * `remove_redundant_POs` is true, redundant POs, i.e. POs connected to a PI or + * constant, are also omitted. The network types of the source and destination + * network are the same. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + */ +template +[[nodiscard]] NtkDest cleanup_dangling( NtkSrc const& ntk, bool remove_dangling_PIs = false, bool remove_redundant_POs = false ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_node_to_index_v, "NtkSrc does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_is_complemented_v, "NtkDest does not implement the is_complemented method" ); + + NtkDest dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis, remove_dangling_PIs ); + + node_map, NtkSrc> old_to_new( ntk ); + if constexpr ( is_crossed_network_type_v ) + { + detail::cleanup_dangling_with_crossings_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + else + { + detail::cleanup_dangling_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + } + + detail::clone_outputs( ntk, dest, old_to_new, remove_redundant_POs ); + + return dest; +} + +/*! \brief Cleans up LUT nodes. + * + * This method reconstructs a LUT network and optimizes LUTs when they do not + * depend on all their fanin, or when some of the fanin are constant inputs. + * + * Constant gate inputs will be propagated. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_fanin` + * - `create_pi` + * - `create_po` + * - `create_node` + * - `create_not` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `node_function` + */ +template +[[nodiscard]] Ntk cleanup_luts( Ntk const& ntk ) +{ + 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_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_node_v, "Ntk does not implement the create_node method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + Ntk dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis ); + + node_map, Ntk> old_to_new( ntk ); + detail::cleanup_luts_impl( ntk, dest, cis.begin(), cis.end(), old_to_new ); + + detail::clone_outputs( ntk, dest, old_to_new ); + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cnf.hpp b/include/mockturtle/algorithms/cnf.hpp new file mode 100644 index 0000000..4a825d6 --- /dev/null +++ b/include/mockturtle/algorithms/cnf.hpp @@ -0,0 +1,507 @@ +/* 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 cnf.hpp + \brief CNF generation methods + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" + +namespace mockturtle +{ + +inline constexpr uint32_t make_lit( uint32_t var, bool is_complemented = false ) +{ + return ( var << 1 ) | ( is_complemented ? 1 : 0 ); +} + +inline constexpr uint32_t lit_not( uint32_t lit ) +{ + return lit ^ 0x1; +} + +inline bill::lit_type lit_not( bill::lit_type lit ) +{ + return ~lit; +} + +inline constexpr uint32_t lit_not_cond( uint32_t lit, bool cond ) +{ + return cond ? lit ^ 0x1 : lit; +} + +inline bill::lit_type lit_not_cond( bill::lit_type lit, bool cond ) +{ + return cond ? ~lit : lit; +} + +namespace detail +{ + +/* c = a & b */ +template +inline void on_and( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { a, lit_not( c ) } ); + fn( { b, lit_not( c ) } ); + fn( { lit_not( a ), lit_not( b ), c } ); +} + +/* c = a & b */ +template +inline void on_and( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { a, ~c } ); + fn( { b, ~c } ); + fn( { ~a, ~b, c } ); +} + +/* c = a | b */ +template +inline void on_or( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { lit_not( a ), c } ); + fn( { lit_not( b ), c } ); + fn( { a, b, lit_not( c ) } ); +} + +/* c = a | b */ +template +inline void on_or( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { ~a, c } ); + fn( { ~b, c } ); + fn( { a, b, ~c } ); +} + +/* c = a ^ b */ +template +inline void on_xor( uint32_t c, uint32_t a, uint32_t b, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), lit_not( c ) } ); + fn( { lit_not( a ), b, c } ); + fn( { a, lit_not( b ), c } ); + fn( { a, b, lit_not( c ) } ); +} + +/* c = a ^ b */ +template +inline void on_xor( bill::lit_type c, bill::lit_type a, bill::lit_type b, ClauseFn const& fn ) +{ + fn( { ~a, ~b, ~c } ); + fn( { ~a, b, c } ); + fn( { a, ~b, c } ); + fn( { a, b, ~c } ); +} + +/* d = */ +template +inline void on_maj( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), d } ); + fn( { lit_not( a ), lit_not( c ), d } ); + fn( { lit_not( b ), lit_not( c ), d } ); + fn( { a, b, lit_not( d ) } ); + fn( { a, c, lit_not( d ) } ); + fn( { b, c, lit_not( d ) } ); +} + +/* d = */ +template +inline void on_maj( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, ~b, d } ); + fn( { ~a, ~c, d } ); + fn( { ~b, ~c, d } ); + fn( { a, b, ~d } ); + fn( { a, c, ~d } ); + fn( { b, c, ~d } ); +} + +/* d = a ^ b ^ c */ +template +inline void on_xor3( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), b, c, d } ); + fn( { a, lit_not( b ), c, d } ); + fn( { a, b, lit_not( c ), d } ); + fn( { a, b, c, lit_not( d ) } ); + fn( { a, lit_not( b ), lit_not( c ), lit_not( d ) } ); + fn( { lit_not( a ), b, lit_not( c ), lit_not( d ) } ); + fn( { lit_not( a ), lit_not( b ), c, lit_not( d ) } ); + fn( { lit_not( a ), lit_not( b ), lit_not( c ), d } ); +} + +/* d = a ^ b ^ c */ +template +inline void on_xor3( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, b, c, d } ); + fn( { a, ~b, c, d } ); + fn( { a, b, ~c, d } ); + fn( { a, b, c, ~d } ); + fn( { a, ~b, ~c, ~d } ); + fn( { ~a, b, ~c, ~d } ); + fn( { ~a, ~b, c, ~d } ); + fn( { ~a, ~b, ~c, d } ); +} + +/* d = a ? b : c */ +template +inline void on_ite( uint32_t d, uint32_t a, uint32_t b, uint32_t c, ClauseFn const& fn ) +{ + fn( { lit_not( a ), lit_not( b ), d } ); + fn( { lit_not( a ), b, lit_not( d ) } ); + fn( { a, lit_not( c ), d } ); + fn( { a, c, lit_not( d ) } ); +} + +/* d = a ? b : c */ +template +inline void on_ite( bill::lit_type d, bill::lit_type a, bill::lit_type b, bill::lit_type c, ClauseFn const& fn ) +{ + fn( { ~a, ~b, d } ); + fn( { ~a, b, ~d } ); + fn( { a, ~c, d } ); + fn( { a, c, ~d } ); +} + +/* general case */ +template +inline void on_function( uint32_t f, std::vector const& child_lits, kitty::dynamic_truth_table const& function, ClauseFn const& fn ) +{ + const auto cnf = kitty::cnf_characteristic( function ); + + auto lits = child_lits; + lits.push_back( f ); + for ( auto const& cube : cnf ) + { + std::vector clause; + for ( auto i = 0u; i < lits.size(); ++i ) + { + if ( cube.get_mask( i ) ) + { + clause.push_back( lit_not_cond( lits[i], !cube.get_bit( i ) ) ); + } + } + fn( clause ); + } +} + +/* general case */ +template +inline void on_function( bill::lit_type f, std::vector const& child_lits, kitty::dynamic_truth_table const& function, ClauseFn const& fn ) +{ + const auto cnf = kitty::cnf_characteristic( function ); + + auto lits = child_lits; + lits.push_back( f ); + for ( auto const& cube : cnf ) + { + bill::result::clause_type clause; + for ( auto i = 0u; i < lits.size(); ++i ) + { + if ( cube.get_mask( i ) ) + { + clause.push_back( cube.get_bit( i ) ? lits[i] : ~lits[i] ); + } + } + fn( clause ); + } +} + +} // namespace detail + +/*! \brief Clause callback function for generate_cnf. */ +template +using clause_callback_t = std::function const& )>; + +/*! \brief Create a default node literal map. + * + * In the default map, constants are mapped to variable `0` (literal `1` for + * constant-1 and literal `0` for constant-0). Then each primary input is + * mapped to variables `1, ..., n`. Then the next variables are assigned to + * each gate in order, unless `gate_offset` is overridden which will be used for + * the next variable id. Therefore, this function can be used to create two + * independent sets of node literals for two networks, but keep the same indexes + * for the primary inputs. + */ +template +node_map node_literals( Ntk const& ntk, std::optional const& gate_offset = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + node_map node_lits( ntk ); + + if constexpr ( std::is_same::value ) + { + /* constants are mapped to var 0 */ + node_lits[ntk.get_constant( false )] = make_lit( 0 ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_lits[ntk.get_constant( true )] = make_lit( 0, true ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_lits[n] = make_lit( i + 1 ); + } ); + + /* compute literals for nodes */ + uint32_t next_var = gate_offset ? *gate_offset : ntk.num_pis() + 1; + ntk.foreach_gate( [&]( auto const& n ) { + node_lits[n] = make_lit( next_var++ ); + } ); + } + else if constexpr ( std::is_same::value ) + { + /* constants are mapped to var 0 */ + node_lits[ntk.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_lits[ntk.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + + /* first indexes (starting from 1) are for PIs */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_lits[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + /* compute literals for nodes */ + uint32_t next_var = gate_offset ? *gate_offset : ntk.num_pis() + 1; + ntk.foreach_gate( [&]( auto const& n ) { + node_lits[n] = bill::lit_type( next_var++, bill::lit_type::polarities::positive ); + } ); + } + + return node_lits; +} + +namespace detail +{ + +template +class generate_cnf_impl +{ +public: + generate_cnf_impl( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits ) + : ntk_( ntk ), + fn_( fn ), + node_lits_( node_lits ? *node_lits : node_literals( ntk ) ) + { + } + + std::vector run() + { + /* unit clause for constant-0 */ + fn_( { lit_not( node_lits_[ntk_.get_constant( false )] ) } ); + + /* compute clauses for nodes */ + ntk_.foreach_gate( [&]( auto const& n ) { + std::vector child_lits; + ntk_.foreach_fanin( n, [&]( auto const& f ) { + child_lits.push_back( lit_not_cond( node_lits_[f], ntk_.is_complemented( f ) ) ); + } ); + lit_t node_lit = node_lits_[n]; + + if constexpr ( has_is_and_v ) + { + if ( ntk_.is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk_.is_or( n ) ) + { + detail::on_or( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk_.is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], fn_ ); + return true; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk_.is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( ntk_.is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk_.is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], fn_ ); + return true; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk_.is_nary_and( n ) ) + { + fmt::print( "[e] nary-AND not yet supported in generate_cnf" ); + std::abort(); + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk_.is_nary_or( n ) ) + { + fmt::print( "[e] nary-OR not yet supported in generate_cnf" ); + std::abort(); + } + } + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk_.is_nary_xor( n ) ) + { + fmt::print( "[e] nary-XOR not yet supported in generate_cnf" ); + std::abort(); + } + } + + /* general case */ + detail::on_function( node_lit, child_lits, ntk_.node_function( n ), fn_ ); + return true; + } ); + + std::vector output_lits; + ntk_.foreach_po( [&]( auto const& f ) { + output_lits.push_back( lit_not_cond( node_lits_[f], ntk_.is_complemented( f ) ) ); + } ); + + return output_lits; + } + +private: + Ntk const& ntk_; + clause_callback_t const& fn_; + + node_map node_lits_; +}; + +} // namespace detail + +/*! \brief Generates CNF for a logic network. + * + * This function generates a CNF for a logic network using the Tseytin encoding + * for regular gates and ISOP-based CNF generation for arbitrary node functions. + * + * Input to the function are the network `ntk` and a clause callback function + * `fn`. For each clause that is generated, `fn` is called. A clause is + * represented as a vector of literals `std::vector`, following the + * customary literal convention, i.e., for a variable `v` its positive literal + * is `2 * v` and its negative literal is `2 * v + 1`. The third optional + * parameter can be used to pass an alternative mapping of nodes to literals. + * If none is given, it uses the default literal map created with the + * `node_literals` function. + * + * The return value of the function is a vector with a literal for each primary + * output in the network, following the same order as the primary outputs have + * been created. + * + * \param ntk Logic network + * \param fn Clause creation function + * \param node_lits (optional) custom node literal map + */ +template +std::vector generate_cnf( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + + detail::generate_cnf_impl impl( ntk, fn, node_lits ); + return impl.run(); +} + +template +std::vector generate_cnf( Ntk const& ntk, clause_callback_t const& fn, std::optional> const& node_lits = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + + detail::generate_cnf_impl impl( ntk, fn, node_lits ); + return impl.run(); +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/collapse_mapped.hpp b/include/mockturtle/algorithms/collapse_mapped.hpp new file mode 100644 index 0000000..7107bdd --- /dev/null +++ b/include/mockturtle/algorithms/collapse_mapped.hpp @@ -0,0 +1,463 @@ +/* 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 collapse_mapped.hpp + \brief Collapses mapped network into k-LUT network + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "simulation.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class collapse_mapped_network_impl +{ +public: + collapse_mapped_network_impl( NtkSource const& ntk ) + : ntk( ntk ) + { + } + + void run( NtkDest& dest ) + { + node_map, NtkSource> node_to_signal( ntk ); + + /* special map for output drivers to perform some optimizations */ + enum class driver_type + { + none, + pos, + neg, + mixed + }; + node_map node_driver_type( ntk, driver_type::none ); + + /* opposites are filled for nodes with mixed driver types, since they have + two nodes in the network. */ + std::unordered_map, signal> opposites; + + /* initial driver types */ + ntk.foreach_po( [&]( auto const& f ) { + switch ( node_driver_type[f] ) + { + case driver_type::none: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::pos; + break; + case driver_type::pos: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::mixed : driver_type::pos; + break; + case driver_type::neg: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::mixed; + break; + case driver_type::mixed: + default: + break; + } + } ); + + /* it could be that internal nodes also point to an output driver node */ + ntk.foreach_node( [&]( auto const n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) || !ntk.is_cell_root( n ) ) + return; + + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + if ( node_driver_type[fanin] == driver_type::neg ) + { + node_driver_type[fanin] = driver_type::mixed; + } + } ); + } ); + + /* constants */ + auto add_constant_to_map = [&]( bool value ) { + const auto n = ntk.get_node( ntk.get_constant( value ) ); + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = dest.get_constant( value ); + break; + + case driver_type::neg: + node_to_signal[n] = dest.get_constant( !value ); + break; + + case driver_type::mixed: + node_to_signal[n] = dest.get_constant( value ); + opposites[n] = dest.get_constant( !value ); + break; + } + }; + + add_constant_to_map( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + add_constant_to_map( true ); + } + + /* primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + signal dest_signal; + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest_signal; + break; + + case driver_type::neg: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest.create_not( dest_signal ); + break; + + case driver_type::mixed: + dest_signal = dest.create_pi(); + node_to_signal[n] = dest_signal; + opposites[n] = dest.create_not( node_to_signal[n] ); + break; + } + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + dest.set_name( dest_signal, ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + + /* ROs & RO names & register information */ + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + std::vector> cis; + ntk.foreach_ro( [&]( auto const& n, auto i ) { + cis.push_back( dest.create_ro() ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + auto const s = ntk.make_signal( n ); + if ( ntk.has_name( s ) ) + { + dest.set_name( cis.back(), ntk.get_name( s ) ); + } + if ( ntk.has_name( !s ) ) + { + dest.set_name( !cis.back(), ntk.get_name( !s ) ); + } + } + dest.set_register( i, ntk.register_at( i ) ); + } ); + + auto it = std::begin( cis ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto n ) { + node_to_signal[n] = *it++; + } ); + } + } + + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view> color_ntk{ fanout_ntk }; + + /* nodes */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) || !ntk.is_cell_root( n ) ) + return; + + std::vector> children; + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + children.push_back( node_to_signal[fanin] ); + } ); + + kitty::dynamic_truth_table tt; + + if constexpr ( has_cell_function_v ) + { + tt = ntk.cell_function( n ); + } + else + { + /* compute function constructing a window */ + std::vector> roots{ n }; + std::vector> leaves; + + ntk.foreach_cell_fanin( n, [&]( auto fanin ) { + leaves.push_back( fanin ); + } ); + + std::vector> gates{ collect_nodes( color_ntk, leaves, roots ) }; + window_view window_ntk{ color_ntk, leaves, roots, gates }; + + using Ntk = mockturtle::window_view>>; + default_simulator sim( window_ntk.num_pis() ); + unordered_node_map node_to_value( window_ntk ); + + simulate_nodes( window_ntk, node_to_value, sim ); + + tt = node_to_value[n]; + } + + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = dest.create_node( children, tt ); + break; + + case driver_type::neg: + node_to_signal[n] = dest.create_node( children, ~tt ); + break; + + case driver_type::mixed: + node_to_signal[n] = dest.create_node( children, tt ); + opposites[n] = dest.create_node( children, ~tt ); + break; + } + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + if ( ntk.is_complemented( f ) && node_driver_type[f] == driver_type::mixed ) + { + dest.create_po( opposites[ntk.get_node( f )] ); + } + else + { + dest.create_po( node_to_signal[f] ); + } + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( ntk.is_complemented( f ) ? dest.create_not( node_to_signal[f] ) : node_to_signal[f] ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } + } + +private: + NtkSource const& ntk; +}; + +} /* namespace detail */ + +/*! \brief Collapse mapped network into k-LUT network. + * + * Collapses a mapped network into a k-LUT network. In the mapped network each + * cell is represented in terms of a collection of nodes from the subject graph. + * This method creates a new network in which each cell is represented by a + * single node. + * + * This function performs some optimizations with respect to possible output + * complementations in the subject graph: + * + * - If an output driver is only used in positive form, nothing changes + * - If an output driver is only used in complemented form, the cell function + * of the node is negated. + * - If an output driver is used in both forms, two nodes will be created for + * the mapped node. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `has_mapping` + * - `get_constant` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_cell_fanin` + * - `is_constant` + * - `is_pi` + * - `is_cell_root` + * - `cell_function` + * - `is_complemented` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_node` + * - `create_not` + */ +template +std::optional collapse_mapped_network( NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_has_mapping_v, "NtkSource does not implement the has_mapping method" ); + static_assert( has_num_gates_v, "NtkSource does not implement the num_gates method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_foreach_cell_fanin_v, "NtkSource does not implement the foreach_cell_fanin method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_cell_root_v, "NtkSource does not implement the is_cell_root method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + if ( !ntk.has_mapping() && ntk.num_gates() > 0 ) + { + return std::nullopt; + } + else + { + detail::collapse_mapped_network_impl p( ntk ); + NtkDest dest; + p.run( dest ); + return dest; + } +} + +/*! \brief Collapse mapped network into k-LUT network. + * + * Collapses a mapped network into a k-LUT network. In the mapped network each + * cell is represented in terms of a collection of nodes from the subject graph. + * This method creates a new network in which each cell is represented by a + * single node. + * + * This function performs some optimizations with respect to possible output + * complementations in the subject graph: + * + * - If an output driver is only used in positive form, nothing changes + * - If an output driver is only used in complemented form, the cell function + * of the node is negated. + * - If an output driver is used in both forms, two nodes will be created for + * the mapped node. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `has_mapping` + * - `get_constant` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_node` + * - `foreach_cell_fanin` + * - `is_constant` + * - `is_pi` + * - `is_cell_root` + * - `cell_function` + * - `is_complemented` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_node` + * - `create_not` + */ +template +bool collapse_mapped_network( NtkDest& dest, NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_has_mapping_v, "NtkSource does not implement the has_mapping method" ); + static_assert( has_num_gates_v, "NtkSource does not implement the num_gates method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_foreach_cell_fanin_v, "NtkSource does not implement the foreach_cell_fanin method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_cell_root_v, "NtkSource does not implement the is_cell_root method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + if ( !ntk.has_mapping() && ntk.num_gates() > 0 ) + { + return false; + } + else + { + detail::collapse_mapped_network_impl p( ntk ); + p.run( dest ); + return true; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/cover_to_graph.hpp b/include/mockturtle/algorithms/cover_to_graph.hpp new file mode 100644 index 0000000..63864cd --- /dev/null +++ b/include/mockturtle/algorithms/cover_to_graph.hpp @@ -0,0 +1,306 @@ +/* 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 cover_to_graph.hpp + \brief transforms a cover data structure into another network type + + \author Andrea Costamagna + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/cover.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct signals_connector +{ + signals_connector() + { + signals.reserve( 10000u ); + } + void insert( signal signal_ntk, uint64_t node_index ) + { + signals[node_index] = signal_ntk; + } + + std::unordered_map> signals; +}; + +/*! \brief cover_to_graph_converter + * This data type is equipped with the main operations involved in the cover to graph conversion. + * Given a cover network its features are mapped into the corresponding ones of a graph using the + * signals_connector for storing the signal of the new network associated to the node index in the cover one. + * The mapping is performed by the convert method, which must be called explicitly to perform the mapping. + */ +template +class cover_to_graph_converter +{ + + using type_cover_signals = std::vector; + +public: + cover_to_graph_converter( Ntk& ntk, const cover_network& cover_ntk ) + : _ntk( ntk ), + _cover_ntk( cover_ntk ) + { + } + +#pragma region recursive functions + /*! \brief recursive_or + * If only one signal is presented the function returns the signal itself. + * If two signals are presented the function returns their disjunction. + * In all other cases recursively split the input signals into two subsets of size differing by at most one. + * These two will give rise to a unique output, which is the OR of two signals coming from the two subgraphs. + * The problem of finding the network of each subgraph presents the same structure as the original problem. + * Therefore, recursion can be performed. + */ + signal recursive_or( const std::vector>& signals ) + { + if ( signals.size() == 0u ) + { + std::cerr << "signals size is zero in recursive or\n"; + return _ntk.get_constant( 0 ); + } + else if ( signals.size() == 1u ) + { + return signals[0]; + } + else if ( signals.size() == 2u ) + { + signal signal_out = _ntk.create_or( signals[0], signals[1] ); + return signal_out; + } + else + { + std::size_t const half_size = signals.size() / 2; + std::vector> vector_l( signals.begin(), signals.begin() + half_size ); + std::vector> vector_r( signals.begin() + half_size, signals.end() ); + + return _ntk.create_or( recursive_or( vector_l ), recursive_or( vector_r ) ); + } + } + + /*! \brief recursive_and + * If only one signal is presented the function returns the signal itself. + * If two signals are presented the function returns their conjunction. + * In all other cases recursively split the input signals into two subsets of size differing by at most one. + * These two will give rise to a unique output, which is the AND of two signals coming from the two subgraphs. + * The problem of finding the network of each subgraph presents the same structure as the original problem. + * Therefore, recursion can be performed. + */ + signal recursive_and( std::vector> const& signals ) + { + if ( signals.size() == 0u ) + { + std::cerr << "signals size is zero in recursive and\n"; + return _ntk.get_constant( 0 ); + } + else if ( signals.size() == 1u ) + { + return signals[0]; + } + else if ( signals.size() == 2u ) + { + signal signal_out = _ntk.create_and( signals[0], signals[1] ); + return signal_out; + } + else + { + std::size_t const half_size = signals.size() / 2; + std::vector> vector_l( signals.begin(), signals.begin() + half_size ); + std::vector> vector_r( signals.begin() + half_size, signals.end() ); + return _ntk.create_and( recursive_and( vector_l ), recursive_and( vector_r ) ); + } + } +#pragma endregion + + /*! \brief convert_cube_to_graph + * Given a node, a cube stored into it and the boolean type associated to the SOP/POS, all children are scanned. + * Unless the cube is independent of the value of the children ( don't care ), the signal is stored in a vector. + * Finally, depending on the bit value of the cube, the signal influencing the cover or their negation are used + * to create the subgraph. This create the products/sums in the SOP/POS. + */ +#pragma region converter functions + signal convert_cube_to_graph( const mockturtle::cover_storage_node& Nde, const kitty::cube& cb, const bool& is_sop ) + { + std::vector> signals; + + for ( auto j = 0u; j < Nde.children.size(); j++ ) + { + if ( cb.get_mask( j ) == 1 ) + { + if ( cb.get_bit( j ) == 1 ) + { + signals.emplace_back( ( is_sop ) ? _connector.signals[Nde.children[j].index] : !_connector.signals[Nde.children[j].index] ); + } + else + { + signals.emplace_back( ( is_sop ) ? !_connector.signals[Nde.children[j].index] : _connector.signals[Nde.children[j].index] ); + } + } + } + + return is_sop ? recursive_and( signals ) : recursive_or( signals ); + } + + /*! \brief convert_node_to_graph + * This helper function receives as input a node, storing the cover information. + * This information corresponds to a vector of cubes and to a boolean determining whether + * the cubes represent the ON set or the OFF set. + * Each cube is mapped into a subgraph and the output signals are collected in a vector, corresponding to the + * products/sums of the SOP/POS. + * Depending on the boolean, the SOP/POS is finally performed using the recursive OR/AND. + */ + signal convert_node_to_graph( const mockturtle::cover_storage_node& Nde ) + { + auto& cbs = _cover_ntk._storage->data.covers[Nde.data[1].h1].first; + + std::vector> signals_internal; + bool is_sop = _cover_ntk._storage->data.covers[Nde.data[1].h1].second; + + for ( auto const& cb : cbs ) + { + signals_internal.emplace_back( convert_cube_to_graph( Nde, cb, is_sop ) ); + } + + return ( is_sop ? recursive_or( signals_internal ) : recursive_and( signals_internal ) ); + } + + Ntk get_network() + { + return _ntk; + } + + /*! \brief convert + * This method combines the helper functions and performs the mapping of a cover network into the desired graph. + */ + void run() + { + /* convert the pi */ + for ( auto const& inpt : _cover_ntk._storage->inputs ) + { + _connector.insert( _ntk.create_pi(), inpt ); + } + + /* convert the nodes */ + for ( auto const& nde : _cover_ntk._storage->nodes ) + { + uint64_t index = _cover_ntk._storage->hash[nde]; + bool condition1 = ( std::find( _cover_ntk._storage->inputs.begin(), _cover_ntk._storage->inputs.end(), index ) != _cover_ntk._storage->inputs.end() ); + bool condition2 = nde.data[1].h1 == 0 || nde.data[1].h1 == 1; + + /* convert only the nodes that are neither inputs nor constants */ + if ( !condition1 && !condition2 ) + { + _connector.insert( convert_node_to_graph( nde ), _cover_ntk._storage->hash[nde] ); + } /* convert separately the constant 0 */ + else if ( nde.data[1].h1 == 0 ) + { + _connector.insert( _ntk.get_constant( false ), _cover_ntk._storage->hash[nde] ); + } /* convert separately the constant 1 */ + else if ( nde.data[1].h1 == 1 ) + { + _connector.insert( _ntk.get_constant( true ), _cover_ntk._storage->hash[nde] ); + } + } + + /* convert the outputs */ + for ( const auto& outpt : _cover_ntk._storage->outputs ) + { + _ntk.create_po( _connector.signals[outpt.index] ); + } + } + +private: + Ntk& _ntk; + cover_network const& _cover_ntk; + signals_connector _connector; +}; + +} /* namespace detail */ + +/*! \brief Inline convert a `cover_network` into another network type. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_buf` + * - `create_not` + * + * \param cover_ntk Input network of type `cover_network`. + * \param ntk Output network of type `Ntk`. + */ +template +void convert_cover_to_graph( Ntk& ntk, const cover_network& cover_ntk ) +{ + static_assert( has_create_and_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_or_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_buf_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + detail::cover_to_graph_converter converter( ntk, cover_ntk ); + converter.run(); +} + +/*! \brief Out-of-place convert a `cover_network` into another network type. + * + * **Required network functions:** + * - `create_and` + * - `create_or` + * - `create_buf` + * - `create_not` + * + * \param cover_ntk Input network of type `cover_network`. + * \return ntk Output network of type `Ntk`. + */ +template +Ntk convert_cover_to_graph( const cover_network& cover_ntk ) +{ + static_assert( has_create_and_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_or_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_buf_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + + Ntk ntk; + detail::cover_to_graph_converter converter( ntk, cover_ntk ); + converter.run(); + return converter.get_network(); +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/cut_enumeration.hpp b/include/mockturtle/algorithms/cut_enumeration.hpp new file mode 100644 index 0000000..89a4dcf --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration.hpp @@ -0,0 +1,1840 @@ +/* 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 cut_enumeration.hpp + \brief Cut enumeration + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Sahand Kashani-Akhavan + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "../traits.hpp" +#include "../utils/cuts.hpp" +#include "../utils/mixed_radix.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/truth_table_cache.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for cut_enumeration. + * + * The data structure `cut_enumeration_params` holds configurable parameters + * with default arguments for `cut_enumeration`. + */ +struct cut_enumeration_params +{ + /*! \brief Maximum number of leaves for a cut. */ + uint32_t cut_size{ 4u }; + + /*! \brief Maximum number of cuts for a node. */ + uint32_t cut_limit{ 25u }; + + /*! \brief Maximum number of fan-ins for a node. */ + uint32_t fanin_limit{ 10u }; + + /*! \brief Prune cuts by removing don't cares. */ + bool minimize_truth_table{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +struct cut_enumeration_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for truth table computation. */ + stopwatch<>::duration time_truth_table{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] truth table time = {:>5.2f} secs\n", to_seconds( time_truth_table ) ); + } +}; + +static constexpr uint32_t max_cut_size = 16; + +template +struct cut_data; + +template +struct cut_data +{ + uint32_t func_id; + T data; +}; + +template +struct cut_data +{ + T data; +}; + +template +using cut_type = cut>; + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct network_cuts; + +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +/* function to update a cut */ +template +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + (void)cut; + (void)cuts; + (void)ntk; + (void)n; + } +}; + +namespace detail +{ +template +class cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * An instance of type `network_cuts` can only be constructed from the + * `cut_enumeration` algorithm. + */ +template +struct network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 26; + using cut_t = cut_type; + using cut_set_t = cut_set; // std::vector // cut_type = cut>; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::dynamic_truth_table zero( 0u ), proj( 1u ); + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::dynamic_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::cut_enumeration_impl; + + template + friend network_cuts<_Ntk, _ComputeTruth, _CutData> cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class cut_enumeration_impl +{ +public: + using cut_t = typename network_cuts::cut_t; + using cut_set_t = typename network_cuts::cut_set_t; + + explicit cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = kitty::extend_to( cuts._truth_tables[( *cut )->func_id], res.size() ); + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + auto tt_res_shrink = shrink_to( tt_res, static_cast( support.size() ) ); + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + return cuts._truth_tables.insert( tt_res_shrink ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, ps.cut_size ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, ps.cut_size ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::dynamic_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired buy cut + enumeration implementations in ABC. + \endverbatim + */ +template +network_cuts cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + network_cuts res( ntk.size() ); + detail::cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct fast_network_cuts; + +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps = {}, cut_enumeration_stats* pst = nullptr ); + +namespace detail +{ +template +class fast_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Cut database for a network. + * + * The function `cut_enumeration` returns an instance of type `fast_network_cuts` + * which contains a cut database and can be queried to return all cuts of a + * node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `fast_network_cuts` can only be constructed from the + * `fast_cut_enumeration` algorithm. + */ +template +struct fast_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 50; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +private: + explicit fast_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) { return _cuts[node_index]; } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const { return _cuts[node_index]; } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::fast_cut_enumeration_impl; + + template + friend fast_network_cuts<_Ntk, _NumVars, _ComputeTruth, _CutData> fast_cut_enumeration( _Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ); + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + +private: + /* compressed representation of cuts */ + std::vector _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class fast_cut_enumeration_impl +{ +public: + using cut_t = typename fast_network_cuts::cut_t; + using cut_set_t = typename fast_network_cuts::cut_set_t; + + explicit fast_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, fast_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + +private: + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit - 1 ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + fast_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +/*! \brief Fast cut enumeration. + * + * This function implements the cut enumeration algorithm. The algorithm + * traverses all nodes in topological order and computes a node's cuts based + * on its fanins' cuts. Dominated cuts are filtered and are not added to the + * cut set. For each node a unit cut is added to the end of each cut set. + * + * The template parameter `ComputeTruth` controls whether truth tables should + * be computed for each cut. Computing truth tables slows down the execution + * time of the algorithm. + * + * The cut size is controlled using the template parameter `NumVars` instead + * of the `cut_size` parameter as in `cut_enumeration`. + * + * Comparing to `cut_enumeration`, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * The number of computed cuts is controlled via the `cut_limit` parameter. + * To decide which cuts are collected in each node's cut set, cuts are sorted. + * Unit cuts do not participate in the sorting and are always added to the end + * of each cut set. + * + * The algorithm can be configured by specifying the template argument `CutData` + * which holds the application specific data assigned to each cut. Examples + * on how to specify custom cost functions for sorting cuts based on the + * application specific cut data can be found in the files contained in the + * directory `include/mockturtle/algorithms/cut_enumeration`. + * + * **Required network functions:** + * - `is_constant` + * - `is_ci` + * - `size` + * - `get_node` + * - `node_to_index` + * - `foreach_node` + * - `foreach_fanin` + * - `compute` for `kitty::static_truth_table` (if `ComputeTruth` is true) + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes + one can wrap the network parameter in a ``topo_view`` view. + + .. note:: + + The implementation of this algorithm was heavily inspired by cut + enumeration implementations in ABC. + \endverbatim + */ +template +fast_network_cuts fast_cut_enumeration( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats* pst ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( !ComputeTruth || has_compute_v, "Ntk does not implement the compute method for kitty::dynamic_truth_table" ); + + cut_enumeration_stats st; + fast_network_cuts res( ntk.size() ); + detail::fast_cut_enumeration_impl p( ntk, ps, st, res ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return res; +} + +/* forward declarations */ +/*! \cond PRIVATE */ +template +struct dynamic_network_cuts; + +namespace detail +{ +template +class dynamic_cut_enumeration_impl; +} +/*! \endcond */ + +/*! \brief Dynamic cut database for a network. + * + * Struct `dynamic_network_cuts` contains a cut database and can be queried + * to return all cuts of a node, or the function of a cut (if it was computed). + * + * Comparing to `network_cuts`, it supports dynamic allocation of cuts for + * networks in expansion. Moreover, it uses static truth tables instead of + * dynamic truth tables to speed-up the truth table computation. + * + * An instance of type `dynamic_network_cuts` can only be constructed from the + * `dynamic_cut_enumeration_impl` algorithm. + */ +template +struct dynamic_network_cuts +{ +public: + static constexpr uint32_t max_cut_num = 16u; + using cut_t = cut_type; + using cut_set_t = cut_set; + static constexpr bool compute_truth = ComputeTruth; + +public: + explicit dynamic_network_cuts( uint32_t size ) : _cuts( size ) + { + kitty::static_truth_table zero, proj; + kitty::create_nth_var( proj, 0u ); + + _truth_tables.insert( zero ); + _truth_tables.insert( proj ); + } + +public: + /*! \brief Returns the cut set of a node */ + cut_set_t& cuts( uint32_t node_index ) + { + if ( node_index >= _cuts.size() ) + _cuts.resize( node_index + 1 ); + + return _cuts[node_index]; + } + + /*! \brief Returns the cut set of a node */ + cut_set_t const& cuts( uint32_t node_index ) const + { + assert( node_index < _cuts.size() ); + return _cuts[node_index]; + } + + /*! \brief Returns the truth table of a cut */ + template && enabled>> + auto truth_table( cut_t const& cut ) const + { + return _truth_tables[cut->func_id]; + } + + /*! \brief Returns the total number of tuples that were tried to be merged */ + auto total_tuples() const + { + return _total_tuples; + } + + /*! \brief Returns the total number of cuts in the database. */ + auto total_cuts() const + { + return _total_cuts; + } + + /*! \brief Returns the number of nodes for which cuts are computed */ + auto nodes_size() const + { + return _cuts.size(); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + std::vector compute_truth_table_support( cut_t const& sub, cut_t const& sup ) const + { + std::vector support; + support.reserve( sub.size() ); + + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support.push_back( static_cast( std::distance( sup.begin(), itp ) ) ); + } + + return support; + } + + /*! \brief Inserts a truth table into the truth table cache. + * + * This message can be used when manually adding or modifying cuts from the + * cut sets. + * + * \param tt Truth table to add + * \return Literal id from the truth table store + */ + uint32_t insert_truth_table( kitty::static_truth_table const& tt ) + { + return _truth_tables.insert( tt ); + } + +private: + template + friend class detail::dynamic_cut_enumeration_impl; + +private: + void add_zero_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( ComputeTruth ) + { + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = _cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( ComputeTruth ) + { + cut->func_id = 2; + } + } + + void clear_cut_set( uint32_t index ) + { + _cuts[index].clear(); + } + +private: + /* compressed representation of cuts */ + std::deque _cuts; + + /* cut truth tables */ + truth_table_cache> _truth_tables; + + /* statistics */ + uint32_t _total_tuples{}; + std::size_t _total_cuts{}; +}; + +/*! \cond PRIVATE */ +namespace detail +{ +template +class dynamic_cut_enumeration_impl +{ +public: + using cut_t = typename dynamic_network_cuts::cut_t; + using cut_set_t = typename dynamic_network_cuts::cut_set_t; + + explicit dynamic_cut_enumeration_impl( Ntk const& ntk, cut_enumeration_params const& ps, cut_enumeration_stats& st, dynamic_network_cuts& cuts ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cuts ) + { + assert( ps.cut_limit < cuts.max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + } + +public: + void run() + { + stopwatch t( st.time_total ); + + ntk.foreach_node( [this]( auto node ) { + const auto index = ntk.node_to_index( node ); + + if ( ps.very_verbose ) + { + std::cout << fmt::format( "[i] compute cut for node at index {}\n", index ); + } + + if ( ntk.is_constant( node ) ) + { + cuts.add_zero_cut( index ); + } + else if ( ntk.is_ci( node ) ) + { + cuts.add_unit_cut( index ); + } + else + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + } ); + } + + void compute_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + + if ( cuts.cuts( index ).size() > 0 ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_cuts( ntk.get_node( f ) ); + } ); + + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( index ); + } + else + { + merge_cuts( index ); + } + } + + void init_cuts() + { + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + cuts.add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ) ); + ntk.foreach_ci( [&]( auto const& n ) { + cuts.add_unit_cut( ntk.node_to_index( n ) ); + } ); + } + + void clear_cuts( node const& n ) + { + const auto index = ntk.node_to_index( n ); + if ( cuts.cuts( index ).size() == 0 ) + return; + + cuts.clear_cut_set( index ); + } + +private: + inline bool fast_support_minimization( kitty::static_truth_table const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + stopwatch t( st.time_truth_table ); + + std::vector> tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = cuts._truth_tables[( *cut )->func_id]; + const auto supp = cuts.compute_truth_table_support( *cut, res ); + kitty::expand_inplace( tt[i], supp ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + } + + return cuts._truth_tables.insert( tt_res ); + } + + void merge_cuts2( uint32_t index ) + { + const auto fanin = 2; + + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts.cuts( index ); + auto& rcuts = *lcuts[fanin]; + rcuts.clear(); + + cut_t new_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, NumVars ) ) + { + continue; + } + + if ( rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( ComputeTruth ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, index ); + + rcuts.insert( new_cut ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + + cuts._total_cuts += rcuts.size(); + + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + cuts.add_unit_cut( index ); + } + } + + void merge_cuts( uint32_t index ) + { + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts.cuts( ntk.node_to_index( ntk.get_node( child ) ) ); + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts.cuts( index ); + + auto& rcuts = *lcuts[fanin]; + + if ( fanin > 1 && fanin <= ps.fanin_limit ) + { + rcuts.clear(); + + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + cuts._total_tuples += pairs; + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, NumVars ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, NumVars ) ) + { + return true; /* continue */ + } + } + + if ( rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + else if ( fanin == 1 ) + { + rcuts.clear(); + + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( ComputeTruth ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + cut_enumeration_update_cut::apply( new_cut, cuts, ntk, ntk.index_to_node( index ) ); + + rcuts.insert( new_cut ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_limit ); + } + + cuts._total_cuts += static_cast( rcuts.size() ); + + cuts.add_unit_cut( index ); + } + +private: + Ntk const& ntk; + cut_enumeration_params const& ps; + cut_enumeration_stats& st; + dynamic_network_cuts& cuts; + + std::array lcuts; +}; +} /* namespace detail */ +/*! \endcond */ + +// This function expects to receive a network where nodes are sorted in +// topological order. Cuts are represented as a 64-bit bit vector where each bit +// determines whether a given node exists in the cut. + +/*! \brief Cut enumeration. + * + * This function implements a generic fast cut enumeration algorithm for graphs + * containing at most 64 nodes. It is generic as it supports graphs in which + * nodes can have variable fan-in. Speed and space-efficiency are achieved by + * representing cuts as 64-bit bit vectors, i.e. each bit represents whether or + * not a node is in a cut. Cut-set union and domination operations then + * transform into bitwise operations that can be performed in a few clock cycles + * each. + * + * Like the larger cut_enumeration algorithm, this algorithm traverses all nodes + * in topological order and computes a node's cuts based on its fanins' cuts. + * Dominated cuts are filtered and are not added to the cut set. For each node a + * unit cut is added to the end of each cut set. + * + * This function computes all cuts of the network (i.e. the number of generated + * cuts is not bounded). Though the number of cuts cannot be bounded, their size + * can be bound by passing a `cut_size` argument to the function. + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_gate` + * - `foreach_ci` + * - `get_node` + * - `node_to_index` + * - `size` + * + * Note that this algorithm *only* works for graphs with at most 64 nodes. + * However, since we cannot know the size of a graph at compile-time, this + * function returns the results wrapped in an std::optional. + * + \verbatim embed:rst + + .. warning:: + + This algorithm expects the nodes in the network to be in topological + order. If the network does not guarantee a topological order of nodes one + can wrap the network parameter in a ``topo_view`` view. + \endverbatim + */ +template +std::optional>> +fast_small_cut_enumeration( Ntk const& ntk, const uint8_t cut_size = 4 ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + // You cannot check whether a graph is topologically sorted at compile-time, + // but the is_topologically_sorted_v template is used inside the + // topo_view class to determine whether the input graph should just be copied + // as it is already topologically-sorted, or whether the graph's topological + // order is to be computed. + // static_assert( is_topologically_sorted_v, "Ntk is not a topologically-sorted network" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + // Max 64 nodes, so 8 bits are enough for indices. + using node_idx_t = uint8_t; + using cut_t = uint64_t; + using cut_set_t = std::vector; + using cut_sets_t = std::vector; + + // It is not possible to know the size of a network at compile-time, so I will + // return a boolean flag stating whether the cut-sets returned by this + // function are valid or not. + constexpr node_idx_t max_nodes = 64; + if ( ntk.size() > max_nodes ) + { + return std::nullopt; + } + + ////////////////////////////////////////////////////////////////////////////// + // Final cut-sets to be computed ///////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // size() returns the number of nodes including constants, PIs, and dead + // nodes, so no need to allocate +1 memory slots like in the lecture notes + // to explicitly represent constants. + // By definition of the vector constructor, each cut-set is initialized to {}. + cut_sets_t cut_sets( ntk.size() ); + + ////////////////////////////////////////////////////////////////////////////// + // Helper functions ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + auto set_bit = []( + node_idx_t idx ) { + return static_cast( 1 ) << idx; + }; + + // Algorithm for counting #1 bits in a uint64_t. It is efficient as it only + // iterates as many times as the bit count to avoid always performing 64 + // iterations. Inspired from the following threads: + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive + // https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation + auto bit_cnt = []( + cut_t n ) { + uint8_t count = 0; + + while ( n > 0 ) + { + count = count + 1; + n = n & ( n - 1 ); + } + + return count; + }; + + // Operation to perform on each n-tuple. In the cut generation algorithm this + // involves computing a new cut from the fan-in nodes' selected cuts, checking + // whether an existing cut dominates it, and if not, adding it to the cutset + // of the current node. + auto visit_n_tuple = [&cut_sets, &bit_cnt, &cut_size]( + // Node for which we are computing the cut. + node_idx_t node_idx, + // Fan-in of the current node. + std::vector const& fanin_idx, + // Index of the cut to choose from the given fan-in node. + std::vector const& cut_idx ) { + // Compute new cut. + cut_t C = 0; + for ( auto i = 0U; i < fanin_idx.size(); i++ ) + { + C |= cut_sets.at( fanin_idx[i] ).at( cut_idx[i] ); + } + + // Restrict cut sizes if too large. + if ( bit_cnt( C ) > cut_size ) + { + return; + } + + // Don't add new cut if existing cut dominates it. A cut C' dominates + // another cut C if C' is a subset of C. Being a subset means that C' has at + // most the same bits set as C, but no more bits. + // + // So if we AND their bitsets together, we can see what they have in common, + // then we can XOR this with C' original bits to see if C' has any bit + // active that C does not. + // + // C' = 0b 00110 + // C = 0b 01010 AND + // -------- + // 0b 00010 + // C' = 0b 00110 XOR + // -------- + // 0b 00100 => C' does NOT dominate C as it contains a node that C does not. + for ( auto C_prime : cut_sets.at( node_idx ) ) + { + cut_t shared_nodes = C_prime & C; + cut_t C_prime_extra_nodes = shared_nodes ^ C_prime; + + bool C_prime_dominates_C = C_prime_extra_nodes == 0; + if ( C_prime_dominates_C ) + { + return; + } + } + + cut_sets.at( node_idx ).push_back( C ); + }; + + // Enumerates cuts of a given node. The inputs of the node can have variable + // fan-in, so the cut-sets they have could have different sizes. We therefore + // cannot use N nested for-loops to perform a cross product of the fan-in cuts + // since we don't know the cut-set sizes in advance. We instead use the + // mixed-radix n-tuple generation algorithm in TAOCP, Vol 4A, algorithm M. + auto cut_enumeration_node = [&cut_sets, &visit_n_tuple]( + Ntk const& ntk, + node const& node ) { + node_idx_t node_idx = ntk.node_to_index( node ); + + // Index of the node's fan-ins. + std::vector fanin_idx; + + // Number of cuts of a given fan-in node ("radix" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_set_size; // radix (m[n-1], ... , m[0]) in TAOCP + + // Index of a cut of a given fan-in node ("value" in TAOCP, Vol 4A, Algorithm M). + std::vector cut_idx; // value (a[n-1], ... , a[0]) in TAOCP + + // We start by initializing the indices of the fan-in nodes' cuts and their + // radix. Need to get the the index of the fan-in nodes for this so we can + // query their number of cuts. + ntk.foreach_fanin( + node, + [&]( auto sig ) { + auto fanin_node = ntk.get_node( sig ); + auto fanin_node_idx = ntk.node_to_index( fanin_node ); + + fanin_idx.push_back( fanin_node_idx ); + // Radix of the given fan-in is determined by the number of cuts it has. + cut_set_size.push_back( cut_sets.at( fanin_node_idx ).size() ); + // Start counting from (0, 0, ... , 0) + cut_idx.push_back( 0 ); + } ); + + // Fan-in = number of cut-sets we must perform a cross-product over. + auto const num_cut_sets = ntk.fanin_size( node ); + + uint8_t j = 0; + while ( j != num_cut_sets ) + { + visit_n_tuple( node_idx, fanin_idx, cut_idx ); + + // Mixed-radix n-tuple generation algorithm. Adding 1 to the n-tuple. + j = 0; + while ( ( j != num_cut_sets ) && ( cut_idx.at( j ) == cut_set_size.at( j ) - 1 ) ) + { + cut_idx.at( j ) = 0; + j += 1; + } + if ( j != num_cut_sets ) + { + cut_idx.at( j ) += 1; + } + } + }; + + ////////////////////////////////////////////////////////////////////////////// + // Main algorithm //////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + // Primary inputs only have themselves as their cut-set. + ntk.foreach_ci( + [&]( auto node ) { + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = { set_bit( idx ) }; + } ); + + // Going through remaining gates (excluding constants and primary inputs). + ntk.foreach_gate( + [&]( auto node ) { + // Technically don't need to do this as vectors are constructed with zero + // length, but we leave it for clarity. + auto const idx = ntk.node_to_index( node ); + cut_sets.at( idx ) = {}; + + // Internally uses TAOCP Vol 4A algorithm M, mixed-radix n-tuple + // generation, to enumerate the cross-product of the node's fan-in + // cut-sets. + cut_enumeration_node( ntk, node ); + + cut_sets.at( idx ).push_back( set_bit( idx ) ); + } ); + + return cut_sets; +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp new file mode 100644 index 0000000..17ce75d --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/cnf_cut.hpp @@ -0,0 +1,109 @@ +/* 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 cnf_cut.hpp + \brief Cut enumeration for CNF mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Cut for CNF mapping applications. + + This cut type uses the clause count in the CNF encoding of the cut function + as cost function. It requires truth table computation during cut enumeration + or LUT mapping in order to work. +*/ +struct cut_enumeration_cnf_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + float cost{ 0 }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + auto tt = cuts.truth_table( cut ); + auto cnf = kitty::cnf_characteristic( tt ); + cut->data.cost = cnf; + float flow = cut.size() < 2 ? 0.0f : 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp new file mode 100644 index 0000000..2404bf2 --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp @@ -0,0 +1,100 @@ +/* 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 exact_map_cut.hpp + \brief Cut enumeration for mapping with exact synthesis + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for graph mapping with a complete database */ +struct cut_enumeration_exact_map_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + uint8_t match_index{ 0 }; + bool ignore{ false }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp new file mode 100644 index 0000000..9ad0126 --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/gia_cut.hpp @@ -0,0 +1,82 @@ +/* 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 gia_cut.hpp + \brief Cut enumeration as in giaCut.c + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation based on ABC's giaCut.c + + See giaCut.c in ABC's repository. +*/ +struct cut_enumeration_gia_cut +{ + uint32_t num_tree_leaves; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + if ( c1->data.num_tree_leaves < c2->data.num_tree_leaves ) + { + return true; + } + if ( c1->data.num_tree_leaves > c2->data.num_tree_leaves ) + { + return false; + } + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + (void)n; + (void)cuts; + cut->data.num_tree_leaves = std::count_if( cut.begin(), cut.end(), + [&ntk]( auto index ) { + return ntk.fanout_size( index ) == 1; + } ); + } +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp new file mode 100644 index 0000000..ebaee4b --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp @@ -0,0 +1,102 @@ +/* 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 mf_cut.hpp + \brief Cut enumeration for MF mapping (see giaMf.c) + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation based on ABC's giaMf.c + + See giaMf.c in ABC's repository. +*/ +struct cut_enumeration_mf_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + float cost{ 0 }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = cut->data.cost = cut.size() < 2 ? 0.0f : 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp new file mode 100644 index 0000000..04a8226 --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/rewrite_cut.hpp @@ -0,0 +1,88 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file rewrite_cut.hpp + \brief Cut enumeration for rewriting + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for rewrite */ +struct cut_enumeration_rewrite_cut +{ + uint32_t cost; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + if ( c1->data.cost < c2->data.cost ) + return true; + if ( c1->data.cost > c2->data.cost ) + return false; + return c1.size() < c2.size(); +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t value = 0; + + for ( auto leaf : cut ) + { + value += ( ntk.fanout_size( ntk.index_to_node( leaf ) ) == 1 ) ? 1u : 0u; + } + + cut->data.cost = value; + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, C = " << std::setw( 3 ) << c->data.cost; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp new file mode 100644 index 0000000..d14dfac --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/spectr_cut.hpp @@ -0,0 +1,197 @@ +/* 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 spectr_cut.hpp + \brief Cut enumeration based on spectral properties of a function + + \author Giulia Meuli + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../../utils/cuts.hpp" +#include "../cut_enumeration.hpp" +#include "../lut_mapping.hpp" +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Cut based on spectral properties. + + This cut type uses the number of non-zero coefficients in the cut function as + cost function. It requires truth table computation during cut enumeration + or LUT mapping in order to work. +*/ +struct cut_enumeration_spectr_cut +{ + uint32_t delay{ 0u }; + float flow{ 0.0f }; + float cost{ 0.0f }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + + if ( c1.size() == c2.size() ) + { + if ( c1->data.cost < c2->data.cost ) + return true; + if ( c1->data.cost > c2->data.cost ) + return false; + } + if ( c1.size() > c2.size() && c1->data.cost < c2->data.cost ) + { + return false; + } + if ( c1.size() < c2.size() && c1->data.cost > c2->data.cost ) + { + return true; + } + if ( c1->data.flow < c2->data.flow - eps ) + return true; + if ( c1->data.flow > c2->data.flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() > c2.size(); +} + +template<> +struct lut_mapping_update_cuts +{ + + template + static void grow_xor_cut( Ntk const& ntk, node const& n, std::map>& node_to_cut ) + { + ntk.foreach_fanin( n, [&]( auto ch ) { + auto ch_node = ntk.get_node( ch ); + if ( ntk.is_xor( ch_node ) ) + { + auto leaves_ch = node_to_cut[ch_node]; + + if ( leaves_ch.size() + node_to_cut[n].size() < max_cut_size ) + { + node_to_cut[n].insert( node_to_cut[n].end(), leaves_ch.begin(), leaves_ch.end() ); + } + else + { + node_to_cut[n].push_back( ch_node ); + } + } + else + { + node_to_cut[n].push_back( ch_node ); + } + } ); + + std::stable_sort( node_to_cut[n].begin(), node_to_cut[n].end() ); + node_to_cut[n].erase( unique( node_to_cut[n].begin(), node_to_cut[n].end() ), node_to_cut[n].end() ); + } + + template + static void apply( NetworkCuts& cuts, Ntk const& ntk ) + { + + std::map> node_to_cut; + + topo_view( ntk ).foreach_node( [&]( auto n ) { + if ( ntk.is_xor( n ) ) + { + const auto index = ntk.node_to_index( n ); + auto& cut_set = cuts.cuts( index ); + + /* clear the cut set of the node */ + cut_set.clear(); + + /* add an empty cut and modify its leaves */ + grow_xor_cut( ntk, n, node_to_cut ); + + auto& my_cut = cut_set.add_cut( node_to_cut[n].begin(), node_to_cut[n].end() ); + + assert( node_to_cut[n].size() <= 16 ); + /* set to zero cost */ + my_cut->data.cost = 0u; + + /* crate cut truth table */ + kitty::dynamic_truth_table tt( node_to_cut[n].size() ); + kitty::create_parity( tt ); + my_cut->func_id = cuts.insert_truth_table( tt ); + } + } ); + } +}; + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + + auto tt = cuts.truth_table( cut ); + auto spectrum = kitty::rademacher_walsh_spectrum( tt ); + cut->data.cost = std::count_if( spectrum.begin(), spectrum.end(), []( auto s ) { return s != 0; } ); + + float flow = cut.size() < 2 ? 0.0f : 1.0f; + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp b/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp new file mode 100644 index 0000000..4702a66 --- /dev/null +++ b/include/mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp @@ -0,0 +1,100 @@ +/* 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 tech_map_cut.hpp + \brief Cut enumeration for technology mapping + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../cut_enumeration.hpp" + +namespace mockturtle +{ + +/*! \brief Cut implementation for technology mapping */ +struct cut_enumeration_tech_map_cut +{ + uint32_t delay{ 0 }; + float flow{ 0 }; + uint8_t match_index{ 0 }; + bool ignore{ false }; +}; + +template +bool operator<( cut_type const& c1, cut_type const& c2 ) +{ + constexpr auto eps{ 0.005f }; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1->data.flow < c2->data.flow - eps; +} + +template<> +struct cut_enumeration_update_cut +{ + template + static void apply( Cut& cut, NetworkCuts const& cuts, Ntk const& ntk, node const& n ) + { + uint32_t delay{ 0 }; + float flow = 1.0f; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts.cuts( leaf )[0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + flow += best_leaf_cut->data.flow; + } + + cut->data.delay = 1 + delay; + cut->data.flow = flow / ntk.fanout_size( n ); + } +}; + +template +std::ostream& operator<<( std::ostream& os, cut> const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}, D = " << std::setw( 3 ) << c->data.delay << " A = " << c->data.flow; + return os; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/cut_rewriting.hpp b/include/mockturtle/algorithms/cut_rewriting.hpp new file mode 100644 index 0000000..85b1bb8 --- /dev/null +++ b/include/mockturtle/algorithms/cut_rewriting.hpp @@ -0,0 +1,870 @@ +/* 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 cut_rewriting.hpp + \brief Cut rewriting + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cut_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "detail/mffc_utils.hpp" +#include "dont_cares.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for cut_rewriting. + * + * The data structure `cut_rewriting_params` holds configurable parameters with + * default arguments for `cut_rewriting`. + */ +struct cut_rewriting_params +{ + cut_rewriting_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 12; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Cut enumeration parameters. */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Allow zero-gain substitutions. */ + bool allow_zero_gain{ false }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Candidate selection strategy. */ + enum + { + minimize_weight, + greedy + } candidate_selection_strategy = minimize_weight; + + /*! \brief Minimum candidate cut size */ + uint32_t min_cand_cut_size{ 3u }; + + /*! \brief Minimum candidate cut size override (in conflict graph) */ + std::optional min_cand_cut_size_override{}; + + /*! \brief If true, candidates are only accepted if they do not increase logic level of node. */ + bool preserve_depth{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +/*! \brief Statistics for cut_rewriting. + * + * The data structure `cut_rewriting_stats` provides data collected by running + * `cut_rewriting`. + */ +struct cut_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Runtime for cut enumeration. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for rewriting. */ + stopwatch<>::duration time_rewriting{ 0 }; + + /*! \brief Runtime to find minimal independent set. */ + stopwatch<>::duration time_mis{ 0 }; + + void report( bool show_time_mis = true ) const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] cut enum. time = {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( "[i] rewriting time = {:>5.2f} secs\n", to_seconds( time_rewriting ) ); + if ( show_time_mis ) + { + fmt::print( "[i] ind. set time = {:>5.2f} secs\n", to_seconds( time_mis ) ); + } + } +}; + +namespace detail +{ + +class graph +{ +public: + auto add_vertex( uint32_t weight ) + { + auto index = _weights.size(); + _weights.emplace_back( weight ); + _adjacent.emplace_back(); + ++_num_vertices; + return index; + } + + void add_edge( uint32_t v1, uint32_t v2 ) + { + if ( v1 == v2 ) + return; + if ( _adjacent[v1].count( v2 ) ) + return; + + _adjacent[v1].insert( v2 ); + _adjacent[v2].insert( v1 ); + ++_num_edges; + } + + void remove_vertex( uint32_t vertex ) + { + assert( _weights[vertex] != -1 ); + _weights[vertex] = -1; + + _num_edges -= _adjacent[vertex].size(); + + for ( auto w : _adjacent[vertex] ) + { + _adjacent[w].erase( vertex ); + } + _adjacent[vertex].clear(); + + --_num_vertices; + } + + bool has_vertex( uint32_t vertex ) const + { + return _weights[vertex] >= 0; + } + + template + void foreach_adjacent( uint32_t vertex, Fn&& fn ) const + { + std::for_each( _adjacent[vertex].begin(), _adjacent[vertex].end(), fn ); + } + + template + void foreach_vertex( Fn&& fn ) const + { + for ( auto i = 0u; i < _weights.size(); ++i ) + { + if ( has_vertex( i ) ) + { + fn( i ); + } + } + } + + auto degree( uint32_t vertex ) const { return _adjacent[vertex].size(); } + auto weight( uint32_t vertex ) const { return _weights[vertex]; } + auto gwmin_value( uint32_t vertex ) const { return (double)weight( vertex ) / ( degree( vertex ) + 1 ); } + auto gwmax_value( uint32_t vertex ) const { return (double)weight( vertex ) / ( degree( vertex ) * ( degree( vertex ) + 1 ) ); } + + auto num_vertices() const { return _num_vertices; } + auto num_edges() const { return _num_edges; } + +private: + uint32_t _num_vertices{ 0u }; + std::size_t _num_edges{ 0u }; + + std::vector> _adjacent; + + std::vector _weights; /* degree = -1 means vertex is removed */ +}; + +inline std::vector maximum_weighted_independent_set_gwmin( graph& g ) +{ + std::vector mwis; + + std::vector vertices( g.num_vertices() ); + std::iota( vertices.begin(), vertices.end(), 0 ); + + std::stable_sort( vertices.begin(), vertices.end(), [&g]( auto v, auto w ) { + const auto value_v = g.gwmin_value( v ); + const auto value_w = g.gwmin_value( w ); + return value_v > value_w || ( value_v == value_w && g.degree( v ) > g.degree( w ) ); + } ); + + for ( auto i : vertices ) + { + if ( !g.has_vertex( i ) ) + continue; + + /* add vertex to independent set, then remove it and all its neighbors */ + mwis.emplace_back( i ); + std::vector neighbors; + g.foreach_adjacent( i, [&]( auto v ) { neighbors.emplace_back( v ); } ); + g.remove_vertex( i ); + + for ( auto v : neighbors ) + { + g.remove_vertex( v ); + } + } + + return mwis; +} + +inline std::vector maximal_weighted_independent_set( graph& g ) +{ + std::vector mwis; + + auto num_vertices = g.num_vertices(); + for ( auto i = 0u; i < num_vertices; ++i ) + { + if ( !g.has_vertex( i ) ) + continue; + + /* add vertex to independent set, then remove it and all its neighbors */ + mwis.emplace_back( i ); + std::vector neighbors; + g.foreach_adjacent( i, [&]( auto v ) { neighbors.emplace_back( v ); } ); + g.remove_vertex( i ); + + for ( auto v : neighbors ) + { + g.remove_vertex( v ); + } + } + + return mwis; +} + +struct cut_enumeration_cut_rewriting_cut +{ + int32_t gain{ -1 }; +}; + +template +std::tuple, uint32_t>>> network_cuts_graph( Ntk const& ntk, network_cuts const& cuts, cut_rewriting_params const& ps ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + + graph g; + + using cut_addr = std::pair, uint32_t>; + std::vector> conflicts( cuts.nodes_size() ); + std::vector vertex_to_cut_addr; + std::vector> cut_addr_to_vertex( cuts.nodes_size() ); + + ntk.clear_visited(); + + ntk.foreach_node( [&]( auto const& n, auto index ) { + if ( index >= cuts.nodes_size() || ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; + + if ( mffc_size( ntk, n ) == 1 ) + return; + + const auto& set = cuts.cuts( ntk.node_to_index( n ) ); + + auto cctr{ 0u }; + for ( auto const& cut : set ) + { + if ( ps.min_cand_cut_size_override ) + { + if ( cut->size() < *ps.min_cand_cut_size_override ) + continue; + } + else if ( cut->size() < ps.min_cand_cut_size ) + continue; + + if ( ( *cut )->data.gain < ( ps.allow_zero_gain ? 0 : 1 ) ) + continue; + + std::vector> leaves; + for ( auto leaf_index : *cut ) + { + leaves.push_back( ntk.index_to_node( leaf_index ) ); + } + cut_view dcut( ntk, leaves, ntk.make_signal( n ) ); + dcut.foreach_gate( [&]( auto const& n2 ) { + // if ( dcut.is_constant( n2 ) || dcut.is_pi( n2 ) ) + // return; + conflicts[ntk.node_to_index( n2 )].emplace_back( n, cctr ); + } ); + + auto v = g.add_vertex( ( *cut )->data.gain ); + assert( v == vertex_to_cut_addr.size() ); + vertex_to_cut_addr.emplace_back( n, cctr ); + cut_addr_to_vertex[ntk.node_to_index( n )].emplace_back( static_cast( v ) ); + + ++cctr; + } + } ); + + for ( auto n = 0u; n < conflicts.size(); ++n ) + { + for ( auto j = 1u; j < conflicts[n].size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + const auto [n1, c1] = conflicts[n][i]; + const auto [n2, c2] = conflicts[n][j]; + + if ( cut_addr_to_vertex[ntk.node_to_index( n1 )][c1] != cut_addr_to_vertex[ntk.node_to_index( n2 )][c2] ) + { + g.add_edge( cut_addr_to_vertex[ntk.node_to_index( n1 )][c1], cut_addr_to_vertex[ntk.node_to_index( n2 )][c2] ); + } + } + } + } + + return { g, vertex_to_cut_addr }; +} + +template +struct has_rewrite_with_dont_cares : std::false_type +{ +}; + +template +struct has_rewrite_with_dont_cares()( std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_rewrite_with_dont_cares_v = has_rewrite_with_dont_cares::value; + +template +class cut_rewriting_with_compatibility_graph_impl +{ +public: + cut_rewriting_with_compatibility_graph_impl( Ntk& ntk, RewritingFn&& rewriting_fn, cut_rewriting_params const& ps, cut_rewriting_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), + rewriting_fn( rewriting_fn ), + ps( ps ), + st( st ), + cost_fn( cost_fn ) {} + + void run() + { + stopwatch t( st.time_total ); + + /* enumerate cuts */ + const auto cuts = call_with_stopwatch( st.time_cuts, [&]() { return cut_enumeration( ntk, ps.cut_enumeration_ps ); } ); + + /* for cost estimation we use reference counters initialized by the fanout size */ + ntk.clear_values(); + ntk.foreach_node( [&]( auto const& n ) { + ntk.set_value( n, ntk.fanout_size( n ) ); + } ); + + /* store best replacement for each cut */ + node_map>, Ntk> best_replacements( ntk ); + + /* iterate over all original nodes in the network */ + const auto size = ntk.size(); + auto max_total_gain = 0u; + progress_bar pbar{ ntk.size(), "cut_rewriting |{0}| node = {1:>4}@{2:>2} / " + std::to_string( size ) + " comm. gain = {3}", ps.progress }; + ntk.foreach_node( [&]( auto const& n, auto index ) { + /* stop once all original nodes were visited */ + if ( index >= size ) + return false; + + /* do not iterate over constants or PIs */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return true; + + /* skip cuts with small MFFC */ + if ( mffc_size( ntk, n ) == 1 ) + return true; + + /* foreach cut */ + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cuts */ + if ( cut->size() < ps.min_cand_cut_size ) + continue; + + const auto tt = cuts.truth_table( *cut ); + assert( cut->size() == static_cast( tt.num_vars() ) ); + + pbar( index, ntk.node_to_index( n ), best_replacements[n].size(), max_total_gain ); + + std::vector> children; + for ( auto l : *cut ) + { + children.push_back( ntk.make_signal( ntk.index_to_node( l ) ) ); + } + + int32_t value = recursive_deref( ntk, n ); + { + stopwatch t( st.time_rewriting ); + int32_t best_gain{ -1 }; + + const auto on_signal = [&]( auto const& f_new ) { + auto [v, contains] = recursive_ref_contains( ntk.get_node( f_new ), n ); + recursive_deref( ntk, ntk.get_node( f_new ) ); + + int32_t gain = contains ? -1 : value - v; + + if ( gain > 0 || ( ps.allow_zero_gain && gain == 0 ) ) + { + if ( best_gain == -1 ) + { + ( *cut )->data.gain = best_gain = gain; + best_replacements[n].push_back( f_new ); + } + else if ( gain > best_gain ) + { + ( *cut )->data.gain = best_gain = gain; + best_replacements[n].back() = f_new; + } + } + + return true; + }; + + if ( ps.use_dont_cares ) + { + if constexpr ( has_rewrite_with_dont_cares_v ) + { + std::vector> pivots; + for ( auto const& c : children ) + { + pivots.push_back( ntk.get_node( c ) ); + } + rewriting_fn( ntk, cuts.truth_table( *cut ), satisfiability_dont_cares( ntk, pivots ), children.begin(), children.end(), on_signal ); + } + else + { + rewriting_fn( ntk, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + } + else + { + rewriting_fn( ntk, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + + if ( best_gain > 0 ) + { + max_total_gain += best_gain; + } + } + + recursive_ref( ntk, n ); + } + + return true; + } ); + + stopwatch t2( st.time_mis ); + auto [g, map] = network_cuts_graph( ntk, cuts, ps ); + + if ( ps.very_verbose ) + { + std::cout << "[i] replacement dependency graph has " << g.num_vertices() << " vertices and " << g.num_edges() << " edges\n"; + } + + const auto is = ( ps.candidate_selection_strategy == cut_rewriting_params::minimize_weight ) ? maximum_weighted_independent_set_gwmin( g ) : maximal_weighted_independent_set( g ); + + if ( ps.very_verbose ) + { + std::cout << "[i] size of independent set is " << is.size() << "\n"; + } + + for ( const auto v : is ) + { + const auto v_node = map[v].first; + const auto v_cut = map[v].second; + + if ( ps.very_verbose ) + { + std::cout << "[i] try to rewrite cut #" << v_cut << " in node #" << ntk.node_to_index( v_node ) << "\n"; + } + + if ( best_replacements[v_node].empty() ) + continue; + + const auto replacement = best_replacements[v_node][v_cut]; + + if ( ntk.is_constant( ntk.get_node( replacement ) ) || v_node == ntk.get_node( replacement ) ) + continue; + + if ( ps.very_verbose ) + { + std::cout << "[i] optimize cut #" << v_cut << " in node #" << ntk.node_to_index( v_node ) << " and replace with node " << ntk.node_to_index( ntk.get_node( replacement ) ) << "\n"; + } + + ntk.substitute_node( v_node, replacement ); + } + } + +private: + std::pair recursive_ref_contains( node const& n, node const& repl ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return { 0, false }; + + /* recursively collect nodes */ + int32_t value = cost_fn( ntk, n ); + bool contains = ( n == repl ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + contains = contains || ( ntk.get_node( s ) == repl ); + if ( ntk.incr_value( ntk.get_node( s ) ) == 0 ) + { + const auto [v, c] = recursive_ref_contains( ntk.get_node( s ), repl ); + value += v; + contains = contains || c; + } + } ); + return { value, contains }; + } + +private: + Ntk& ntk; + RewritingFn&& rewriting_fn; + cut_rewriting_params const& ps; + cut_rewriting_stats& st; + NodeCostFn cost_fn; +}; + +} /* namespace detail */ + +/*! \brief In-place cut rewriting algorithm with compatibility graph. + * + * This algorithm enumerates cut of a network and then tries to rewrite the cut + * in terms of gates of the same network. The rewritten structures are added + * to the network, and if they lead to area improvement, will be used as new + * parts of the logic. The resulting network therefore has a lot of dangling + * nodes from unsuccessful candidates, which can be removed by calling + * `cleanup_dangling` after the rewriting algorithm. + * + * The rewriting function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some rewriting algorithms in the folder + * `mockturtle/algorithms/node_resynthesis`, since the resynthesis functions + * have the same signature. + * + * In contrast to node resynthesis, cut rewriting uses the same type for the + * input and output network. Consequently, the algorithm does not return a + * new network but applies changes in-place to the input network. + * + * **Required network functions:** + * - `fanout_size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * - `clear_values` + * - `incr_value` + * - `decr_value` + * - `set_value` + * - `node_to_index` + * - `index_to_node` + * - `substitute_node` + * - `make_signal` + * + * \param ntk Network (will be modified) + * \param rewriting_fn Rewriting function + * \param ps Rewriting params + * \param pst Rewriting statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void cut_rewriting_with_compatibility_graph( Ntk& ntk, RewritingFn&& rewriting_fn, cut_rewriting_params const& ps = {}, cut_rewriting_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + cut_rewriting_stats st; + if constexpr ( std::is_same_v ) + { + detail::cut_rewriting_with_compatibility_graph_impl p( ntk, rewriting_fn, ps, st, cost_fn ); + p.run(); + } + else + { + fanout_view_params fvps; + fvps.update_on_delete = false; + fanout_view ntk_fo{ ntk, fvps }; + detail::cut_rewriting_with_compatibility_graph_impl, RewritingFn, NodeCostFn> p( ntk_fo, rewriting_fn, ps, st, cost_fn ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +namespace detail +{ + +template +struct cut_rewriting_impl +{ + cut_rewriting_impl( Ntk const& ntk, RewritingFn const& rewriting_fn, cut_rewriting_params const& ps, cut_rewriting_stats& st ) + : ntk_( ntk ), + rewriting_fn_( rewriting_fn ), + ps_( ps ), + st_( st ) {} + + NtkDest run() + { + stopwatch t( st_.time_total ); + + /* initial node map */ + node_map, Ntk> old2new( ntk_ ); + Ntk res; + old2new[ntk_.get_constant( false )] = res.get_constant( false ); + if ( ntk_.get_node( ntk_.get_constant( true ) ) != ntk_.get_node( ntk_.get_constant( false ) ) ) + { + old2new[ntk_.get_constant( true )] = res.get_constant( true ); + } + ntk_.foreach_pi( [&]( auto const& n ) { + old2new[n] = res.create_pi(); + } ); + + /* enumerate cuts */ + const auto cuts = call_with_stopwatch( st_.time_cuts, [&]() { return cut_enumeration( ntk_, ps_.cut_enumeration_ps ); } ); + + /* for cost estimation we use reference counters initialized by the fanout size */ + initialize_values_with_fanout( ntk_ ); + + /* original cost */ + const auto orig_cost = costs( ntk_ ); + + progress_bar pbar{ ntk_.num_gates(), "cut_rewriting |{0}| node = {1:>4} / " + std::to_string( ntk_.num_gates() ) + " original cost = " + std::to_string( orig_cost ), ps_.progress }; + ntk_.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i ); + + /* nothing to optimize? */ + int32_t value = mffc_size( ntk_, n ); + if ( value == 1 ) + { + std::vector> children( ntk_.fanin_size( n ) ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f]; + } ); + + old2new[n] = res.clone_node( ntk_, n, children ); + } + else + { + /* foreach cut */ + int32_t best_gain = -1; + signal best_signal; + for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) + { + /* skip small enough cuts */ + if ( cut->size() == 1 || cut->size() < ps_.min_cand_cut_size ) + continue; + + const auto tt = cuts.truth_table( *cut ); + assert( cut->size() == static_cast( tt.num_vars() ) ); + + std::vector> children( cut->size() ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[ctr++] = old2new[ntk_.index_to_node( l )]; + } + + const auto on_signal = [&]( auto const& f_new ) { + auto value2 = recursive_ref( res, res.get_node( f_new ) ); + recursive_deref( res, res.get_node( f_new ) ); + int32_t gain = value - value2; + + if ( ( gain > 0 || ( ps_.allow_zero_gain && gain == 0 ) ) && gain > best_gain ) + { + if constexpr ( has_level_v ) + { + if ( !ps_.preserve_depth || res.level( res.get_node( f_new ) ) <= ntk_.level( n ) ) + { + best_gain = gain; + best_signal = f_new; + } + } + else + { + best_gain = gain; + best_signal = f_new; + } + } + + return true; + }; + stopwatch<> t( st_.time_rewriting ); + rewriting_fn_( res, cuts.truth_table( *cut ), children.begin(), children.end(), on_signal ); + } + + if ( best_gain == -1 ) + { + std::vector> children( ntk_.fanin_size( n ) ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f]; + } ); + + old2new[n] = res.clone_node( ntk_, n, children ); + } + else + { + old2new[n] = best_signal; + } + } + + recursive_ref( res, res.get_node( old2new[n] ) ); + } ); + + /* create POs */ + ntk_.foreach_po( [&]( auto const& f ) { + res.create_po( ntk_.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + + mockturtle::print(res); + NtkDest ret = cleanup_dangling( res ); + + /* new costs */ + return costs( ret ) > orig_cost ? static_cast( ntk_ ) : ret; + } + +private: + Ntk const& ntk_; + RewritingFn const& rewriting_fn_; + cut_rewriting_params const& ps_; + cut_rewriting_stats& st_; +}; + +} // namespace detail + +/*! \brief Cut rewriting algorithm. + * + * This algorithm enumerates cut of a network and then tries to rewrite the cut + * in terms of gates of the same network. The rewritten structures are added + * to the network, and if they lead to area improvement, will be used as new + * parts of the logic. + * + * The rewriting function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some rewriting algorithms in the folder + * `mockturtle/algorithms/node_resynthesis`, since the resynthesis functions + * have the same signature. + * + * In contrast to node resynthesis, cut rewriting uses the same type for the + * input and output network. + * + * \param ntk Network + * \param rewriting_fn Rewriting function + * \param ps Rewriting params + * \param pst Rewriting statistics + */ +template> +Ntk cut_rewriting( Ntk const& ntk, RewritingFn const& rewriting_fn = {}, cut_rewriting_params const& ps = {}, cut_rewriting_stats* pst = nullptr ) +{ + cut_rewriting_stats st; + const auto result = [&]() { + if ( ps.preserve_depth ) + { + depth_view depth_ntk{ ntk }; + return detail::cut_rewriting_impl, RewritingFn, NodeCostFn>( depth_ntk, rewriting_fn, ps, st ).run(); + } + else + { + return detail::cut_rewriting_impl( ntk, rewriting_fn, ps, st ).run(); + } + }(); + + if ( ps.verbose ) + { + st.report( false ); + } + if ( pst ) + { + *pst = st; + } + + ntk.clear_values(); + return result; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/decomposition.hpp b/include/mockturtle/algorithms/decomposition.hpp new file mode 100644 index 0000000..541de0b --- /dev/null +++ b/include/mockturtle/algorithms/decomposition.hpp @@ -0,0 +1,301 @@ +/* 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 decomposition.hpp + \brief Shannon and Davio decomposition + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../traits.hpp" +#include "node_resynthesis/null.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +class shannon_decomposition_impl +{ +public: + shannon_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + func_( func ), + vars_( vars ), + pis_( children ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + f = ntk_.create_ite( pis_[vars_[var_index]], f1, f0 ); + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + kitty::dynamic_truth_table func_; + std::vector vars_; + std::vector> const& pis_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +template +class davio_decomposition_impl +{ +public: + davio_decomposition_impl( Ntk& ntk, bool polarity, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn ) + : ntk_( ntk ), + polarity_( polarity ), + func_( func ), + pis_( children ), + vars_( vars ), + resyn_( resyn ) + { + cache_.insert( { func.construct(), ntk_.get_constant( false ) } ); + + for ( auto i = 0u; i < pis_.size(); ++i ) + { + auto var = func.construct(); + kitty::create_nth_var( var, i ); + cache_.insert( { func.construct(), pis_[i] } ); + } + } + + signal run() + { + return decompose( 0u, func_ ); + } + +private: + signal decompose( uint32_t var_index, kitty::dynamic_truth_table const& func ) + { + /* cache lookup... */ + auto it = cache_.find( func ); + if ( it != cache_.end() ) + { + return it->second; + } + + /* ...and for the complement */ + it = cache_.find( ~func ); + if ( it != cache_.end() ) + { + return ntk_.create_not( it->second ); + } + + signal f; + if ( var_index == vars_.size() ) + { + auto copy = func; + const auto support = kitty::min_base_inplace( copy ); + const auto small_func = kitty::shrink_to( copy, static_cast( support.size() ) ); + std::vector> small_pis( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + small_pis[i] = pis_[support[i]]; + } + resyn_( ntk_, small_func, small_pis.begin(), small_pis.end(), [&]( auto const& _f ) { + f = _f; + return false; + } ); + } + else + { + /* decompose */ + const auto f0 = decompose( var_index + 1, kitty::cofactor0( func, vars_[var_index] ) ); + const auto f1 = decompose( var_index + 1, kitty::cofactor1( func, vars_[var_index] ) ); + + if ( polarity_ ) + { + f = ntk_.create_xor( f0, ntk_.create_and( pis_[vars_[var_index]], ntk_.create_xor( f0, f1 ) ) ); + } + else + { + f = ntk_.create_xor( f1, ntk_.create_and( ntk_.create_not( pis_[vars_[var_index]] ), ntk_.create_xor( f0, f1 ) ) ); + } + } + cache_.insert( { func, f } ); + return f; + } + +private: + Ntk& ntk_; + bool polarity_; + kitty::dynamic_truth_table func_; + std::vector> const& pis_; + std::vector const& vars_; + SynthesisFn const& resyn_; + std::unordered_map, kitty::hash> cache_; +}; + +} // namespace detail + +/*! \brief Shannon decomposition + * + * This function applies Shannon decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_ite` + * - `get_constant` + */ +template> +signal shannon_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::shannon_decomposition_impl impl( ntk, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Positive Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal positive_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, true, func, vars, children, resyn ); + return impl.run(); +} + +/*! \brief Negative Davio decomposition + * + * This function applies positive Davio decomposition on an input truth table and + * constructs a network based. The variable ordering can be specified as + * an input. If not all variables are specified, the remaining co-factors + * are synthesizes using the resynthesis function. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_xor` + * - `get_constant` + */ +template> +signal negative_davio_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector const& vars, std::vector> const& children, SynthesisFn const& resyn = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_ite method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_ite method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + detail::davio_decomposition_impl impl( ntk, false, func, vars, children, resyn ); + return impl.run(); +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/detail/database_generator.hpp b/include/mockturtle/algorithms/detail/database_generator.hpp new file mode 100644 index 0000000..49414c0 --- /dev/null +++ b/include/mockturtle/algorithms/detail/database_generator.hpp @@ -0,0 +1,96 @@ +/* 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 database_generator.hpp + \brief Utility class to generate a database in form of a logic + network from functions. + + \author Heinz Riener +*/ + +#pragma once + +#include +#include + +#include + +namespace mockturtle::detail +{ + +struct database_generator_params +{ + uint32_t num_vars{ 4u }; + bool multiple_candidates{ false }; + bool verbose{ false }; +}; /* database_generator_param */ + +template +class database_generator +{ +public: + using signal = mockturtle::signal; + +public: + explicit database_generator( Ntk& ntk, ResynFn const& resyn, database_generator_params const& ps ) + : ntk( ntk ), resyn( resyn ), ps( ps ) + { + for ( auto i = 0u; i < ps.num_vars; ++i ) + { + pis.emplace_back( ntk.create_pi() ); + } + } + + void add_function( kitty::dynamic_truth_table tt ) + { + /* normalize the function first if necessary */ + if ( !kitty::is_normal( tt ) ) + { + tt = ~tt; + } + + /* resynthesize the function and add it to the database */ + resyn( ntk, tt, std::begin( pis ), std::end( pis ), + [&]( const signal& s ) { + if ( ps.verbose ) + { + std::cout << "[i] function: "; + kitty::print_binary( tt ); + std::cout << " stored at PO #" << ntk.num_pos() << std::endl; + } + ntk.create_po( s ); + return ps.multiple_candidates; + } ); + } + + Ntk& ntk; + ResynFn const& resyn; + database_generator_params const& ps; + + std::vector pis; +}; /* database_generator */ + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/include/mockturtle/algorithms/detail/mffc_utils.hpp b/include/mockturtle/algorithms/detail/mffc_utils.hpp new file mode 100644 index 0000000..94da045 --- /dev/null +++ b/include/mockturtle/algorithms/detail/mffc_utils.hpp @@ -0,0 +1,135 @@ +/* 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 mffc_utils.hpp + \brief Utility functions for DAG-aware reference counting and + MFFC-size computation + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../../traits.hpp" +#include "../../utils/cost_functions.hpp" + +namespace mockturtle::detail +{ + +template +void initialize_values_with_fanout( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + ntk.clear_values(); + ntk.foreach_node( [&]( auto const& n ) { + ntk.set_value( n, ntk.fanout_size( n ) ); + } ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, TermCond const& terminate ) +{ + /* terminate? */ + if ( terminate( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value = NodeCostFn{}( ntk, n ); + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_value( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk, ntk.get_node( s ), terminate ); + } + } ); + return value; +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n, LeavesIterator begin, LeavesIterator end ) +{ + const auto terminate = [&]( auto const& n ) { return std::find( begin, end, n ) != end; }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t recursive_deref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_deref( ntk, n, terminate ); +} + +template> +uint32_t recursive_ref( Ntk const& ntk, node const& n ) +{ + const auto terminate = [&]( auto const& n ) { return ntk.is_constant( n ) || ntk.is_pi( n ); }; + return recursive_ref( ntk, n, terminate ); +} + +template> +uint32_t mffc_size( Ntk const& ntk, node const& n ) +{ + auto v1 = recursive_deref( ntk, n ); + auto v2 = recursive_ref( ntk, n ); + assert( v1 == v2 ); + (void)v2; + return v1; +} + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/detail/minmc_xags.hpp b/include/mockturtle/algorithms/detail/minmc_xags.hpp new file mode 100644 index 0000000..4575564 --- /dev/null +++ b/include/mockturtle/algorithms/detail/minmc_xags.hpp @@ -0,0 +1,198 @@ +/* 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 minmc_xags.hpp + \brief Optimum MC XAGs up to 5 variables + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +namespace mockturtle::detail +{ + +// These circuits are based on Cryptology ePrint Archive: Report 2015/848 +// Meltem Sönmez Turan and Rene Péralta: +// The Multiplicative Complexity of Boolean Functions on Four and Five Variables + +// These circuits have been resynthesized to match the affine representative +// in kitty::hybrid_exact_spectral_canonization + +// The original (and partly revised) networks are kept in the legacy namespace +// below + +// clang-format off +static std::vector, std::string>>> minmc_xags = { + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}, + {1, 0x8, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}}, + {{0, 0x00, {1 << 8 | 0, 0}, "0"}, + {2, 0x88, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {1, 0x80, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}}, + {{0, 0x0000, {1 << 8 | 0, 0}, "0"}, + {1, 0x8000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + {2, 0x8080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + {3, 0x0888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {4, 0x8888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {5, 0x2a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {6, 0xf888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {7, 0x7888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}}, + {{ 0, 0x00000000, {1 << 8 | 0, 0}, "0"}, + { 1, 0x80000000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18}, "(abcde)"}, + { 2, 0x80008000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + { 3, 0x00808080, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 14, 20}, "[(abcde)(abc)]"}, + { 4, 0x80808080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + { 5, 0x08888000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 14, 10, 12, 16, 18}, "[(abcd)(abe)]"}, + { 6, 0xaa2a2a80, {7 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 8, 16, 18, 2, 10, 20, 22, 16, 24}, "[(abc)(ad)(ae)(ade)(abcde)]"}, + { 7, 0x88080808, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(abc)(ab)]"}, + { 8, 0x2888a000, {4 << 16 | 1 << 8 | 5, 4, 10, 6, 8, 14, 12, 2, 16, 18}, "[(acd)(abe)]"}, + { 9, 0xf7788000, {8 << 16 | 1 << 8 | 5, 6, 8, 2, 4, 12, 10, 8, 6, 18, 10, 20, 14, 16, 22, 24, 10, 26}, "[(abcd)(abe)(ce)(de)(cde)]"}, + {10, 0xa8202020, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 6, 4, 14, 16, 6, 2, 18, 20}, "[(ac)(abc)(abde)]"}, + {11, 0x08880888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {12, 0xbd686868, {8 << 16 | 1 << 8 | 5, 8, 10, 12, 4, 2, 14, 4, 6, 16, 6, 18, 2, 20, 22, 24, 12, 26}, "[(ab)(ac)(bc)(abc)(de)(ade)(abcde)]"}, + {13, 0xaa808080, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 16, 2, 20, 22}, "[(abcde)(abc)(ade)]"}, + {14, 0x7e686868, {9 << 16 | 1 << 8 | 5, 8, 10, 12, 4, 12, 2, 14, 16, 2, 6, 18, 6, 20, 14, 22, 24, 26, 12, 28}, "[(ab)(ac)(bc)(abc)(ade)(bde)(cde)(abcde)]"}, + {15, 0x2208a208, {7 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 10, 14, 14, 8, 18, 12, 16, 2, 20, 22, 24}, "[(ab)(abc)(ad)(abcde)]"}, + {16, 0x08888888, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20}, "[(abcde)(ab)]"}, + {17, 0x88888888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {18, 0xea404040, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18, 12, 20}, "[(abc)(ade)(bc)]"}, + {19, 0x2a802a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {20, 0x73d28c88, {9 << 16 | 1 << 8 | 5, 2, 4, 10, 4, 14, 12, 8, 16, 18, 10, 6, 2, 20, 22, 18, 12, 26, 24, 28}, "[(ab)(bd)(abd)(bcd)(abcd)(ae)(ce)(de)(ade)(cde)]"}, + {21, 0xea808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 2, 22}, "[(bcde)(abc)(ade)]"}, + {22, 0xa28280a0, {6 << 16 | 1 << 8 | 5, 6, 8, 12, 10, 4, 2, 14, 16, 18, 6, 2, 20, 22}, "[(ac)(acd)(abcd)(ae)(abe)]"}, + {23, 0x13284c88, {8 << 16 | 1 << 8 | 5, 2, 6, 12, 2, 4, 14, 12, 8, 16, 10, 20, 4, 18, 22, 24, 16, 26}, "[(ab)(bd)(abd)(abcd)(ace)(de)]"}, + {24, 0xa2220888, {5 << 16 | 1 << 8 | 5, 6, 8, 4, 12, 14, 4, 16, 10, 2, 18, 20}, "[(ab)(abcd)(ae)]"}, + {25, 0xaae6da80, {12 << 16 | 1 << 8 | 5, 4, 6, 12, 10, 4, 2, 14, 16, 18, 12, 20, 6, 10, 22, 24, 6, 26, 2, 8, 28, 30, 18, 32, 12, 34}, "[(abc)(ad)(cd)(ae)(be)(ade)(bde)(cde)(abcde)]"}, + {26, 0x58d87888, {9 << 16 | 1 << 8 | 5, 2, 4, 8, 2, 6, 14, 16, 6, 10, 18, 12, 6, 20, 2, 22, 24, 26, 16, 28}, "[(ab)(cd)(ce)(ace)(cde)(abcde)]"}, + {27, 0x8c88ac28, {8 << 16 | 1 << 8 | 5, 6, 10, 12, 4, 2, 14, 4, 8, 16, 6, 18, 2, 20, 22, 24, 18, 26}, "[(ab)(ac)(bd)(abd)(bcd)(ace)(abcde)]"}, + {28, 0x8880f880, {7 << 16 | 1 << 8 | 5, 2, 4, 12, 6, 8, 14, 10, 16, 18, 12, 6, 20, 22, 16, 24}, "[(abc)(abd)(cd)(cde)(abcde)]"}, + {29, 0x9ee8e888, {11 << 16 | 1 << 8 | 5, 2, 4, 12, 6, 14, 8, 10, 16, 6, 8, 20, 18, 10, 2, 24, 4, 26, 18, 22, 28, 30, 12, 32}, "[(ab)(acd)(bcd)(ace)(bce)(ade)(bde)(cde)(abcde)]"}, + {30, 0x4268c268, {9 << 16 | 1 << 8 | 5, 4, 6, 10, 12, 8, 4, 16, 12, 14, 2, 18, 20, 22, 6, 2, 24, 26, 12, 28}, "[(ab)(ac)(bc)(abc)(ad)(abcde)]"}, + {31, 0x16704c80, {7 << 16 | 1 << 8 | 5, 2, 10, 12, 4, 8, 14, 2, 4, 18, 10, 6, 20, 22, 16, 24}, "[(abc)(bd)(ce)(ade)]"}, + {32, 0x78888888, {4 << 16 | 1 << 8 | 5, 6, 8, 10, 12, 2, 4, 16, 14, 18}, "[(ab)(cde)]"}, + {33, 0x4966bac0, {10 << 16 | 1 << 8 | 5, 2, 8, 4, 6, 12, 10, 14, 2, 16, 18, 10, 6, 12, 8, 24, 4, 22, 26, 28, 20, 30}, "[(bc)(ad)(cd)(acd)(abcd)(ae)(be)(bce)(de)(ade)]"}, + {34, 0x372840a0, {9 << 16 | 1 << 8 | 5, 2, 4, 12, 2, 6, 14, 4, 6, 18, 16, 20, 10, 12, 8, 22, 24, 26, 16, 28}, "[(ac)(acd)(bcd)(abcd)(abe)(de)]"}, + {35, 0x5208d288, {7 << 16 | 1 << 8 | 5, 2, 4, 10, 12, 14, 8, 6, 2, 16, 18, 14, 12, 22, 20, 24}, "[(ab)(ad)(cd)(abce)]"}, + {36, 0x7ca00428, {10 << 16 | 1 << 8 | 5, 8, 2, 4, 12, 10, 14, 10, 2, 18, 14, 8, 20, 6, 4, 22, 2, 24, 26, 28, 16, 30}, "[(ab)(ac)(bd)(acd)(bcd)(abcd)(abe)(cde)]"}, + {37, 0xf8880888, {5 << 16 | 1 << 8 | 5, 2, 4, 12, 10, 6, 14, 8, 16, 18, 12, 20}, "[(ab)(abcd)(cde)]"}, + {38, 0x2ec0ae40, {8 << 16 | 1 << 8 | 5, 8, 6, 4, 12, 14, 8, 2, 16, 10, 18, 20, 14, 4, 22, 24, 18, 26}, "[(bc)(abc)(ad)(bd)(abd)(abce)]"}, + {39, 0xf888f888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {40, 0x58362ec0, {11 << 16 | 1 << 8 | 5, 6, 10, 10, 8, 14, 12, 4, 2, 16, 18, 20, 8, 2, 22, 24, 6, 12, 4, 26, 28, 30, 20, 32}, "[(bc)(ad)(bd)(abd)(ae)(be)(ce)(ace)(bce)(abcde)]"}, + {41, 0x0eb8f6c0, {13 << 16 | 1 << 8 | 5, 6, 10, 2, 8, 14, 10, 12, 2, 16, 18, 14, 4, 22, 8, 20, 4, 12, 6, 28, 26, 24, 30, 32, 12, 34, 22, 36}, "[(bc)(ad)(bd)(cd)(acd)(abe)(ce)(abcde)]"}, + {42, 0x567cea40, {10 << 16 | 1 << 8 | 5, 2, 8, 10, 12, 14, 4, 16, 2, 6, 18, 20, 10, 6, 4, 22, 24, 20, 12, 28, 26, 30}, "[(bc)(abc)(ad)(be)(ce)(abcde)]"}, + {43, 0xf8887888, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(ab)(cd)]"}, + {44, 0x78887888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}, + {45, 0xe72890a0, {8 << 16 | 1 << 8 | 5, 4, 10, 12, 6, 2, 14, 8, 4, 6, 18, 20, 10, 8, 22, 24, 16, 26}, "[(ac)(cd)(bcd)(abe)(de)]"}, + {46, 0x268cea40, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 4, 10, 16, 12, 20, 18, 22}, "[(bc)(abc)(ad)(be)]"}, + {47, 0x6248eac0, {7 << 16 | 1 << 8 | 5, 2, 8, 6, 12, 14, 10, 2, 16, 18, 6, 4, 20, 22, 12, 24}, "[(bc)(ad)(abcd)(abe)]"}} +}; +// clang-format on + +namespace legacy +{ + +// These circuits are based on Cryptology ePrint Archive: Report 2015/848 +// Meltem Sönmez Turan and Rene Péralta: +// The Multiplicative Complexity of Boolean Functions on Four and Five Variables + +// clang-format off +static std::vector, std::string>>> minmc_xags = { + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}}, + {{0, 0x0, {1 << 8 | 0, 0}, "0"}, + {1, 0x8, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}}, + {{0, 0x00, {1 << 8 | 0, 0}, "0"}, + {2, 0x88, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {1, 0x80, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}}, + {{0, 0x0000, {1 << 8 | 0, 0}, "0"}, + {1, 0x8000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + {2, 0x8080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + {3, 0x0888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {4, 0x8888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {5, 0x2a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {6, 0xf888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {7, 0x7888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}}, + {{ 0, 0x00000000, {1 << 8 | 0, 0}, "0"}, + { 1, 0x80000000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18}, "(abcde)"}, + { 2, 0x80008000, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14}, "(abcd)"}, + { 3, 0x00808080, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 14, 20}, "[(abcde)(abc)]"}, + { 4, 0x80808080, {2 << 16 | 1 << 8 | 3, 2, 4, 6, 8, 10}, "(abc)"}, + { 5, 0x40808080, {4 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 2, 12, 16, 18}, "[(bcde)(abc)]"}, + { 6, 0x22080808, {7 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 4, 20, 16, 2, 22, 24}, "[(abcde)(abc)(ade)(ab)]"}, + { 7, 0x88080808, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(abc)(ab)]"}, + { 8, 0x2a808080, {4 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18}, "[(abc)(ade)]"}, + { 9, 0x15808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 18, 22}, "[(bcde)(abc)(ade)(de)]"}, + {10, 0xc8080808, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 2, 4, 18, 20}, "[(bcde)(ab)(abc)]"}, + {11, 0x08880888, {4 << 16 | 1 << 8 | 4, 2, 4, 6, 10, 8, 12, 14, 10, 16}, "[(abcd)(ab)]"}, + {12, 0x95404040, {8 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 16, 12, 18, 14, 2, 20, 22, 12, 24, 14, 26}, "[(abcde)(abc)(ade)(de)(bc)]"}, + {13, 0xaa808080, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 14, 12, 18, 16, 2, 20, 22}, "[(abcde)(abc)(ade)]"}, + {14, 0x6a404040, {7 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 12, 14, 16, 12, 18, 14, 2, 20, 22, 12, 24}, "[(abcde)(abc)(ade)(bc)]"}, + {15, 0xaa802a80, {6 << 16 | 1 << 8 | 5, 4, 6, 2, 8, 10, 14, 16, 2, 12, 18, 20, 14, 22}, "[(abcde)(abc)(ad)]"}, + {16, 0x08888888, {5 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 14, 10, 16, 18, 12, 20}, "[(abcde)(ab)]"}, + {17, 0x88888888, {1 << 16 | 1 << 8 | 2, 2, 4, 6}, "(ab)"}, + {18, 0xea404040, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 18, 12, 20}, "[(abc)(ade)(bc)]"}, + {19, 0x2a802a80, {3 << 16 | 1 << 8 | 4, 4, 6, 10, 8, 2, 12, 14}, "[(abc)(ad)]"}, + {20, 0x37080808, {6 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 2, 4, 18, 20, 12, 22}, "[(bcde)(abc)(ab)(de)]"}, + {21, 0xea808080, {6 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 2, 14, 18, 20, 2, 22}, "[(bcde)(abc)(ade)]"}, + {22, 0x8c804c80, {5 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 14, 16, 8, 4, 18, 20}, "[(bcde)(bd)(abc)]"}, + {23, 0xea802a80, {8 << 16 | 1 << 8 | 5, 6, 4, 6, 12, 14, 6, 16, 8, 10, 16, 20, 2, 19, 22, 24, 2, 26}, "[(bcde)(abc)(ad)]"}, + {24, 0x7f008000, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 14, 10, 8, 16, 18}, "[(abcd)(de)]"}, + {25, 0x96704c80, {12 << 16 | 1 << 8 | 5, 10, 8, 12, 2, 10, 14, 16, 6, 6, 4, 8, 20, 22, 2, 4, 24, 26, 12, 18, 28, 22, 16, 32, 30, 34}, "[(abcde)(abc)(ade)(ce)(bd)]"}, + {26, 0x77080808, {7 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 14, 16, 18, 12, 20, 14, 22, 16, 24}, "[(abcde)(abc)(ab)(de)]"}, + {27, 0x66804c80, {8 << 16 | 1 << 8 | 5, 6, 4, 12, 2, 6, 14, 16, 8, 2, 8, 10, 20, 22, 4, 18, 24, 26}, "[(abcde)(abc)(ade)(bd)]"}, + {28, 0xa6408c40, {9 << 16 | 1 << 8 | 5, 4, 6, 8, 6, 14, 2, 2, 10, 18, 4, 8, 20, 22, 16, 12, 24, 26, 22, 28}, "[(abcde)(abc)(ade)(bd)(bc)]"}, + {29, 0xff808080, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 14, 16, 18, 14, 20, 16, 22}, "[(abcde)(abc)(de)]"}, + {30, 0x7808f808, {7 << 16 | 1 << 8 | 5, 2, 4, 8, 10, 12, 14, 16, 12, 18, 8, 6, 20, 22, 12, 24}, "[(abcde)(abc)(ab)(cd)]"}, + {31, 0xe6804c80, {6 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 12, 2, 16, 4, 8, 20, 18, 22}, "[(abc)(ade)(bd)]"}, + {32, 0x7f808080, {4 << 16 | 1 << 8 | 5, 2, 4, 6, 12, 8, 10, 16, 14, 18}, "[(abc)(de)]"}, + {33, 0xd6704c80, {10 << 16 | 1 << 8 | 5, 4, 6, 12, 2, 8, 10, 16, 12, 14, 18, 10, 4, 8, 6, 22, 24, 26, 16, 28, 20, 30}, "[(bcde)(abc)(ade)(ce)(bd)]"}, + {34, 0x1a702a80, {7 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 4, 14, 16, 10, 6, 18, 2, 8, 22, 20, 24}, "[(bcde)(abc)(ad)(ce)]"}, + {35, 0xb8887888, {5 << 16 | 1 << 8 | 5, 6, 8, 10, 12, 14, 2, 4, 16, 18, 12, 20}, "[(bcde)(ab)(cd)]"}, + {36, 0xd9804c80, {7 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 4, 6, 16, 12, 14, 18, 4, 8, 22, 20, 24}, "[(bcde)(abc)(ade)(bd)(de)]"}, + {37, 0xbf808080, {5 << 16 | 1 << 8 | 5, 4, 6, 8, 10, 14, 2, 12, 16, 18, 14, 20}, "[(bcde)(abc)(de)]"}, + {38, 0x3808f808, {7 << 16 | 1 << 8 | 5, 4, 8, 10, 12, 2, 4, 16, 14, 18, 8, 6, 20, 22, 16, 24}, "[(bcde)(abc)(ab)(cd)]"}, + {39, 0xf888f888, {5 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 10, 12, 14, 10, 16, 12, 18}, "[(abcd)(ab)(cd)]"}, + {40, 0xa9b08c40, {15 << 16 | 1 << 8 | 5, 8, 4, 6, 4, 10, 4, 14, 16, 18, 12, 16, 2, 10, 8, 24, 14, 26, 2, 2, 8, 30, 14, 28, 32, 34, 22, 20, 36, 38, 30, 40}, "[(abcde)(abc)(ade)(de)(ce)(bd)(bc)]"}, + {41, 0x56b08c40, {13 << 16 | 1 << 8 | 5, 8, 2, 2, 12, 14, 4, 10, 8, 6, 2, 6, 4, 22, 2, 10, 24, 26, 20, 6, 28, 30, 18, 16, 32, 34, 26, 36}, "[(abcde)(abc)(ade)(ce)(bd)(bc)]"}, + {42, 0x664c2a80, {11 << 16 | 1 << 8 | 5, 8, 2, 6, 12, 14, 8, 8, 6, 18, 4, 4, 10, 22, 6, 20, 24, 26, 2, 16, 28, 30, 22, 32}, "[(abcde)(abc)(ad)(be)]"}, + {43, 0xf8887888, {6 << 16 | 1 << 8 | 5, 2, 4, 6, 8, 12, 14, 10, 16, 18, 12, 20, 14, 22}, "[(abcde)(ab)(cd)]"}, + {44, 0x78887888, {3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}, "[(ab)(cd)]"}, + {45, 0xd6b08c40, {8 << 16 | 1 << 8 | 5, 2, 10, 12, 4, 6, 2, 6, 16, 18, 8, 14, 20, 6, 10, 24, 22, 26}, "[(abc)(ade)(bc)(bd)(ce)]"}, + {46, 0xe64c2a80, {5 << 16 | 1 << 8 | 5, 4, 6, 12, 8, 2, 14, 4, 10, 18, 16, 20}, "[(abc)(ad)(be)]"}, + {47, 0x7c704c80, {10 << 16 | 1 << 8 | 5, 8, 10, 12, 2, 6, 4, 16, 2, 18, 12, 14, 20, 22, 8, 4, 24, 6, 10, 28, 26, 30}, "[(bcde)(abc)(bd)(ce)]"}} +}; +// clang-format on + +} // namespace legacy + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/detail/resub_utils.hpp b/include/mockturtle/algorithms/detail/resub_utils.hpp new file mode 100644 index 0000000..9db62ab --- /dev/null +++ b/include/mockturtle/algorithms/detail/resub_utils.hpp @@ -0,0 +1,782 @@ +/* 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 resub_utils.hpp + \brief Utility classes for the resubstitution framework + + class `node_mffc_inside`, class `window_simulator` (originally `simulator`), + and class `default_resub_functor` moved from resubstitution.hpp + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mockturtle::experimental::detail +{ + +struct divisor_collector_params +{ + /*! \brief Maximum number of nodes to be collected in the transitive fanin cone. */ + uint32_t max_num_tfi{ std::numeric_limits::max() }; + + /*! \brief Maximum number of nodes to collect (in total). */ + uint32_t max_num_collect{ std::numeric_limits::max() }; + + /*! \brief Maximum fanout size when considering a node in the "wings". */ + uint32_t max_fanouts{ std::numeric_limits::max() }; + + /*! \brief Maximum level when considering a node in the "wings". + * + * The network should be wrapped with `depth_view` for this parameter + * to be in effect. + */ + uint32_t max_level{ std::numeric_limits::max() }; +}; + +/*! \brief Implements helper functions for collecting divisors/supported nodes. */ +template +class divisor_collector +{ +public: + using node = typename Ntk::node; + + divisor_collector( Ntk const& ntk, divisor_collector_params ps = {} ) + : ntk( ntk ), ps( ps ) + { + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method (please wrap with fanout_view)" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + assert( ps.max_num_collect >= ps.max_num_tfi ); + } + + void set_max_level( uint32_t max_level ) + { + ps.max_level = max_level; + } + + /*! \brief Collect nodes in the transitive fanin cone of root until leaves in a topological order. + * + * `root` itself is included and will be the last element. + * Constant node(s) is not collected. + * Leaves are not collected. + * + * \param root The root node + * \param leaves The leaf nodes forming a cut supporting the root node + * \param tfi The container where TFI nodes are collected + */ + void collect_tfi( node const& root, std::vector const& leaves, std::vector& tfi ) + { + ntk.incr_trav_id(); + for ( const auto& l : leaves ) + { + ntk.set_visited( l, ntk.trav_id() ); + } + collect_tfi_rec( root, tfi ); + } + + /*! \brief Collect nodes in the TFI (BFS) and wings. + * + * Constant node(s) is not collected. + * Any node in the TFO of root is not collected. + * Collects TFI nodes with breadth-first search until PIs (unbounded) + * or until the limit on `max_num_tfi` exceeds, and then collects "wings" + * nodes until the limit on `max_num_collect` exceeds. + * Collected nodes are in a topological order, with `root` itself being the last element. + * + * \param root The root node + * \param collected The container where TFI and "wings" nodes are collected + */ + void collect_tfi_and_wings( node const& root, std::vector& collected ) + { + collect_tfi_bfs( root, collected ); + std::reverse( collected.begin(), collected.end() ); /* now in topo order */ + collected.pop_back(); /* remove `root` */ + + /* note: we cannot use range-based loop here because we push to the vector in the loop */ + for ( auto i = 0u; i < collected.size(); ++i ) + { + if ( !collect_wings( root, collected.at( i ), collected ) ) + { + break; + } + } + + collected.emplace_back( root ); + return; + } + + /*! \brief Collect all nodes that are supported by the leaves until the root (TFI + wings). + * + * Constant node(s) is not collected. + * Leaves are not collected. + * Any node in the TFO of root is not collected. + * Collected nodes are in a topological order, with `root` itself being the last element. + * + * \param root The root node + * \param leaves The leaf nodes forming a cut supporting the root node + * \param supported The container where supported nodes are collected + */ + void collect_supported_nodes( node const& root, std::vector const& leaves, std::vector& supported ) + { + /* collect TFI nodes until (excluding) leaves */ + collect_tfi( root, leaves, supported ); + supported.pop_back(); /* remove `root` */ + + if ( supported.size() > ps.max_num_tfi ) + { + return; + } + + /* collect "wings" */ + for ( auto const& l : leaves ) + { + if ( !collect_wings( root, l, supported ) ) + { + supported.emplace_back( root ); + return; + } + } + + /* note: we cannot use range-based loop here because we push to the vector in the loop */ + for ( auto i = 0u; i < supported.size(); ++i ) + { + if ( !collect_wings( root, supported.at( i ), supported ) ) + { + break; + } + } + + supported.emplace_back( root ); + return; + } + +private: + void collect_tfi_rec( node const& n, std::vector& tfi ) + { + /* collect until leaves and skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + /* collect in topological order -- lower nodes first */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + collect_tfi_rec( ntk.get_node( f ), tfi ); + } ); + + if ( !ntk.is_constant( n ) ) + { + tfi.emplace_back( n ); + } + } + + /*! \brief Collect nodes in the transitive fanin cone of root with breadth-first search. + * + * `root` itself is included and will be the first element. + * Constant node(s) is not collected. + * Collects until PIs (unbounded) or until the limit on `max_num_tfi` exceeds. + * Collected nodes are NOT in a topological order. + * + * \param root The root node + * \param tfi The container where TFI nodes are collected + */ + void collect_tfi_bfs( node const& root, std::vector& tfi ) + { + ntk.incr_trav_id(); + assert( tfi.size() == 0 ); + tfi.reserve( ps.max_num_tfi ); + tfi.emplace_back( root ); + ntk.set_visited( root, ntk.trav_id() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); /* don't collect constant node */ + uint32_t i{ 0 }; + while ( i < tfi.size() && tfi.size() < ps.max_num_tfi ) + { + node const& n = tfi.at( i++ ); + ntk.foreach_fanin( n, [&]( const auto& f ) { + node const& ni = ntk.get_node( f ); + if ( ntk.visited( ni ) != ntk.trav_id() ) + { + tfi.emplace_back( ni ); + ntk.set_visited( ni, ntk.trav_id() ); + } + } ); + } + } + + /*\return Whether to continue collecting */ + bool collect_wings( node const& root, node const& n, std::vector& supported ) + { + if ( ntk.fanout_size( n ) > ps.max_fanouts ) + { + return true; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( n, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() ) + { + return true; /* next fanout */ + } + + if constexpr ( has_level_v ) + { + if ( ntk.level( p ) > ps.max_level ) + { + return true; /* next fanout */ + } + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( !all_fanins_visited ) + { + return true; /* next fanout */ + } + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + supported.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + /* quit fanout-loop if there are too many nodes collected */ + return supported.size() < ps.max_num_collect; + } ); + return supported.size() < ps.max_num_collect; + } + +private: + Ntk const& ntk; + divisor_collector_params ps; +}; /* divisor_collector */ + +template +class window_simulator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit window_simulator( Ntk const& ntk, std::vector& tts, uint32_t num_pis ) + : ntk( ntk ), tts( tts ), node_to_id( ntk ), num_pis( num_pis ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for TT" ); + assert( ntk.get_node( ntk.get_constant( false ) ) == ntk.get_node( ntk.get_constant( true ) ) && "network types whose constant nodes are different are not supported" ); + + tts.resize( num_pis + 1 ); + auto tt = kitty::create( num_pis ); + tts[0] = tt; + node_to_id[ntk.get_constant( false )] = 0; + + for ( auto i = 0u; i < num_pis; ++i ) + { + kitty::create_nth_var( tt, i ); + tts[i + 1] = tt; + } + } + + /*! \brief Simulates a window in a network. + * + * Every node in `nodes` must have all of its fanins either in `leaves`, or in + * `nodes` and precedes it (i.e., supported and in a topological order). + * + * After simulation, `tts` contains: + * - `tts[0]` is the constant-zero truth table + * - `tts[1]` to `tts[num_pis]` are the projection functions (primary inputs) + * - `tts[num_pis + 1 + i]` is the truth table for the node `nodes[i]` + */ + std::vector& simulate( std::vector const& leaves, std::vector const& nodes ) + { + node_to_id.resize(); + assert( leaves.size() <= num_pis ); + for ( auto i = 0u; i < leaves.size(); ++i ) + { + node_to_id[leaves[i]] = i + 1; + } + + tts.resize( num_pis + 1 ); + for ( auto const& n : nodes ) + { + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + assert( node_to_id.has( f ) && node_to_id[f] < tts.size() && "some node in `nodes` has a fanin outside of `nodes` and `leaves`, or `nodes` is not in a topological order" ); + fanin_values[i] = tts[node_to_id[f]]; + } ); + node_to_id[n] = tts.size(); + tts.emplace_back( ntk.compute( n, std::begin( fanin_values ), std::begin( fanin_values ) + ntk.fanin_size( n ) ) ); + } + + assert( tts.size() == num_pis + 1 + nodes.size() ); + return tts; + } + +private: + Ntk const& ntk; + std::vector& tts; + incomplete_node_map node_to_id; + std::array fanin_values; + uint32_t num_pis; +}; /* window_simulator */ + +} /* namespace mockturtle::experimental::detail */ + +namespace mockturtle::detail +{ + +/* based on abcRefs.c */ +template +class node_mffc_inside +{ +public: + using node = typename Ntk::node; + +public: + explicit node_mffc_inside( Ntk const& ntk ) + : ntk( ntk ) + { + static_assert( has_incr_fanout_size_v, "Ntk does not implement the incr_fanout_size method" ); + static_assert( has_decr_fanout_size_v, "Ntk does not implement the decr_fanout_size method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + } + + template + int32_t call_on_mffc_and_count( node const& n, std::vector const& leaves, Fn&& fn ) + { + /* increment the fanout counters for the leaves */ + for ( const auto& l : leaves ) + ntk.incr_fanout_size( l ); + + /* dereference the node */ + auto count1 = node_deref_rec( n ); + + /* call `fn` on MFFC nodes */ + ntk.incr_trav_id(); + node_mffc_cone_rec( n, true, fn ); + + /* reference it back */ + auto count2 = node_ref_rec( n ); + (void)count2; + assert( count1 == count2 ); + + for ( const auto& l : leaves ) + ntk.decr_fanout_size( l ); + + return count1; + } + + int32_t run( node const& n, std::vector const& leaves, std::vector& inside ) + { + inside.clear(); + return call_on_mffc_and_count( n, leaves, [&inside]( node const& m ) { inside.emplace_back( m ); } ); + } + +private: + /* ! \brief Dereference the node's MFFC */ + int32_t node_deref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return 0; + + int32_t counter = 1; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + ntk.decr_fanout_size( p ); + if ( ntk.fanout_size( p ) == 0 ) + { + counter += node_deref_rec( p ); + } + } ); + + return counter; + } + + /* ! \brief Reference the node's MFFC */ + int32_t node_ref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return 0; + + int32_t counter = 1; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + auto v = ntk.fanout_size( p ); + ntk.incr_fanout_size( p ); + if ( v == 0 ) + { + counter += node_ref_rec( p ); + } + } ); + + return counter; + } + + template + void node_mffc_cone_rec( node const& n, bool top_most, Fn&& fn ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + if ( !top_most && ( ntk.is_pi( n ) || ntk.fanout_size( n ) > 0 ) ) + { + return; + } + + /* recurse on children */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + node_mffc_cone_rec( ntk.get_node( f ), false, fn ); + } ); + + /* collect the internal nodes */ + fn( n ); + } + +private: + Ntk const& ntk; +}; /* node_mffc_inside */ + +template +class window_simulator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using truthtable_t = TT; + + explicit window_simulator( Ntk const& ntk, uint32_t num_divisors, uint32_t max_pis ) + : ntk( ntk ), num_divisors( num_divisors ), tts( num_divisors + 1 ), node_to_index( ntk.size(), 0u ), phase( ntk.size(), false ) + { + auto tt = kitty::create( max_pis ); + tts[0] = tt; + + for ( auto i = 0u; i < tt.num_vars(); ++i ) + { + kitty::create_nth_var( tt, i ); + tts[i + 1] = tt; + } + } + + void resize() + { + if ( ntk.size() > node_to_index.size() ) + { + node_to_index.resize( ntk.size(), 0u ); + } + if ( ntk.size() > phase.size() ) + { + phase.resize( ntk.size(), false ); + } + } + + void assign( node const& n, uint32_t index ) + { + assert( n < node_to_index.size() ); + assert( index < num_divisors + 1 ); + node_to_index[n] = index; + } + + truthtable_t get_tt( signal const& s ) const + { + auto const tt = tts.at( node_to_index.at( ntk.get_node( s ) ) ); + return ntk.is_complemented( s ) ? ~tt : tt; + } + + void set_tt( uint32_t index, truthtable_t const& tt ) + { + tts[index] = tt; + } + + void normalize( std::vector const& nodes ) + { + for ( const auto& n : nodes ) + { + assert( n < phase.size() ); + assert( n < node_to_index.size() ); + + if ( n == 0 ) + { + return; + } + + auto& tt = tts[node_to_index.at( n )]; + if ( kitty::get_bit( tt, 0 ) ) + { + tt = ~tt; + phase[n] = true; + } + else + { + phase[n] = false; + } + } + } + + bool get_phase( node const& n ) const + { + assert( n < phase.size() ); + return phase.at( n ); + } + +private: + Ntk const& ntk; + uint32_t num_divisors; + + std::vector tts; + std::vector node_to_index; + std::vector phase; +}; /* window_simulator */ + +struct default_resub_functor_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: default_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts ) ); + } +}; + +/*! \brief A window-based resub functor which is basically doing functional reduction (fraig). */ +template +class default_resub_functor +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using stats = default_resub_functor_stats; + + explicit default_resub_functor( Ntk const& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, default_resub_functor_stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) const + { + /* The default resubstitution functor does not insert any gates + and consequently does not use the argument `max_inserts`. Other + functors, however, make use of this argument. */ + (void)care; + (void)max_inserts; + assert( kitty::is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + return std::nullopt; + } + +private: + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( const auto& d : divs ) + { + if ( root == d ) + { + break; + } + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + { + continue; /* next */ + } + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + +private: + Ntk const& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t num_divs; + stats& st; +}; /* default_resub_functor */ + +template +void update_node_level_once( Ntk& ntk, node const& n, bool first_node ) +{ + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( first_node ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level_once( ntk, p, false ); + } ); + } + } +} + +/*! \brief Register an `on_modified` event that lazily updates node levels. + * + * This is a trick learnt from ABC's implementation and is used in + * enumeration-based resubstitution algorithms. It only updates the level of + * the modified node and its fanout nodes. The update is not propagated to + * the fanouts' fanouts, thus being fast but inaccurate. + * + * This method can be called in the constructor of an algorithm's implementation + * class. Note that its return value should be stored and + * `release_lazy_level_update_events` should then be called in the destructor + * of the class. + */ +template::modified_event_type>> +event_t register_lazy_level_update_events( Ntk& ntk ) +{ + static_assert( has_foreach_fanout_v, "Ntk does not have fanout interface." ); + using node = typename Ntk::node; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level_once( ntk, n, true ); + }; + + return ntk.events().register_modified_event( update_level_of_existing_node ); +} + +template::modified_event_type>> +void release_lazy_level_update_events( Ntk& ntk, event_t& event ) +{ + ntk.events().release_modified_event( event ); +} + +} /* namespace mockturtle::detail */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/detail/switching_activity.hpp b/include/mockturtle/algorithms/detail/switching_activity.hpp new file mode 100644 index 0000000..c5df29c --- /dev/null +++ b/include/mockturtle/algorithms/detail/switching_activity.hpp @@ -0,0 +1,70 @@ +/* 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 switching_activity.hpp + \brief Utility to compute the switching activity + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include "../simulation.hpp" + +#include +#include + +namespace mockturtle::detail +{ + +/*! \brief Switching Activity. + * + * This function computes the switching activity for each node + * in the network by performing random simulation. + * + * \param ntk Network + * \param simulation_size Number of simulation bits + */ +template +std::vector switching_activity( Ntk const& ntk, unsigned simulation_size = 2048 ) +{ + std::vector sw_map( ntk.size() ); + partial_simulator sim( ntk.num_pis(), simulation_size ); + + auto tts = simulate_nodes( ntk, sim ); + + ntk.foreach_node( [&]( auto const& n ) { + float ones = static_cast( kitty::count_ones( tts[n] ) ); + float activity = 2.0 * ones / simulation_size * ( simulation_size - ones ) / simulation_size; + sw_map[ntk.node_to_index( n )] = activity; + } ); + + return sw_map; +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/include/mockturtle/algorithms/dont_cares.hpp b/include/mockturtle/algorithms/dont_cares.hpp new file mode 100644 index 0000000..0e03d54 --- /dev/null +++ b/include/mockturtle/algorithms/dont_cares.hpp @@ -0,0 +1,358 @@ +/* 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 dont_cares.hpp + \brief Compute don't cares + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../algorithms/reconv_cut.hpp" +#include "../algorithms/simulation.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/node_map.hpp" +#include "../views/color_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Computes satisfiability don't cares of a set of nodes. + * + * This function returns an under approximation of input assignments that + * cannot occur on a given set of nodes in a network. They may therefore be + * used as don't care conditions. + * + * \param ntk Network + * \param leaves Set of nodes + * \param max_tfi_inputs Maximum number of inputs in the transitive fanin. + */ +template +kitty::dynamic_truth_table satisfiability_dont_cares( Ntk const& ntk, std::vector> const& leaves, uint64_t max_tfi_inputs = 16u ) +{ + reconvergence_driven_cut_parameters ps; + ps.max_leaves = max_tfi_inputs; + reconvergence_driven_cut_statistics st; + + detail::reconvergence_driven_cut_impl cuts( ntk, ps, st ); + auto const extended_leaves = cuts.run( leaves ).first; + + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view color_ntk{ fanout_ntk }; + + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, leaves ) }; + window_view window_ntk{ color_ntk, extended_leaves, leaves, gates }; + + default_simulator sim( window_ntk.num_pis() ); + const auto tts = simulate_nodes( window_ntk, sim ); + + /* first create care and then invert */ + kitty::dynamic_truth_table care( static_cast( leaves.size() ) ); + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + for ( auto j = 0u; j < leaves.size(); ++j ) + { + entry |= kitty::get_bit( tts[leaves[j]], i ) << j; + } + kitty::set_bit( care, entry ); + } + return ~care; +} + +/*! \brief Computes observability don't cares of a node. + * + * This function returns input assignments for which a change of the + * node's value cannot be observed at any of the roots. They may + * therefore be used as don't care conditions. + * + * \param ntk Network + * \param node A node in the ntk + * \param leaves Set of leave nodes + * \param roots Set of root nodes + */ +template +kitty::dynamic_truth_table observability_dont_cares( Ntk const& ntk, node const& n, std::vector> const& leaves, std::vector> const& roots ) +{ + fanout_view fanout_ntk{ ntk }; + fanout_ntk.clear_visited(); + color_view color_ntk{ fanout_ntk }; + + std::vector> gates{ collect_nodes( color_ntk, leaves, roots ) }; + window_view window_ntk{ color_ntk, leaves, roots, gates }; + + default_simulator sim( window_ntk.num_pis() ); + unordered_node_map node_to_value0( ntk ); + unordered_node_map node_to_value1( ntk ); + + node_to_value0[n] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + simulate_nodes( ntk, node_to_value0, sim ); + + node_to_value1[n] = ~sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + simulate_nodes( ntk, node_to_value1, sim ); + + kitty::dynamic_truth_table care( static_cast( leaves.size() ) ); + for ( const auto& r : roots ) + { + care |= node_to_value0[r] ^ node_to_value1[r]; + } + return ~care; +} + +namespace detail +{ + +template +void clearTFO_rec( Ntk const& ntk, Container& tts, node const& n, std::set>& roots, int level ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) /* visited */ + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + tts.erase( n ); + + if ( level == 0 ) + { + roots.insert( n ); + return; + } + + ntk.foreach_fanout( n, [&]( auto const& fo ) { + clearTFO_rec( ntk, tts, fo, roots, level - 1 ); + } ); +} + +template +void simulate_TFO_rec( Ntk const& ntk, node const& n, partial_simulator const& sim, Container& tts, int level ) +{ + if ( ntk.visited( n ) == ntk.trav_id() ) /* visited */ + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + if ( !tts.has( n ) ) + { + simulate_node( ntk, n, tts, sim ); + } + else + { + assert( tts[n].num_bits() == sim.num_bits() ); + } + + if ( level == 0 ) + { + return; + } + + ntk.foreach_fanout( n, [&]( auto const& fo ) { + simulate_TFO_rec( ntk, fo, sim, tts, level - 1 ); + } ); +} + +} /* namespace detail */ + +/*! \brief Compute the observability don't care patterns in a partial_simulator with respect to a node. + * + * A pattern is unobservable w.r.t. a node `n` if under this input assignment, + * replacing `n` with `!n` does not affect the value of any primary output or + * any leaf node of `levels` levels of transitive fanout cone. + * + * Return value: a `partial_truth_table` with the same length as `sim.num_bits()`. + * A `1` in it corresponds to an unobservable pattern. + * + * \param sim The `partial_simulator` containing the patterns to be tested. + * \param tts Stores the simulation signatures of each node. Can be empty or incomplete. + * \param levels Level of transitive fanout to consider. -1 = consider until PO. + */ +template> +kitty::partial_truth_table observability_dont_cares( Ntk const& ntk, node const& n, partial_simulator const& sim, Container& tts, int levels = -1 ) +{ + std::set> roots; + unordered_node_map tts_roots( ntk ); + + /* Make sure n is up-to-date and record its truth table. */ + if ( !tts.has( n ) || tts[n].num_bits() != sim.num_bits() ) + { + simulate_node( ntk, n, tts, sim ); + } + auto const tt_n = tts[n]; + + /* Clear (mark) TFO nodes and collect roots (leaves). */ + ntk.incr_trav_id(); + detail::clearTFO_rec( ntk, tts, n, roots, levels ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) /* PO is in TFO */ + { + roots.insert( ntk.get_node( f ) ); + } + } ); + + /* Simulate the negated version and collect TTs of roots. */ + tts[n] = ~tt_n; + ntk.incr_trav_id(); + detail::simulate_TFO_rec( ntk, n, sim, tts, levels ); + for ( const auto& r : roots ) + { + tts_roots[r] = tts[r]; + } + + /* Revert the negation and simulate again */ + tts[n] = tt_n; + ntk.incr_trav_id(); + detail::clearTFO_rec( ntk, tts, n, roots, levels ); + ntk.incr_trav_id(); + detail::simulate_TFO_rec( ntk, n, sim, tts, levels ); + + kitty::partial_truth_table care( sim.num_bits() ); + for ( const auto& r : roots ) + { + assert( tts[r].num_bits() == sim.num_bits() ); + care |= tts[r] ^ tts_roots[r]; + } + + if constexpr ( has_EXODC_interface_v ) + { + if ( levels == -1 ) + { + for ( auto i = 0u; i < sim.num_bits(); ++i ) + { + if ( kitty::get_bit( care, i ) ) + { + kitty::cube pat1, pat2; + ntk.foreach_po( [&]( auto const& f, auto po_index ) + { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) /* PO is in TFO */ + { + pat1.set_mask( po_index ); + pat2.set_mask( po_index ); + assert( tts[f].num_bits() == sim.num_bits() ); + assert( tts_roots[f].num_bits() == sim.num_bits() ); + if ( kitty::get_bit( tts[f], i ) ^ ntk.is_complemented( f ) ) + pat1.set_bit( po_index ); + if ( kitty::get_bit( tts_roots[f], i ) ^ ntk.is_complemented( f ) ) + pat2.set_bit( po_index ); + } + }); + + if ( ntk.are_observably_equivalent( pat1, pat2 ) ) + { + kitty::clear_bit( care, i ); + } + } + } + } + } + + return ~care; +} + +/*! \brief Check if a pattern is observable with respect to a node. + * + * A pattern is unobservable w.r.t. a node `n` if under this input assignment, + * replacing `n` with `!n` does not affect the value of any primary output or + * any leaf node of `levels` levels of transitive fanout cone. + * + * \param levels Level of transitive fanout to consider. -1 = consider until PO. + */ +template +bool pattern_is_observable( Ntk const& ntk, node const& n, std::vector const& pattern, int levels = -1 ) +{ + partial_simulator sim( ntk.num_pis(), 0 ); + sim.add_pattern( pattern ); + unordered_node_map tts( ntk ); + + auto const care = observability_dont_cares( ntk, n, sim, tts, levels ); + return !kitty::is_const0( care ); +} + +/*! \brief SAT-based satisfiability don't cares checker + * + * Initialize this class with a network and then call `is_dont_care` on a node + * to check whether the given assignment is a satisfiability don't care. + * + * The assignment is assumed to be directly at the inputs of the gate, not + * taking into account possible complemented fanins. + */ +template +struct satisfiability_dont_cares_checker +{ + explicit satisfiability_dont_cares_checker( Ntk const& ntk ) + : ntk_( ntk ), + literals_( node_literals( ntk ) ) + { + init(); + } + + bool is_dont_care( node const& n, std::vector const& assignment ) + { + if ( ntk_.fanin_size( n ) != assignment.size() ) + return false; + + std::vector assumptions( assignment.size() ); + ntk_.foreach_fanin( n, [&]( auto const& f, auto i ) { + assumptions[i] = lit_not_cond( literals_[ntk_.get_node( f )], assignment[i] == ntk_.is_complemented( f ) ); + } ); + + return solver_.solve( &assumptions[0], &assumptions[0] + assumptions.size(), 0 ) == percy::failure; + } + +private: + void init() + { + generate_cnf( + ntk_, [&]( auto const& clause ) { + solver_.add_clause( clause ); + }, + literals_ ); + } + +private: + Ntk const& ntk_; + percy::bsat_wrapper solver_; + node_map literals_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/dsd_decomposition.hpp b/include/mockturtle/algorithms/dsd_decomposition.hpp new file mode 100644 index 0000000..66bbaf6 --- /dev/null +++ b/include/mockturtle/algorithms/dsd_decomposition.hpp @@ -0,0 +1,238 @@ +/* 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 dsd_decomposition.hpp + \brief DSD decomposition + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for dsd_decomposition */ +struct dsd_decomposition_params +{ + /*! \brief Apply XOR decomposition. */ + bool with_xor{ true }; +}; + +namespace detail +{ + +template +class dsd_decomposition_impl +{ +public: + dsd_decomposition_impl( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector> const& children, Fn&& on_prime, dsd_decomposition_params const& ps ) + : _ntk( ntk ), + remainder( func ), + pis( children ), + _on_prime( on_prime ), + _ps( ps ) + { + for ( auto i = 0u; i < func.num_vars(); ++i ) + { + if ( kitty::has_var( func, i ) ) + { + support.push_back( i ); + } + } + } + + signal run() + { + /* terminal cases */ + if ( kitty::is_const0( remainder ) ) + { + return _ntk.get_constant( false ); + } + if ( kitty::is_const0( ~remainder ) ) + { + return _ntk.get_constant( true ); + } + + /* projection case */ + if ( support.size() == 1u ) + { + auto var = remainder.construct(); + kitty::create_nth_var( var, support.front() ); + if ( remainder == var ) + { + return pis[support.front()]; + } + else + { + if ( remainder != ~var ) + { + fmt::print( "remainder = {}, vars = {}\n", kitty::to_binary( remainder ), remainder.num_vars() ); + assert( false ); + } + assert( remainder == ~var ); + return _ntk.create_not( pis[support.front()] ); + } + } + + /* try top decomposition */ + for ( auto var : support ) + { + if ( auto res = kitty::is_top_decomposable( remainder, var, &remainder, _ps.with_xor ); + res != kitty::top_decomposition::none ) + { + /* remove var from support, pis do not change */ + support.erase( std::remove( support.begin(), support.end(), var ), support.end() ); + const auto right = run(); + + switch ( res ) + { + default: + assert( false ); + case kitty::top_decomposition::and_: + return _ntk.create_and( pis[var], right ); + case kitty::top_decomposition::or_: + return _ntk.create_or( pis[var], right ); + case kitty::top_decomposition::lt_: + return _ntk.create_lt( pis[var], right ); + case kitty::top_decomposition::le_: + return _ntk.create_le( pis[var], right ); + case kitty::top_decomposition::xor_: + return _ntk.create_xor( pis[var], right ); + } + } + } + + /* try bottom decomposition */ + for ( auto j = 1u; j < support.size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + if ( auto res = kitty::is_bottom_decomposable( remainder, support[i], support[j], &remainder, _ps.with_xor ); + res != kitty::bottom_decomposition::none ) + { + /* update pis based on decomposition type */ + switch ( res ) + { + default: + assert( false ); + case kitty::bottom_decomposition::and_: + pis[support[i]] = _ntk.create_and( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::or_: + pis[support[i]] = _ntk.create_or( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::lt_: + pis[support[i]] = _ntk.create_lt( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::le_: + pis[support[i]] = _ntk.create_le( pis[support[i]], pis[support[j]] ); + break; + case kitty::bottom_decomposition::xor_: + pis[support[i]] = _ntk.create_xor( pis[support[i]], pis[support[j]] ); + break; + } + + /* remove var from support */ + support.erase( support.begin() + j ); + + return run(); + } + } + } + + /* cannot decompose anymore */ + std::vector> new_pis; + for ( auto var : support ) + { + new_pis.push_back( pis[var] ); + } + auto prime_large = remainder; + kitty::min_base_inplace( prime_large ); + auto prime = kitty::shrink_to( prime_large, static_cast( support.size() ) ); + return _on_prime( prime, new_pis ); + } + +private: + Ntk& _ntk; + kitty::dynamic_truth_table remainder; + std::vector support; + std::vector> pis; + Fn&& _on_prime; + dsd_decomposition_params const& _ps; +}; + +} // namespace detail + +/*! \brief DSD decomposition + * + * This function applies DSD decomposition on an input truth table and + * constructs a network based on all possible decompositions. If the truth + * table is only partially decomposable, then the remaining *prime function* + * is returned back to the caller using the call back `on_prime` together with + * the computed primary inputs for that remainder. + * + * The `on_prime` function must be of type `NtkDest::signal( + * kitty::dynamic_truth_table const&, std::vector const&)`. + * + * **Required network functions:** + * - `create_not` + * - `create_and` + * - `create_or` + * - `create_lt` + * - `create_le` + * - `create_xor` + */ +template +signal dsd_decomposition( Ntk& ntk, kitty::dynamic_truth_table const& func, std::vector> const& children, Fn&& on_prime, dsd_decomposition_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_lt_v, "Ntk does not implement the create_lt method" ); + static_assert( has_create_le_v, "Ntk does not implement the create_le method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + + detail::dsd_decomposition_impl impl( ntk, func, children, on_prime, ps ); + return impl.run(); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/emap.hpp b/include/mockturtle/algorithms/emap.hpp new file mode 100644 index 0000000..055d9b2 --- /dev/null +++ b/include/mockturtle/algorithms/emap.hpp @@ -0,0 +1,5972 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2024 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 emap.hpp + \brief An extended technology mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "../networks/aig.hpp" +#include "../networks/block.hpp" +#include "../networks/klut.hpp" +#include "../utils/cuts.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/tech_library.hpp" +#include "../views/binding_view.hpp" +#include "../views/cell_view.hpp" +#include "../views/choice_view.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "detail/mffc_utils.hpp" +#include "detail/switching_activity.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for emap. + * + * The data structure `emap_params` holds configurable parameters + * with default arguments for `emap`. + */ +struct emap_params +{ + emap_params() + { + cut_enumeration_ps.cut_limit = 16; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 16. + * The maximum cut limit is 19. + * By default, truth table minimization + * is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Do area-oriented mapping. */ + bool area_oriented_mapping{ false }; + + /*! \brief Maps using multi-output gates */ + bool map_multioutput{ false }; + + /*! \brief Matching mode + * + * Boolean uses Boolean matching (up to 6-input cells), + * Structural uses pattern matching for fully-DSD cells, + * Hybrid combines the two. + */ + enum matching_mode_t + { + boolean, + structural, + hybrid + } matching_mode = hybrid; + + /*! \brief Target required time (for each PO). */ + double required_time{ 0.0f }; + + /*! \brief Required time relaxation in percentage (10 = 10%). */ + double relax_required{ 0.0f }; + + /*! \brief Custom input arrival times. */ + std::vector arrival_times{}; + + /*! \brief Custom output required times. */ + std::vector required_times{}; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 3u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Number of rounds for exact switching power optimization. */ + uint32_t eswp_rounds{ 0u }; + + /*! \brief Number of patterns for switching activity computation. */ + uint32_t switching_activity_patterns{ 2048u }; + + /*! \brief Compute area-oriented alternative matches */ + bool use_match_alternatives{ true }; + + /*! \brief Remove the cuts that are contained in others */ + bool remove_dominated_cuts{ false }; + + /*! \brief Remove overlapping multi-output cuts */ + bool remove_overlapping_multicuts{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for emap. + * + * The data structure `emap_stats` provides data collected by running + * `emap`. + */ +struct emap_stats +{ + /*! \brief Area result. */ + double area{ 0 }; + /*! \brief Worst delay result. */ + double delay{ 0 }; + /*! \brief Power result. */ + double power{ 0 }; + /*! \brief Power result. */ + uint32_t inverters{ 0 }; + + /*! \brief Mapped multi-output gates. */ + uint32_t multioutput_gates{ 0 }; + + /*! \brief Runtime for multi-output matching. */ + stopwatch<>::duration time_multioutput{ 0 }; + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Delay and area stats for each round. */ + std::vector round_stats{}; + + /*! \brief Mapping error. */ + bool mapping_error{ false }; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay ); + if ( power != 0 ) + std::cout << fmt::format( " Power = {:>5.2f};\n", power ); + else + std::cout << "\n"; + if ( multioutput_gates ) + { + std::cout << fmt::format( "[i] Multi-output gates = {:>5}\n", multioutput_gates ); + std::cout << fmt::format( "[i] Multi-output runtime = {:>5.2f} secs\n", to_seconds( time_multioutput ) ); + } + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +#pragma region cut set +template +struct cut_enumeration_emap_cut +{ + /* stats */ + uint32_t delay; + float flow; + bool ignore; + + /* pattern index for structural matching*/ + uint32_t pattern_index; + + /* function */ + kitty::static_truth_table<6> function; + + /* list of supergates matching the cut for positive and negative output phases */ + std::array> const*, 2> supergates; + /* input negations, 0: pos, 1: neg */ + std::array negations; +}; + +struct cut_enumeration_emap_multi_cut +{ + /* stats */ + uint64_t id{ 0 }; +}; + +enum class emap_cut_sort_type +{ + DELAY = 0, + DELAY2 = 1, + AREA = 2, + AREA2 = 3, + NONE = 4 +}; + +template +class emap_cut_set +{ +public: + /*! \brief Standard constructor. + */ + emap_cut_set() + { + clear(); + } + + /*! \brief Assignment operator. + */ + emap_cut_set& operator=( emap_cut_set const& other ) + { + if ( this != &other ) + { + _pcend = _pend = _pcuts.begin(); + _set_limit = other._set_limit; + + auto it = other.begin(); + while ( it != other.end() ) + { + **_pend++ = **it++; + ++_pcend; + } + } + + return *this; + } + + /*! \brief Clears a cut set. + */ + void clear() + { + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } + } + + /*! \brief Sets the cut limit. + */ + void set_cut_limit( uint32_t limit ) + { + _set_limit = std::min( MaxCuts, limit ); + } + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ) + { + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; + } + + /*! \brief Appends a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param cut Cut to insert + */ + void append_cut( CutType const& cut ) + { + assert( _pend != _pcuts.end() ); + + **_pend++ = cut; + ++_pcend; + } + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const + { + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; + } + + static bool sort_delay( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_delay2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1->flow < c2->flow - eps; + } + + static bool sort_area( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + return c1->delay < c2->delay - eps; + } + + static bool sort_area2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->ignore && c2->ignore ) + return true; + if ( c1->ignore && !c2->ignore ) + return false; + if ( c1->flow < c2->flow - eps ) + return true; + if ( c1->flow > c2->flow + eps ) + return false; + if ( c1->delay < c2->delay - eps ) + return true; + if ( c1->delay > c2->delay + eps ) + return false; + return c1.size() < c2.size(); + } + + /*! \brief Compare two cuts using sorting functions. + * + * This method compares two cuts using a sorting function. + * + * \param cut1 first cut. + * \param cut2 second cut. + * \param sort sorting function. + */ + static bool compare( CutType const& cut1, CutType const& cut2, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + if ( sort == emap_cut_sort_type::DELAY ) + { + return sort_delay( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::DELAY2 ) + { + return sort_delay2( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA ) + { + return sort_area( cut1, cut2 ); + } + else if ( sort == emap_cut_sort_type::AREA2 ) + { + return sort_area2( cut1, cut2 ); + } + else + { + return false; + } + } + + /*! \brief Inserts a cut into a set without checking dominance. + * + * This method will insert a cut into a set and maintain an order. This + * method doesn't remove the cuts that are dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param sort Cut prioritization function. + */ + void simple_insert( CutType const& cut, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + /* insert cut in a sorted way */ + typename std::array::iterator ipos = _pcuts.begin(); + + bool limit_reached = std::distance( _pcuts.begin(), _pend ) >= _set_limit; + + /* do not insert if worst than set_limit */ + if ( limit_reached ) + { + if ( sort == emap_cut_sort_type::AREA && !sort_area( cut, **( ipos + _set_limit - 1 ) ) ) + { + return; + } + else if ( sort != emap_cut_sort_type::AREA ) + { + return; + } + } + + if ( sort == emap_cut_sort_type::NONE ) + { + ipos = _pend; + } + else /* AREA */ + { + ipos = std::upper_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } ); + } + + /* check for redundant cut */ + typename std::array::iterator jpos = ipos; + if ( cut->ignore ) + { + while ( jpos != _pcuts.begin() ) + { + --jpos; + if ( ( *jpos )->size() < cut.size() ) + break; + if ( ( *jpos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *jpos )->begin() ) ) + return; + } + } + else if ( ipos != _pcuts.begin() ) + { + if ( ( *( ipos - 1 ) )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *( ipos - 1 ) )->begin() ) ) + { + return; + } + } + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() || limit_reached ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; + } + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. Variable `skip0` tell to skip the dominance check on + * cut zero. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param skip0 Skip dominance check on cut zero. + * \param sort Cut prioritization function. + */ + void insert( CutType const& cut, bool skip0 = false, emap_cut_sort_type sort = emap_cut_sort_type::NONE ) + { + auto begin = _pcuts.begin(); + + if ( skip0 && _pend != _pcuts.begin() ) + ++begin; + + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + simple_insert( cut, sort ); + } + + /*! \brief Replaces a cut of the set. + * + * This method replaces the cut at position `index` in the set by `cut` + * and maintains the cuts order. The function does not check whether + * index is in the valid range. + * + * \param index Index of the cut to replace. + * \param cut Cut to insert. + */ + void replace( uint32_t index, CutType const& cut ) + { + *_pcuts[index] = cut; + } + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ) + { + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; + } + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ) + { + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } + } + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, emap_cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + + /*! \brief Returns if the cut set contains already `cut`. */ + bool is_contained( CutType const& cut ) + { + typename std::array::iterator ipos = _pcuts.begin(); + + while ( ipos != _pend ) + { + if ( ( *ipos )->signature() == cut.signature() && std::equal( cut.begin(), cut.end(), ( *ipos )->begin() ) ) + return true; + ++ipos; + } + + return false; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; + uint32_t _set_limit{ MaxCuts }; +}; +#pragma endregion + +#pragma region Hashing +template +struct emap_triple_hash +{ + inline uint64_t operator()( const std::array& p ) const + { + uint64_t seed = hash_block( p[0] ); + + for ( uint32_t i = 1; i < max_multioutput_cut_size; ++i ) + { + hash_combine( seed, hash_block( p[i] ) ); + } + + return seed; + } +}; +#pragma endregion + +template +struct best_gate_emap +{ + supergate const* gate; + double arrival; + float area; + float flow; + unsigned phase : 16; + unsigned cut : 12; + unsigned size : 4; +}; + +template +struct node_match_emap +{ + /* best gate match for positive and negative output phases */ + supergate const* best_gate[2]; + /* alternative best gate for positibe and negative output phase */ + best_gate_emap best_alternative[2]; + /* fanin pin phases for both output phases */ + uint16_t phase[2]; + /* best cut index for both phases */ + uint16_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match; + /* node is mapped to a multi-output gate */ + bool multioutput_match[2]; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg */ + uint32_t map_refs[2]; + /* references estimation */ + float est_refs[2]; + /* area flow */ + float flows[2]; +}; + +template +class emap_impl +{ +private: + union multi_match_data + { + uint64_t data{ 0 }; + struct + { + uint64_t in_tfi : 1; + uint64_t cut_index : 31; + uint64_t node_index : 32; + }; + }; + union multioutput_info + { + uint32_t data; + struct + { + unsigned index : 29; + unsigned lowest_index : 1; + unsigned highest_index : 1; + unsigned has_info : 1; + }; + }; + +public: + static constexpr float epsilon = 0.0005; + static constexpr uint32_t max_cut_num = 20; + using cut_t = cut>; + using cut_set_t = emap_cut_set; + using cut_merge_t = typename std::array; + using fanin_cut_t = typename std::array; + using support_t = typename std::array; + using TT = kitty::static_truth_table<6>; + using truth_compute_t = typename std::array; + using node_match_t = std::vector>; + using klut_map = std::unordered_map, 2>>; + using block_map = std::unordered_map, 2>>; + + static constexpr uint32_t max_multioutput_cut_size = 3; + static constexpr uint32_t max_multioutput_output_size = 2; + using multi_cuts_t = fast_network_cuts; + using multi_cut_t = typename multi_cuts_t::cut_t; + using multi_leaves_set_t = std::array; + using multi_output_set_t = std::vector; + using multi_hash_t = phmap::flat_hash_map>; + using multi_match_t = std::array; + using multi_cut_set_t = std::vector>; + using multi_single_matches_t = std::vector; + using multi_matches_t = std::vector>; + + using clock = typename std::chrono::steady_clock; + using time_point = typename clock::time_point; + +public: + explicit emap_impl( Ntk const& ntk, tech_library const& library, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector( 0 ) ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + explicit emap_impl( Ntk const& ntk, tech_library const& library, std::vector const& switch_activity, emap_params const& ps, emap_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + node_tuple_match( ntk.size() ), + switch_activity( switch_activity ), + cuts( ntk.size() ) + { + std::memset( node_tuple_match.data(), 0, sizeof( multioutput_info ) * ntk.size() ); + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + tmp_visited.reserve( 100 ); + } + + cell_view run_block() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_block_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover_block( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_klut() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* multi-output initialization */ + if ( ps.map_multioutput && ps.matching_mode != emap_params::structural ) + { + compute_multioutput_match(); + } + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* search for large matches */ + if ( ps.matching_mode == emap_params::structural || CutSize > 6 ) + { + if ( !compute_struct_match() ) + { + return res; + } + } + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + + binding_view run_node_map() + { + time_begin = clock::now(); + + auto [res, old2new] = initialize_map_network(); + + /* [i] multi-output support is currently not implemented */ + + /* compute and save topological order */ + init_topo_order(); + + /* init arrival time */ + if ( !init_arrivals() ) + return res; + + /* compute cuts, matches, and initial mapping */ + if ( !ps.area_oriented_mapping ) + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + else + { + if ( !compute_mapping_match_node() ) + { + return res; + } + } + + /* run area recovery */ + if ( !improve_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + st.time_total = ( clock::now() - time_begin ); + + return res; + } + +private: + bool improve_mapping() + { + /* compute mapping using global area flow */ + uint32_t i = 0; + while ( i++ < ps.area_flow_rounds ) + { + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using exact area */ + i = 0; + compute_required_time( true ); + while ( i++ < ps.ela_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + /* compute mapping using exact switching activity estimation */ + i = 0; + while ( i++ < ps.eswp_rounds ) + { + if ( !compute_mapping_exact_reversed() ) + { + return false; + } + } + + return true; + } + +#pragma region Core + template + bool compute_mapping_match() + { + bool warning_box = false; + + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + + if ( !compute_matches_node( n, warning_box ) ) + { + continue; + } + + /* load multi-output cuts and data */ + if ( ps.map_multioutput && node_tuple_match[index].has_info ) + { + match_multi_add_cuts( n ); + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + + /* try multi-output matches */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + if ( match_multioutput( n ) ) + multi_node_update( n ); + } + } + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + inline bool compute_matches_node( node const& n, bool& warning_box ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0.0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.best_alternative[0].flow = node_data.best_alternative[1].flow = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + node_data.best_alternative[0].arrival = node_data.best_alternative[1].arrival = 0.0f; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_zero_cut( index ); + match_constants( index ); + } + return false; + } + else if ( ntk.is_pi( n ) ) + { + node_data.flows[0] = 0.0f; + node_data.best_alternative[0].flow = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + node_data.best_alternative[1].flow = lib_inv_area / node_data.est_refs[1]; + /* skip if cuts have been computed before */ + if ( cuts[index].size() == 0 ) + { + add_unit_cut( index ); + } + return false; + } + + if ( ps.matching_mode == emap_params::structural ) + return true; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + warning_box |= initialize_box( n ); + return false; + } + } + + /* compute cuts for node */ + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + merge_cuts2( n ); + } + else + { + merge_cuts( n ); + } + + return true; + } + + template + void merge_cuts2( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + ntk.foreach_fanin( ntk.index_to_node( index ), [this]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* move pre-computed structural cuts to a temporary cutset */ + bool reinsert_cuts = false; + if ( rcuts.size() ) + { + temp_cuts.clear(); + for ( auto& cut : rcuts ) + { + if ( ( *cut )->ignore ) + continue; + recompute_cut_data( *cut, n ); + temp_cuts.simple_insert( *cut ); + reinsert_cuts = true; + } + rcuts.clear(); + } + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + new_cut->pattern_index = 0; + fanin_cut_t vcuts; + + for ( auto const& c1 : *lcuts[0] ) + { + /* skip cuts of pattern matching */ + if ( ( *c1 )->pattern_index > 1 ) + continue; + vcuts[0] = c1; + + for ( auto const& c2 : *lcuts[1] ) + { + /* skip cuts of pattern matching */ + if ( ( *c2 )->pattern_index > 1 ) + continue; + + if ( !c1->merge( *c2, new_cut, max_cut_size ) ) + { + continue; + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + continue; + } + + /* compute function */ + vcuts[1] = c2; + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + if ( reinsert_cuts ) + { + for ( auto const& cut : temp_cuts ) + { + rcuts.simple_insert( *cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + void merge_cuts( node const& n ) + { + static constexpr uint32_t max_cut_size = CutSize > 6 ? 6 : CutSize; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + cut_t best_cut; + + /* compute cuts */ + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + } ); + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + fanin_cut_t vcuts; + + if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit ) + { + cut_t new_cut, tmp_cut; + + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, max_cut_size ) ) + { + return true; /* continue */ + } + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + else if ( fanin == 1 ) + { + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + vcuts[0] = cut; + + compute_truth_table( index, vcuts, fanin, new_cut ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + + cuts_total += rcuts.size(); + + add_unit_cut( index ); + } + + bool compute_struct_match() + { + if ( ps.matching_mode == emap_params::boolean ) + return true; + + /* compatible only with AIGs */ + if constexpr ( !is_aig_network_type_v ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library works only with AIGs\n"; + return false; + } + return true; + } + + /* no large gates identified */ + if ( library.num_structural_gates() == 0 ) + { + if ( ps.matching_mode == emap_params::structural ) + { + std::cerr << "[e] MAP ERROR: structural library is empty\n"; + return false; + } + return true; + } + + bool warning_box = false; + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + add_zero_cut( index ); + match_constants( index ); + continue; + } + else if ( ntk.is_pi( n ) ) + { + add_unit_cut( index ); + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + add_unit_cut( index ); + continue; + } + } + + /* compute cuts for node */ + merge_cuts_structural( n ); + } + + if ( warning_box ) + { + std::cerr << "[i] MAP WARNING: not mapped don't touch gates are treated as sequential black boxes\n"; + } + + /* round stats */ + if ( ps.verbose ) + { + st.round_stats.push_back( fmt::format( "[i] SCuts : Cuts = {:>12d} Time = {:>12.2f}\n", cuts_total, to_seconds( clock::now() - time_begin ) ) ); + } + + return true; + } + + void merge_cuts_structural( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + emap_cut_sort_type sort = emap_cut_sort_type::AREA; + + /* compute cuts */ + const auto fanin = 2; + std::array children_phase; + ntk.foreach_fanin( ntk.index_to_node( index ), [&]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + children_phase[i] = ntk.is_complemented( child ) ? 1 : 0; + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + /* set cut limit for run-time optimization*/ + rcuts.set_cut_limit( ps.cut_enumeration_ps.cut_limit ); + + cut_t new_cut; + std::vector vcuts( fanin ); + + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + /* filter large cuts */ + if ( c1->size() + c2->size() > CutSize || c1->size() + c2->size() > NInputs ) + continue; + /* filter cuts involving constants */ + if ( ( *c1 )->pattern_index == 0 || ( *c2 )->pattern_index == 0 ) + continue; + + vcuts[0] = c1; + vcuts[1] = c2; + uint32_t pattern_id1 = ( ( *c1 )->pattern_index << 1 ) | children_phase[0]; + uint32_t pattern_id2 = ( ( *c2 )->pattern_index << 1 ) | children_phase[1]; + if ( pattern_id1 > pattern_id2 ) + { + std::swap( vcuts[0], vcuts[1] ); + std::swap( pattern_id1, pattern_id2 ); + } + + uint32_t new_pattern = library.get_pattern_id( pattern_id1, pattern_id2 ); + + /* pattern not matched */ + if ( new_pattern == UINT32_MAX ) + continue; + + create_structural_cut( new_cut, vcuts, new_pattern, pattern_id1, pattern_id2 ); + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + continue; + + /* match cut and compute data */ + compute_cut_data_structural( new_cut, n ); + + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + } + + template + bool compute_mapping_match_node() + { + for ( auto const& n : topo_order ) + { + auto const index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.best_gate[0] = node_data.best_gate[1] = nullptr; + node_data.same_match = 0; + node_data.multioutput_match[0] = node_data.multioutput_match[1] = false; + node_data.required[0] = node_data.required[1] = std::numeric_limits::max(); + node_data.map_refs[0] = node_data.map_refs[1] = 0; + node_data.est_refs[0] = node_data.est_refs[1] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = node_data.flows[1] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + add_zero_cut( index ); + match_constants( index ); + continue; + } + else if ( ntk.is_pi( n ) ) + { + /* all terminals have flow 0 */ + node_data.flows[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.flows[1] = lib_inv_area / node_data.est_refs[1]; + add_unit_cut( index ); + continue; + } + + /* compute the node mapping */ + add_node_cut( n ); + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* select alternative matches to use */ + select_alternatives( n ); + } + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + void add_node_cut( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& rcuts = &cuts[index]; + + std::vector fanin_indexes; + fanin_indexes.reserve( Ntk::max_fanin_size ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + fanin_indexes.push_back( ntk.node_to_index( ntk.get_node( f ) ) ); + } ); + + assert( fanin_indexes.size() <= CutSize ); + + cut_t new_cut = rcuts.add_cut( fanin_indexes.begin(), fanin_indexes.end() ); + new_cut->function = kitty::extend_to<6>( ntk.node_function( n ) ); + + /* match cut and compute data */ + compute_cut_data( new_cut, n ); + + ++cuts_total; + } + + template + bool compute_mapping() + { + for ( auto const& n : topo_order ) + { + uint32_t index = ntk.node_to_index( n ); + + /* reset mapping */ + node_match[index].map_refs[0] = node_match[index].map_refs[1] = 0u; + + if ( ntk.is_constant( n ) ) + continue; + if ( ntk.is_pi( n ) ) + { + node_match[index].flows[1] = lib_inv_area / node_match[index].est_refs[1]; + node_match[index].best_alternative[1].flow = lib_inv_area / node_match[index].est_refs[1]; + continue; + } + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + } + continue; + } + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + /* try a multi-output match */ + if constexpr ( DO_AREA ) + { + if ( ps.map_multioutput && node_tuple_match[index].highest_index ) + { + bool multi_success = match_multioutput( n ); + if ( multi_success ) + multi_node_update( n ); + } + } + + assert( node_match[index].arrival[0] < node_match[index].required[0] + epsilon ); + assert( node_match[index].arrival[1] < node_match[index].required[1] + epsilon ); + } + + double area_old = area; + bool success = set_mapping_refs_and_req(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool compute_mapping_exact_reversed() + { + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_constant( *it ) || ntk.is_pi( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip not mapped nodes */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + continue; + } + } + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + double old_required = -1; + if ( node_data.same_match ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[use_phase ^ 1] > 0 ) + { + old_required = node_data.required[use_phase]; + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[use_phase ^ 1] - lib_inv_delay ); + } + } + else if ( !node_data.map_refs[0] || !node_data.map_refs[1] ) + { + use_phase = node_data.map_refs[0] ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, *it, use_phase ); + node_data.same_match = true; + } + + /* match positive phase */ + match_phase_exact( *it, 0u ); + + /* match negative phase */ + match_phase_exact( *it, 1u ); + + /* restore required time */ + if ( old_required > 0 ) + { + node_data.required[use_phase] = old_required; + } + + /* try to drop one phase */ + match_drop_phase( *it ); + + /* try a multi-output match */ /* TODO: fix the required time*/ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index ) + { + bool mapped = match_multioutput_exact( *it, true ); + + /* propagate required time for the selected gates */ + if ( mapped ) + { + match_multioutput_propagate_required( *it ); + } + else + { + match_propagate_required( index ); + } + } + else + { + match_propagate_required( index ); + } + } + + double area_old = area; + + propagate_arrival_times(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + if constexpr ( SwitchActivity ) + stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + else + stats << fmt::format( "[i] Area Rev : Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + + return true; + } + + inline void match_propagate_required( uint32_t index ) + { + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_backward_white_box( n ); + } + return; + } + } + + auto& node_data = node_match[index]; + + /* propagate required time through the leaves */ + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_gate[0] != nullptr || node_data.best_gate[1] != nullptr ); + // assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto const& supergate = node_data.best_gate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto const& best_cut = cuts[index][node_data.best_cut[other_phase]]; + auto const& supergate = node_data.best_gate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + } + + template + bool set_mapping_refs() + { + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + bool set_mapping_refs_and_req() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + set_output_required_time( iteration == 0 ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + inv = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + { + std::cerr << "[e] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_pi( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( *it ) ) + { + set_mapping_refs_dont_touch( *it ); + continue; + } + } + + /* refine best matches with alternatives */ + if constexpr ( !DO_AREA ) + { + if ( ps.use_match_alternatives ) + refine_best_matches( *it ); + } + + unsigned use_phase = node_data.best_gate[0] == nullptr ? 1u : 0u; + if ( node_data.best_gate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[e] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_data.map_refs[use_phase]; + } + area += lib_inv_area; + ++inv; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + + if ( !ps.area_oriented_mapping ) + { + match_propagate_required( index ); + } + } + + ++iteration; + + if constexpr ( ELA ) + { + return true; + } + + /* blend estimated references */ + float const coef = 1.0f / ( ( iteration + 1.0f ) * ( iteration + 1.0f ) ); + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[0] = std::max( 1.0f, coef * node_match[i].est_refs[0] + ( 1 - coef ) * node_match[i].map_refs[0] ); + node_match[i].est_refs[1] = std::max( 1.0f, coef * node_match[i].est_refs[1] + ( 1 - coef ) * node_match[i].map_refs[1] ); + } + + return true; + } + + template + inline void set_mapping_refs_dont_touch( node const& n ) + { + if constexpr ( !ELA ) + { + /* reference node */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + uint32_t leaf = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + node_match[leaf].map_refs[phase]++; + } ); + } + + const auto index = ntk.node_to_index( n ); + + if constexpr ( has_has_binding_v ) + { + /* increase area */ + area += node_match[index].area[0]; + if ( node_match[index].map_refs[1] ) + { + if ( iteration < ps.area_flow_rounds ) + { + ++node_match[index].map_refs[0]; + } + area += lib_inv_area; + ++inv; + } + } + } + + void set_output_required_time( bool warning ) + { + double required = delay; + /* relax delay constraints */ + if ( iteration == 0 && ps.required_time == 0.0f && ps.required_times.empty() && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + /* Global target time constraint */ + if ( ps.required_times.empty() ) + { + if ( ps.required_time != 0.0f ) + { + if ( ps.required_time < delay - epsilon ) + { + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + return; + } + + /* Output-specific target time constraint */ + ntk.foreach_po( [&]( auto const& s, uint32_t i ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + uint8_t phase = ntk.is_complemented( s ) ? 1 : 0; + if ( node_match[index].arrival[phase] > ps.required_times[i] + epsilon ) + { + /* maintain the same delay */ + node_match[index].required[phase] = node_match[index].arrival[phase]; + if ( warning ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f} at output {}", ps.required_times[i], i ) << std::endl; + } + else + { + node_match[index].required[phase] = ps.required_times[i]; + } + } ); + } + + void compute_required_time( bool exit_early = false ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return if mapping is area oriented */ + if ( ps.area_oriented_mapping ) + return; + + set_output_required_time( iteration == 1 ); + + if ( exit_early ) + return; + + /* propagate required time to the PIs */ + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_pi( *it ) || ntk.is_constant( *it ) ) + break; + + const auto index = ntk.node_to_index( *it ); + + if ( !node_match[index].map_refs[0] && !node_match[index].map_refs[1] ) + continue; + + match_propagate_required( index ); + } + } + + void propagate_arrival_times() + { + area = 0.0f; + inv = 0; + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* measure area */ + if ( ntk.is_constant( n ) ) + { + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + ++inv; + } + continue; + } + + /* reset required time */ + node_data.required[0] = std::numeric_limits::max(); + node_data.required[1] = std::numeric_limits::max(); + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + node n = ntk.index_to_node( index ); + if ( ntk.is_dont_touch( n ) ) + { + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + if ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) + area += node_data.area[0]; + if ( node_data.map_refs[1] ) + { + area += lib_inv_area; + ++inv; + } + } + continue; + } + } + + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + /* compute area */ + if ( node_data.map_refs[use_phase] > 0 || ( node_data.same_match && ( node_match[index].map_refs[0] || node_match[index].map_refs[1] ) ) ) + { + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + ++inv; + } + } + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + continue; + } + + assert( node_data.best_gate[use_phase] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + + if ( node_data.map_refs[use_phase] > 0 ) + { + area += node_data.area[use_phase]; + } + } + + /* compute the current worst delay */ + delay = 0.0f; + ntk.foreach_po( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + } ); + + /* return if mapping is area oriented */ + ++iteration; + if ( ps.area_oriented_mapping ) + return; + + /* set the required time at POs */ + ntk.foreach_po( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = delay; + else + node_match[index].required[0] = delay; + } ); + } + + void propagate_arrival_node( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + + /* compute arrival of use_phase */ + supergate const* best_gate = node_data.best_gate[use_phase]; + double worst_arrival = 0; + uint16_t best_phase = node_data.phase[use_phase]; + auto ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + node_data.arrival[use_phase] = worst_arrival; + + /* compute arrival of the other phase */ + use_phase ^= 1; + if ( node_data.same_match ) + { + node_data.arrival[use_phase] = worst_arrival + lib_inv_delay; + return; + } + + assert( node_data.best_gate[0] != nullptr ); + + best_gate = node_data.best_gate[use_phase]; + worst_arrival = 0; + best_phase = node_data.phase[use_phase]; + ctr = 0u; + for ( auto l : cuts[index][node_data.best_cut[use_phase]] ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_gate->tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + node_data.arrival[use_phase] = worst_arrival; + } + + template + void match_phase( node const& n, uint8_t phase ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + uint32_t cut_index = 0u; + + node_data.best_gate[phase] = nullptr; + node_data.arrival[phase] = std::numeric_limits::max(); + node_data.flows[phase] = std::numeric_limits::max(); + node_data.area[phase] = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + + best_gate_emap& gA = node_data.best_alternative[phase]; + gA.gate = nullptr; + gA.arrival = std::numeric_limits::max(); + gA.flow = std::numeric_limits::max(); + uint32_t best_sizeA = UINT32_MAX; + + /* unmap multioutput */ + node_data.multioutput_match[phase] = false; + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + double worst_arrivalA = 0.0f; + float area_local = gate.area; + float area_localA = gate.area; + + auto ctr = 0u; + for ( auto l : *cut ) + { + uint8_t leaf_phase = ( gate_polarity >> ctr ) & 1; + + double arrival_pinA = node_match[l].best_alternative[leaf_phase].arrival + gate.tdelay[ctr]; + worst_arrivalA = std::max( worst_arrivalA, arrival_pinA ); + + // if constexpr ( DO_AREA ) + // { + // if ( worst_arrivalA > node_data.required[phase] + epsilon || worst_arrivalA >= std::numeric_limits::max() ) + // break; + // } + + double arrival_pin = node_match[l].arrival[leaf_phase] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + + area_local += node_match[l].flows[leaf_phase]; + area_localA += node_match[l].best_alternative[leaf_phase].flow; + ++ctr; + } + + bool skip = false; + if constexpr ( DO_AREA ) + { + if ( ctr < cut->size() ) + continue; + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + skip = true; + } + + if ( !skip && compare_map( worst_arrival, node_data.arrival[phase], area_local, node_data.flows[phase], cut->size(), best_size ) ) + { + node_data.best_gate[phase] = &gate; + node_data.arrival[phase] = worst_arrival; + node_data.flows[phase] = area_local; + node_data.best_cut[phase] = cut_index; + node_data.area[phase] = gate.area; + node_data.phase[phase] = gate_polarity; + best_size = cut->size(); + } + + /* compute the alternative */ + if ( compare_map( worst_arrivalA, gA.arrival, area_localA, gA.flow, cut->size(), best_sizeA ) ) + { + gA.gate = &gate; + gA.arrival = worst_arrivalA; + gA.area = gate.area; + gA.flow = area_localA; + gA.phase = gate_polarity; + gA.cut = cut_index; + best_sizeA = cut->size(); + gA.size = cut->size(); + } + } + + ++cut_index; + } + } + + template + void match_phase_exact( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint16_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + supergate const* best_gate = node_data.best_gate[phase]; + + /* unmap multioutput */ + if ( node_data.multioutput_match[phase] ) + { + /* dereference multi-output */ + if ( !node_data.same_match && best_gate != nullptr && node_data.map_refs[phase] ) + { + auto const& cut = multi_cut_set[node_data.best_cut[phase]][0]; + cut_deref( cut, n, phase ); + } + best_gate = nullptr; + node_data.multioutput_match[phase] = false; + } + + /* recompute best match info */ + if ( best_gate != nullptr ) + { + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + auto const& cut = cuts[index][node_data.best_cut[phase]]; + cut_deref( cut, n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts[index] ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = ( *cut )->supergates; + auto const negation = ( *cut )->negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint16_t gate_polarity = gate.polarity ^ negation; + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if ( worst_arrival > node_data.required[phase] + epsilon || worst_arrival >= std::numeric_limits::max() ) + continue; + + node_data.phase[phase] = gate_polarity; + node_data.area[phase] = gate.area; + float area_exact = cut_measure_mffc( *cut, n, phase ); + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = gate_polarity; + best_gate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_gate[phase] = best_gate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts[index][best_cut], n, phase ); + } + } + + template + void match_drop_phase( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_gate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_gate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival improves matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon ); + use_one = worst_arrival_npos < ( node_data.required[0] + epsilon ); + } + + /* condition on not used phases, evaluate a substitution during exact area recovery */ + if constexpr ( ELA ) + { + if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 ) + { + /* select the used match */ + auto phase = 0; + auto nphase = 0; + if ( node_data.map_refs[0] == 0 ) + { + phase = 1; + use_one = true; + use_zero = false; + } + else + { + nphase = 1; + use_one = false; + use_zero = true; + } + /* select the not used match instead if it leads to area improvement and doesn't violate the required time */ + if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon ) + { + auto size_phase = cuts[index][node_data.best_cut[phase]].size(); + auto size_nphase = cuts[index][node_data.best_cut[nphase]].size(); + + if ( compare_map( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) ) + { + /* invert the choice */ + use_zero = !use_zero; + use_one = !use_one; + } + } + } + } + + if ( ( !use_zero && !use_one ) ) + { + /* use both phases */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + + /* use area flow as a tiebreaker */ + if ( use_zero && use_one ) + { + auto size_zero = cuts[index][node_data.best_cut[0]].size(); + auto size_one = cuts[index][node_data.best_cut[1]].size(); + + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + /* both phases were implemented --> evaluate substitution */ + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + node_data.flows[1] = cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + node_data.flows[0] = cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + /* evaluate based on inverter cost */ + if constexpr ( !SwitchActivity ) + { + use_zero = lib_inv_area < node_data.flows[1] + epsilon; + use_one = lib_inv_area < node_data.flows[0] + epsilon; + } + + if ( use_one && use_zero ) + { + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + else if ( !use_one && !use_zero && node_data.same_match ) + { + node_data.same_match = false; + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + return; + } + } + else + { + /* compare flows by looking at the most convinient and referenced */ + if ( node_data.flows[0] / node_data.est_refs[0] + lib_inv_area < node_data.flows[1] / node_data.est_refs[1] + epsilon ) + { + use_one = false; + } + else if ( node_data.flows[1] / node_data.est_refs[1] + lib_inv_area < node_data.flows[0] / node_data.est_refs[0] + epsilon ) + { + use_zero = false; + } + else + { + /* delay the decision on what to keep --> wait for better estimations */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.same_match = false; + return; + } + } + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the negative phase cut if in use */ + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts[index][node_data.best_cut[1]], n, 1 ); + /* reference the positive cut if not in use before */ + if ( node_data.map_refs[0] == 0 && node_data.map_refs[1] > 0 ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the positive phase cut if in use */ + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts[index][node_data.best_cut[0]], n, 0 ); + /* reference the negative cut if not in use before */ + if ( node_data.map_refs[1] == 0 && node_data.map_refs[0] > 0 ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[0] || node_data.map_refs[1] ) + cut_ref( cuts[index][node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_gate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase]; + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase_n] = ( node_data.flows[phase] + lib_inv_area ) / node_data.est_refs[phase_n]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[phase]; + } + + template + inline void select_alternatives( node const& n ) + { + if constexpr ( DO_AREA ) + return; + + if ( !ps.use_match_alternatives ) + return; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + float g0flow = g0.flow / node_data.est_refs[0]; + float g1flow = g1.flow / node_data.est_refs[1]; + + /* process for best area */ /* removed check on required since this is executed only during a delay pass */ + if ( g0.gate != nullptr && g0flow + lib_inv_area < g1flow + epsilon ) + { + g1 = g0; + g1.gate = nullptr; + g1.arrival += lib_inv_delay; + g1.flow = ( g1.flow + lib_inv_area ) / node_data.est_refs[1]; + g0.flow = g0flow; + return; + } + else if ( g1.gate != nullptr && g1flow + lib_inv_area < g0flow + epsilon ) + { + g0 = g1; + g0.gate = nullptr; + g0.arrival += lib_inv_delay; + g0.flow = ( g0.flow + lib_inv_area ) / node_data.est_refs[0]; + g1.flow = g1flow; + return; + } + + g0.flow = g0flow; + g1.flow = g1flow; + } + + inline void refine_best_matches( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* evaluate to change the best matches with the best alternative */ + best_gate_emap& g0 = node_data.best_alternative[0]; + best_gate_emap& g1 = node_data.best_alternative[1]; + + if ( node_data.map_refs[0] && node_data.map_refs[1] ) + { + if ( node_data.same_match ) + { + /* pick best implementation between the two alternatives */ + unsigned best_match_phase = node_data.best_gate[0] == nullptr ? 1 : 0; + unsigned use_phase = g0.gate == nullptr ? 1 : 0; + if ( g0.gate != nullptr && g1.gate != nullptr ) + { + if ( g0.arrival > node_data.required[0] + epsilon || g1.arrival > node_data.required[1] + epsilon ) + return; + + refine_best_matches_copy_refinement( n, 0, false ); + refine_best_matches_copy_refinement( n, 1, false ); + node_data.same_match = false; + return; + } + else + { + best_gate_emap& gUse = node_data.best_alternative[use_phase]; + if ( gUse.arrival > node_data.required[use_phase] + epsilon || gUse.arrival + lib_inv_delay > node_data.required[use_phase ^ 1] + epsilon ) + { + return; + } + refine_best_matches_copy_refinement( n, use_phase, true ); + return; + } + } + else + { + /* not same match: evaluate both zero and one phase */ + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ); + } + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ); + } + } + } + else if ( node_data.map_refs[0] ) + { + if ( g0.gate != nullptr && g0.arrival < node_data.required[0] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 0, false ); + } + else if ( g0.gate == nullptr && g1.arrival + lib_inv_delay < node_data.required[0] + epsilon ) + { + refine_best_matches_copy_refinement( n, 1, true ); + } + } + else + { + if ( g1.gate != nullptr && g1.arrival < node_data.required[1] + epsilon ) + { + node_data.same_match = false; + refine_best_matches_copy_refinement( n, 1, false ); + } + else if ( g1.gate == nullptr && g0.arrival + lib_inv_delay < node_data.required[1] + epsilon ) + { + refine_best_matches_copy_refinement( n, 0, true ); + } + } + } + + inline void refine_best_matches_copy_refinement( node const& n, unsigned phase, bool both_phases ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + best_gate_emap& bg = node_data.best_alternative[phase]; + + node_data.best_gate[phase] = bg.gate; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival; + node_data.area[phase] = bg.area; + node_data.flows[phase] = bg.flow; + + if ( !both_phases ) + return; + + node_data.same_match = true; + phase ^= 1; + node_data.best_gate[phase] = nullptr; + node_data.phase[phase] = bg.phase; + node_data.best_cut[phase] = bg.cut; + node_data.arrival[phase] = bg.arrival + lib_inv_delay; + node_data.area[phase] = bg.area; + node_data.flows[phase] = ( bg.flow * node_data.est_refs[phase ^ 1] + lib_inv_area ) / node_data.est_refs[phase]; + } + + bool initialize_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( cuts[index].size() == 0 ) + add_unit_cut( index ); + + auto& node_data = node_match[index]; + node_data.same_match = true; + + /* if it has mapping data propagate the delays and measure the data */ + if constexpr ( has_has_binding_v ) + { + propagate_data_forward_white_box( n ); + return false; + } + + /* consider as a black box */ + node_data.flows[0] = 0.0f; + node_data.flows[1] = lib_inv_area / node_data.est_ref[1]; + node_data.arrival[0] = 0.0f; + node_data.arrival[1] = lib_inv_delay; + node_data.area[0] = node_data.area[1] = 0; + + return true; + } + + void propagate_data_forward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + /* propagate arrival time */ + double arrival = 0; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + arrival = std::max( arrival, node_match[f_index].arrival[phase] + propagation_delay ); + } ); + + /* set data */ + node_data.arrival[0] = arrival; + node_data.arrival[1] = arrival + lib_inv_delay; + node_data.area[0] = node_data.area[1] = gate.area; + node_data.flows[1] = ( node_data.flows[0] + lib_inv_area ) / node_data.est_refs[1]; + node_data.flows[0] = node_data.area[0] / node_data.est_refs[0]; + } + + void propagate_data_backward_white_box( node const& n ) + { + uint32_t index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto const& gate = ntk.get_binding( n ); + + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.map_refs[1] > 0 ) + { + node_data.required[0] = std::min( node_data.required[0], node_data.required[1] - lib_inv_delay ); + } + + if ( node_data.map_refs[0] ) + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + if ( node_data.map_refs[1] ) + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + uint32_t f_index = ntk.node_to_index( ntk.get_node( f ) ); + uint8_t phase = ntk.is_complemented( f ) ? 1 : 0; + double propagation_delay = std::max( gate.pins[i].rise_block_delay, gate.pins[i].fall_block_delay ); + node_match[f_index].required[phase] = std::min( node_match[f_index].required[phase], node_data.required[0] - propagation_delay ); + } ); + } + + void match_constants( uint32_t index ) + { + auto& node_data = node_match[index]; + + kitty::static_truth_table<6> zero_tt; + auto const supergates_zero = library.get_supergates( zero_tt ); + auto const supergates_one = library.get_supergates( ~zero_tt ); + + /* Not available in the library */ + if ( supergates_zero == nullptr && supergates_one == nullptr ) + { + return; + } + /* if only one is available, the other is obtained using an inverter */ + if ( supergates_zero != nullptr ) + { + node_data.best_gate[0] = &( ( *supergates_zero )[0] ); + node_data.arrival[0] = node_data.best_gate[0]->tdelay[0]; + node_data.area[0] = node_data.best_gate[0]->area; + node_data.phase[0] = 0; + } + if ( supergates_one != nullptr ) + { + node_data.best_gate[1] = &( ( *supergates_one )[0] ); + node_data.arrival[1] = node_data.best_gate[1]->tdelay[0]; + node_data.area[1] = node_data.best_gate[1]->area; + node_data.phase[1] = 0; + } + else + { + node_data.same_match = true; + node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay; + node_data.area[1] = node_data.area[0] + lib_inv_area; + node_data.phase[1] = 1; + } + if ( supergates_zero == nullptr ) + { + node_data.same_match = true; + node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay; + node_data.area[0] = node_data.area[1] + lib_inv_area; + node_data.phase[0] = 1; + } + } + + template + bool match_multioutput( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* get the cut */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_flow; + std::array area; + std::array phase; + std::array pin_phase; + std::array est_refs; + std::array cut_index; + bool mapped_multioutput = false; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool is_best = true; + bool respects_required = true; + double old_flow_sum = 0; + + /* iterate for each output of the multi-output gate */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_data.same_match ) + return false; + + /* get the output phase */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time: same_match is true */ + if constexpr ( DO_AREA ) + { + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + } + + /* check required time of the current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.same_match && node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute area flow */ + if ( j == 0 || !node_data.multioutput_match[0] ) + { + uint8_t current_phase = node_data.best_gate[0] == nullptr ? 1 : 0; + old_flow_sum += node_data.flows[current_phase]; + } + uint8_t old_phase = node_data.phase[phase[j]]; + node_data.phase[phase[j]] = gate.polarity; + area[j] = gate.area; + area_flow[j] = gate.area + cut_leaves_flow( cut, n, phase[j] ); + node_data.phase[phase[j]] = old_phase; + + /* current version may lead to delay increase */ + est_refs[j] = node_data.est_refs[phase[j]]; + } + + /* not better than individual gates */ + if ( !valid ) + continue; + + if constexpr ( !DO_AREA ) + { + if ( !is_best ) + continue; + } + + /* combine evaluation for precise area flow estimantion */ + /* compute equation AF(n) = ( Area(G) + |roots| * SUM_{l in leaves} AF(l) ) / SUM_{p in roots} est_refs( p ) */ + float flow_sum_pos = 0, flow_sum_neg; + float combined_est_refs = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + flow_sum_pos += area_flow[j]; + combined_est_refs += est_refs[j]; + } + flow_sum_neg = flow_sum_pos; + flow_sum_pos /= combined_est_refs; + + /* not better than individual gates */ + if ( respects_required && ( flow_sum_pos > old_flow_sum + epsilon ) ) + continue; + + mapped_multioutput = true; + flow_sum_neg = ( flow_sum_neg + lib_inv_area ) / combined_est_refs; + + /* commit multi-output gate */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + node_data.multioutput_match[mapped_phase] = true; + + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_pos; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = flow_sum_neg; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + bool match_multioutput_exact( node const& n, bool last_round ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + /* local values storage */ + std::array best_exact_area; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + /* protection on complicated duplicated nodes to remap to multioutput */ + if ( !node_match[tuple_data[j].node_index].same_match ) + return false; + } + + /* if one of the outputs is not referenced, do not use multi-output gate */ + if ( last_round ) + { + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + if ( !node_match[node_index].map_refs[0] && !node_match[node_index].map_refs[1] ) + { + return false; + } + } + } + + /* if "same match" and used in the cover dereference the leaves (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + uint8_t selected_phase = node_match[node_index].best_gate[0] == nullptr ? 1 : 0; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + /* match is always single output here */ + auto const& cut = cuts[node_index][node_match[node_index].best_cut[0]]; + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + best_exact_area[j] = cut_deref( cut, ntk.index_to_node( node_index ), use_phase ); + + /* mapping a non referenced phase */ + if ( node_match[node_index].map_refs[selected_phase] == 0 ) + best_exact_area[j] += lib_inv_area; + } + } + + /* perform mapping */ + bool mapped_multioutput = false; + mapped_multioutput = match_multioutput_exact_core( tuple_data, best_exact_area ); + + /* if "same match" and used in the cover reference the leaves (topo order) */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + + if ( node_match[node_index].map_refs[0] || node_match[node_index].map_refs[1] ) + { + uint8_t use_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[node_index][node_match[node_index].best_cut[use_phase]]; + cut_ref( best_cut, ntk.index_to_node( node_index ), use_phase ); + } + } + + return mapped_multioutput; + } + + template + inline bool match_multioutput_exact_core( multi_match_t const& tuple_data, std::array& best_exact_area ) + { + /* get the cut representative */ + auto const& cut0 = cuts[tuple_data[0].node_index][tuple_data[0].cut_index]; + + /* local values storage */ + std::array arrival; + std::array area_exact; + std::array area; + std::array phase; + std::array pin_phase; + std::array cut_index; + + uint8_t iteration_phase = cut0->supergates[0] == nullptr ? 1 : 0; + + bool mapped_multioutput = false; + + /* iterate for each possible match */ + for ( auto i = 0; i < cut0->supergates[iteration_phase]->size(); ++i ) + { + /* store local validity and comparison info */ + bool valid = true; + bool is_best = true; + bool respects_required = true; + uint32_t it_counter = 0; + + /* iterate for each output of the multi-output gate (reverse topo order) */ + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + uint32_t node_index = tuple_data[j].node_index; + cut_index[j] = tuple_data[j].cut_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + ++it_counter; + + /* get the output phase and area */ + pin_phase[j] = gate.polarity; + phase[j] = ( gate.polarity >> NInputs ) ^ phase_inverted; + area[j] = gate.area; + + /* compute arrival */ + arrival[j] = 0.0; + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( gate.polarity >> ctr ) & 1] + gate.tdelay[ctr]; + arrival[j] = std::max( arrival[j], arrival_pin ); + ++ctr; + } + + /* check required time */ + if ( arrival[j] > node_data.required[phase[j]] + epsilon ) + { + valid = false; + break; + } + if ( arrival[j] + lib_inv_delay > node_data.required[phase[j] ^ 1] + epsilon ) + { + valid = false; + break; + } + + /* check required time of current solution */ + if ( node_data.arrival[phase[j]] > node_data.required[phase[j]] ) + respects_required = false; + if ( node_data.arrival[phase[j] ^ 1] > node_data.required[phase[j] ^ 1] ) + respects_required = false; + + /* compute exact area for match: needed only for the first node (leaves are shared) */ + if ( it_counter == 1 ) + { + auto old_phase = node_data.phase[phase[j]]; + auto old_area = node_data.area[phase[j]]; + node_data.phase[phase[j]] = pin_phase[j]; + node_data.area[phase[j]] = area[j]; + area_exact[j] = cut_measure_mffc( cut, ntk.index_to_node( node_index ), phase[j] ); + node_data.phase[phase[j]] = old_phase; + node_data.area[phase[j]] = old_area; + } + else + { + area_exact[j] = area[j]; + } + + /* Add output inverter cost if mapping a non referenced phase */ + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + area_exact[j] += lib_inv_area; + } + } + + /* check quality: TODO add output inverter in the cost if necessary */ + float best_exact_area_total = 0; + float area_exact_total = 0; + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + best_exact_area_total += best_exact_area[j]; + area_exact_total += area_exact[j]; + } + + /* not better than individual gates */ + if ( !valid || ( area_exact_total > best_exact_area_total - epsilon && respects_required ) ) + { + continue; + } + + mapped_multioutput = true; + + /* commit multi-output gate (topo order) */ + for ( uint32_t j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + auto& node_data = node_match[node_index]; + auto const& cut = cuts[node_index][cut_index[j]]; + uint8_t phase_inverted = cut->supergates[0] == nullptr ? 1 : 0; + supergate const& gate = ( *( cut->supergates[phase_inverted] ) )[i]; + + uint8_t mapped_phase = phase[j]; + best_exact_area[j] = area_exact[j]; + + if ( node_data.map_refs[phase[j]] == 0 && node_data.map_refs[phase[j] ^ 1] > 0 ) + { + best_exact_area[j] += lib_inv_area; + } + + /* write data */ + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = &gate; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j]; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + + node_data.flows[mapped_phase] = area_exact[j]; /* partial exact area contribution */ + /* select opposite phase */ + mapped_phase ^= 1; + node_data.multioutput_match[mapped_phase] = true; + node_data.best_gate[mapped_phase] = nullptr; + node_data.best_cut[mapped_phase] = cut_index[j]; + node_data.phase[mapped_phase] = pin_phase[j]; + node_data.arrival[mapped_phase] = arrival[j] + lib_inv_delay; + node_data.area[mapped_phase] = area[j]; /* partial area contribution */ + node_data.flows[mapped_phase] = area_exact[j]; + + assert( node_data.arrival[mapped_phase] < node_data.required[mapped_phase] + epsilon ); + } + } + + return mapped_multioutput; + } + + template + void multi_node_update( node const& n ) + { + uint32_t check_index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + /* avoid cycles by recomputing arrival times for multi-output gates or decomposing them */ + if ( node_data.same_match && node_data.multioutput_match[0] ) + { + propagate_arrival_node( n ); + /* check required time */ + if ( node_data.arrival[0] < node_data.required[0] + epsilon && node_data.arrival[1] < node_data.required[1] + epsilon ) + return; + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < node_data.required[0] + epsilon ); + assert( node_data.arrival[1] < node_data.required[1] + epsilon ); + } + + template + void multi_node_update_exact( node const& n ) + { + uint32_t check_index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[ntk.node_to_index( n )].index][0]; + uint64_t signature = 0; + + /* check if a node is in TFI: there is a path of length > 1 */ + bool in_tfi = false; + node min_node = n; + for ( auto j = 0; j < max_multioutput_output_size - 1; ++j ) + { + if ( tuple_data[j].in_tfi ) + { + min_node = ntk.index_to_node( tuple_data[j].node_index ); + in_tfi = true; + signature |= UINT64_C( 1 ) << ( tuple_data[j].node_index & 0x3f ); + } + } + + if ( !in_tfi ) + return; + + /* recompute data in between: should I mark the leaves? (not necessary under some assumptions) */ + ntk.incr_trav_id(); + ntk.foreach_fanin( n, [&]( auto const& f ) { + /* TODO: this recursion works as it is for a maximum multioutput value of 2 */ + multi_node_update_exact_rec( ntk.get_node( f ), min_node + 1, signature ); + } ); + } + + template + void multi_node_update_exact_rec( node const& n, uint32_t min_index, uint64_t& signature ) + { + uint32_t index = ntk.node_to_index( n ); + + if ( index < min_index ) + return; + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_node_update_exact_rec( ntk.get_node( f ), min_index, signature ); + } ); + + /* update the node if uses an updated leaf */ + auto& node_data = node_match[index]; + bool leaf_used = multi_node_update_cut_check( index, signature, 0 ); + + if ( !node_data.same_match ) + leaf_used |= multi_node_update_cut_check( index, signature, 1 ); + + if ( !leaf_used ) + return; + + signature |= UINT64_C( 1 ) << ( index & 0x3f ); + + assert( !node_data.multioutput_match[0] ); + assert( !node_data.multioutput_match[1] ); + + if ( node_data.same_match && ( node_data.map_refs[0] || node_data.map_refs[1] ) ) + { + uint8_t use_phase = node_data.best_gate[0] != nullptr ? 0 : 1; + auto const& best_cut = cuts[index][node_data.best_cut[use_phase]]; + cut_deref( best_cut, n, use_phase ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n ); + + assert( node_data.arrival[0] < std::numeric_limits::max() ); + assert( node_data.arrival[1] < std::numeric_limits::max() ); + } + + inline void match_multioutput_propagate_required( node const& n ) + { + /* extract outputs tuple */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + + for ( int j = max_multioutput_output_size - 1; j >= 0; --j ) + { + const auto node_index = tuple_data[j].node_index; + match_propagate_required( node_index ); + } + } + + void match_multi_add_cuts( node const& n ) + { + /* assume a single cut (current version) */ + uint32_t index = ntk.node_to_index( n ); + multi_match_t& matches = multi_node_match[node_tuple_match[index].index][0]; + + /* find the corresponding cut */ + uint32_t cut_p = 0; + while ( matches[cut_p].node_index != index ) + ++cut_p; + + assert( cut_p < matches.size() ); + uint32_t cut_index = matches[cut_p].cut_index; + auto& cut = multi_cut_set[cut_index][cut_p]; + auto single_cut = multi_cut_set[cut_index][cut_p]; + auto& rcuts = cuts[index]; + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + match_multi_add_cuts_remove_entry( matches ); + return; + } + + /* insert single cut variation if unique (for delay preservation) */ + if ( !rcuts.is_contained( single_cut ) ) + { + single_cut->pattern_index = 0; + compute_cut_data( single_cut, ntk.index_to_node( index ) ); + rcuts.append_cut( single_cut ); + + /* not enough space in the data structure: abort */ + if ( rcuts.size() == max_cut_num ) + { + rcuts.limit( rcuts.size() - 1 ); + match_multi_add_cuts_remove_entry( matches ); + return; + } + } + + /* add multi-output cut */ + uint32_t num_cuts_pre = rcuts.size(); + cut->ignore = true; + rcuts.append_cut( cut ); + + uint32_t num_cuts_after = rcuts.size(); + assert( num_cuts_after == num_cuts_pre + 1 ); + + rcuts.limit( num_cuts_pre ); + + /* update tuple data */ + matches[cut_p].cut_index = num_cuts_pre; + } + + inline void match_multi_add_cuts_remove_entry( multi_match_t const& matches ) + { + /* reset matches */ + for ( multi_match_data const& entry : matches ) + { + node_tuple_match[entry.node_index].data = 0; + } + } + + inline bool multi_node_update_cut_check( uint32_t index, uint64_t signature, uint8_t phase ) + { + auto const& cut = cuts[index][node_match[index].best_cut[phase]]; + + if ( ( signature & cut.signature() ) > 0 ) + return true; + + return false; + } +#pragma endregion + +#pragma region Mapping utils + inline double cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + double flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + template + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + --node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive dereferencing */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_deref( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_measure_mffc( cut_t const& cut, node const& n, uint8_t phase ) + { + tmp_visited.clear(); + + float count = cut_ref_visit( cut, n, phase ); + + /* dereference visited */ + for ( auto s : tmp_visited ) + { + uint32_t leaf = s >> 1; + --node_match[leaf].map_refs[s & 1]; + } + + return count; + } + + template + float cut_ref_visit( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + return count; + } + } + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* add to visited */ + tmp_visited.push_back( ( static_cast( leaf ) << 1 ) | leaf_phase ); + + if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Recursive referencing if leaf was not referenced */ + if ( !node_match[leaf].map_refs[0] && !node_match[leaf].map_refs[1] ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_gate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + auto const& best_cut = cuts[leaf][node_match[leaf].best_cut[leaf_phase]]; + count += cut_ref_visit( best_cut, ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } +#pragma endregion + +#pragma region Initialize and dump the mapped network + void insert_buffers() + { + if ( lib_buf_id != UINT32_MAX ) + { + double area_old = area; + bool buffers = false; + + ntk.foreach_po( [&]( auto const& f ) { + auto const& n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ntk.is_pi( n ) && !ntk.is_complemented( f ) ) + { + area += lib_buf_area; + delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay ); + buffers = true; + } + } ); + + /* round stats */ + if ( ps.verbose && buffers ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + area_gain = float( ( area_old - area ) / area_old * 100 ); + + stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} Gain = {:>5.2f} % Inverters = {:>5} Time = {:>5.2f}\n", delay, area, area_gain, inv, to_seconds( clock::now() - time_begin ) ); + st.round_stats.push_back( stats.str() ); + } + } + } + + std::pair, klut_map> initialize_map_network() + { + binding_view dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + std::pair, block_map> initialize_block_network() + { + cell_view dest( library.get_cells() ); + block_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + void init_topo_order() + { + topo_order.reserve( ntk.size() ); + + if ( multi_node_match.size() > 0 ) + { + multi_init_topo_order(); + return; + } + + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + } + + bool init_arrivals() + { + if ( ps.required_times.size() && ps.required_times.size() != ntk.num_pos() ) + { + std::cerr << "[e] MAP ERROR: required time vector does not match the output size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + if ( ps.arrival_times.empty() ) + { + ntk.foreach_pi( [&]( auto const& n ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = 0; + node_data.arrival[1] = node_data.best_alternative[1].arrival = lib_inv_delay; + } ); + return true; + } + + if ( ps.arrival_times.size() != ntk.num_pis() ) + { + std::cerr << "[e] MAP ERROR: arrival time vector does not match the input size of the network" << std::endl; + st.mapping_error = true; + return false; + } + + ntk.foreach_pi( [&]( auto const& n, uint32_t i ) { + auto& node_data = node_match[ntk.node_to_index( n )]; + node_data.arrival[0] = node_data.best_alternative[0].arrival = ps.arrival_times[i]; + node_data.arrival[1] = node_data.best_alternative[1].arrival = ps.arrival_times[i] + lib_inv_delay; + } ); + + return true; + } + + void finalize_cover( binding_view& res, klut_map& old2new ) + { + uint32_t multioutput_count = 0; + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box( res, old2new, index ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id ); + } + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* count multioutput gates */ + if ( ps.map_multioutput && node_tuple_match[index].lowest_index && node_data.multioutput_match[phase] ) + { + ++multioutput_count; + } + } + + st.multioutput_gates = multioutput_count; + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void finalize_cover_block( cell_view& res, block_map& old2new ) + { + uint32_t multioutput_count = 0; + + /* get standard cells */ + std::vector const& lib = res.get_library(); + + /* get translation ID from GENLIB to STD_CELL */ + std::vector genlib_to_cell( library.get_gates().size() ); + for ( standard_cell const& cell : lib ) + { + for ( gate const& g : cell.gates ) + { + genlib_to_cell[g.id] = cell.id; + } + } + + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell[lib_inv_id] ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + /* don't touch box */ + if constexpr ( has_is_dont_touch_v ) + { + if ( ntk.is_dont_touch( n ) ) + { + clone_box2( res, old2new, index, genlib_to_cell ); + continue; + } + } + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + /* create multioutput gates */ + if ( ps.map_multioutput && node_data.multioutput_match[phase] ) + { + assert( node_data.same_match == true ); + + if ( node_tuple_match[index].has_info && node_tuple_match[index].lowest_index ) + { + ++multioutput_count; + create_block_for_gate( res, old2new, index, phase, genlib_to_cell ); + } + continue; + } + + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_cell( res.get_node( old2new[index][phase ^ 1] ), genlib_to_cell[lib_inv_id] ); + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + assert( !ps.map_multioutput || !node_data.multioutput_match[phase] ); + create_lut_for_gate2( res, old2new, index, phase, genlib_to_cell ); + } + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_pi( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_cell( res.get_node( buf ), genlib_to_cell[lib_buf_id] ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + /* write final results */ + st.area = area; + st.delay = delay; + st.multioutput_gates = multioutput_count; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + void create_lut_for_gate( binding_view& res, klut_map& old2new, uint32_t index, unsigned phase ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_binding( res.get_node( f ), gate->root->id ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate_rec( res, *gate, children ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate_rec( binding_view& res, composed_gate const& gate, std::vector> const& children ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate_rec( res, *fanin, children ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_binding( res.get_node( f ), gate.root->id ); + return f; + } + + void create_lut_for_gate2( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + auto const& node_data = node_match[index]; + auto const& best_cut = cuts[index][node_data.best_cut[phase]]; + auto const& gate = node_data.best_gate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_gate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate->root->id ) ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate2_rec( res, *gate, children, genlib_to_cell ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + signal create_lut_for_gate2_rec( cell_view& res, composed_gate const& gate, std::vector> const& children, std::vector const& genlib_to_cell ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate2_rec( res, *fanin, children, genlib_to_cell ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( gate.root->id ) ); + return f; + } + + void create_block_for_gate( cell_view& res, block_map& old2new, uint32_t index, unsigned phase, std::vector const& genlib_to_cell ) + { + std::vector const& lib = res.get_library(); + composed_gate const* local_gate = node_match[index].best_gate[phase]->root; + standard_cell const& cell = lib[genlib_to_cell.at( local_gate->root->id )]; + + assert( !local_gate->is_super ); + auto const& best_cut = cuts[index][node_match[index].best_cut[phase]]; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( cell.gates.front().num_vars ); + + /* output negations have already been assigned by the mapper */ + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= local_gate->num_vars ) + break; + children[node_match[index].best_gate[phase]->permutation[ctr]] = old2new[l][( node_match[index].phase[phase] >> ctr ) & 1]; + ++ctr; + } + + multi_match_t const& tuple_data = multi_node_match[node_tuple_match[index].index][0]; + std::vector outputs; + std::vector functions; + + /* re-order outputs to match the ones of the cell */ + for ( gate const& g : cell.gates ) + { + /* find the correct node */ + for ( auto j = 0; j < max_multioutput_output_size; ++j ) + { + uint32_t node_index = tuple_data[j].node_index; + assert( node_match[node_index].same_match ); + uint8_t node_phase = node_match[node_index].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[node_index].multioutput_match[node_phase] ); + + gate const* node_gate = node_match[node_index].best_gate[node_phase]->root->root; + + /* wrong output */ + if ( node_gate->id != g.id ) + continue; + + outputs.push_back( node_index ); + functions.push_back( g.function ); + } + } + + assert( outputs.size() == cell.gates.size() ); + + /* create the block */ + auto f = res.create_node( children, functions ); + res.add_cell( res.get_node( f ), genlib_to_cell.at( local_gate->root->id ) ); + + for ( uint32_t s : outputs ) + { + /* add inverted version if used */ + uint8_t node_phase = node_match[s].best_gate[0] != nullptr ? 0 : 1; + assert( node_match[s].same_match ); + + /* add the node in the data structure */ + old2new[s][node_phase] = f; + + if ( node_match[s].map_refs[node_phase ^ 1] > 0 ) + { + old2new[s][node_phase ^ 1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[s][node_phase ^ 1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + f = res.next_output_pin( f ); + } + } + + void clone_box( binding_view& res, klut_map& old2new, uint32_t index ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* create the node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_binding( res.get_node( f ), ntk.get_binding_index( n ) ); + } + } + + void clone_box2( cell_view& res, klut_map& old2new, uint32_t index, std::vector const& genlib_to_cell ) + { + node n = ntk.index_to_node( index ); + std::vector> children; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[ntk.get_node( f )][ntk.is_complemented( f ) ? 1 : 0] ); + } ); + + /* check if multi-output */ + std::vector const& lib = res.get_library(); + if constexpr ( has_has_binding_v ) + { + bool is_multioutput = false; + if ( ntk.has_binding( n ) ) + { + uint32_t cell_id = genlib_to_cell.at( ntk.get_binding_index( n ) ); + if ( lib.at( cell_id ).gates.size() > 1 ) + is_multioutput = true; + } + + /* create the multioutput node (partially dangling) */ + if ( is_multioutput ) + { + standard_cell const& cell = lib.at( genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + std::vector functions; + for ( auto const& g : cell.gates ) + { + functions.push_back( g.function ); + } + + auto f = res.create_node( children, functions ); + + /* find and connect the correct pin */ + for ( auto const& g : cell.gates ) + { + if ( g.id == cell.id ) + break; + res.next_output_pin( f ); + } + + old2new[index][0] = f; + res.add_cell( res.get_node( f ), cell.id ); + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + return; + } + } + + /* create the single-output node */ + auto const& tt = ntk.node_function( n ); + auto f = res.create_node( children, tt ); + + /* add the node in the data structure */ + old2new[index][0] = f; + if ( node_match[index].map_refs[1] ) + { + old2new[index][1] = res.create_not( f ); + res.add_cell( res.get_node( old2new[index][1] ), genlib_to_cell.at( lib_inv_id ) ); + } + + if constexpr ( has_has_binding_v ) + { + if ( ntk.has_binding( n ) ) + res.add_cell( res.get_node( f ), genlib_to_cell.at( ntk.get_binding_index( n ) ) ); + } + } +#pragma endregion + +#pragma region Cuts and matching utils + void compute_cut_data( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + if ( cut.size() > NInputs || cut.size() > 6 ) + { + /* Ignore cuts too big to be mapped using the library */ + cut->ignore = true; + return; + } + + const auto tt = cut->function; + const kitty::static_truth_table<6> fe = kitty::extend_to<6>( tt ); + auto fe_canon = fe; + + uint16_t negations_pos = 0; + uint16_t negations_neg = 0; + + /* match positive polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_pos = std::get<1>( canon ); + } + + auto const supergates_pos = library.get_supergates( fe_canon ); + + /* match negative polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( ~fe, cut.size() ); + fe_canon = std::get<0>( canon ); + negations_neg = std::get<1>( canon ); + } + else + { + fe_canon = ~fe; + } + + auto const supergates_neg = library.get_supergates( fe_canon ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + cut->negations = { negations_pos, negations_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void compute_cut_data_structural( cut_t& cut, node const& n ) + { + cut->delay = std::numeric_limits::max(); + cut->flow = std::numeric_limits::max(); + cut->ignore = false; + + assert( cut.size() <= NInputs ); + + const auto supergates_pos = library.get_supergates_pattern( cut->pattern_index, false ); + const auto supergates_neg = library.get_supergates_pattern( cut->pattern_index, true ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut->supergates = { supergates_pos, supergates_neg }; + } + else + { + /* Ignore not matched cuts */ + cut->ignore = true; + return; + } + + /* compute cut cost based on LUT area */ + recompute_cut_data( cut, n ); + } + + void recompute_cut_data( cut_t& cut, node const& n ) + { + /* compute cut cost based on LUT area */ + uint32_t best_arrival = 0; + float best_area_flow = cut.size() > 1 ? cut.size() : 0; + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + best_arrival = std::max( best_arrival, best_leaf_cut->delay ); + best_area_flow += best_leaf_cut->flow; + } + + cut->delay = best_arrival + ( cut.size() > 1 ) ? 1 : 0; + cut->flow = best_area_flow / ntk.fanout_size( n ); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt ) + { + size_t j = 0; + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + lsupport[j++] = static_cast( std::distance( sup.begin(), itp ) ); + } + + /* swap variables in the truth table */ + for ( int i = j - 1; i >= 0; --i ) + { + assert( i <= lsupport[i] ); + kitty::swap_inplace( tt, i, lsupport[i] ); + } + } + + void add_zero_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 0; + cut->negations[0] = cut->negations[1] = 0; + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index + 1 ); + + kitty::create_nth_var( cut->function, 0 ); + cut->ignore = true; + cut->delay = 0; + cut->flow = 0; + cut->pattern_index = 1; + cut->negations[0] = cut->negations[1] = 0; + } + + inline void create_structural_cut( cut_t& new_cut, std::vector const& vcuts, uint32_t new_pattern, uint32_t pattern_id1, uint32_t pattern_id2 ) + { + new_cut.set_leaves( *vcuts[0] ); + new_cut.add_leaves( vcuts[1]->begin(), vcuts[1]->end() ); + new_cut->pattern_index = new_pattern; + + /* get the polarity of the leaves of the new cut */ + uint16_t neg_l = 0, neg_r = 0; + if ( ( *vcuts[0] )->pattern_index == 1 ) + { + neg_r = static_cast( pattern_id1 & 1 ); + } + else + { + neg_r = ( *vcuts[0] )->negations[0]; + } + if ( ( *vcuts[1] )->pattern_index == 1 ) + { + neg_l = static_cast( pattern_id2 & 1 ); + } + else + { + neg_l = ( *vcuts[1] )->negations[0]; + } + + new_cut->negations[0] = ( neg_l << vcuts[0]->size() ) | neg_r; + new_cut->negations[1] = new_cut->negations[0]; + } + + inline bool fast_support_minimization( TT const& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + } + + return true; + } + + void compute_truth_table( uint32_t index, fanin_cut_t const& vcuts, uint32_t fanin, cut_t& res ) + { + for ( uint32_t i = 0; i < fanin; ++i ) + { + cut_t const* cut = vcuts[i]; + ltruth[i] = ( *cut )->function; + compute_truth_table_support( *cut, res, ltruth[i] ); + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), ltruth.begin(), ltruth.begin() + fanin ); + + if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + } + + res->function = tt_res; + } +#pragma endregion + + template + inline bool compare_map( double arrival, double best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + return size < best_size; + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + return size < best_size; + } + } + + double compute_switching_power() + { + double power = 0.0f; + + for ( auto const& n : topo_order ) + { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_gate[0] == nullptr && node_data.best_gate[1] == nullptr ) + continue; + } + else if ( ntk.is_pi( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + continue; + } + + /* continue if cut is not in the cover */ + if ( !node_data.map_refs[0] && !node_data.map_refs[1] ) + continue; + + unsigned phase = ( node_data.best_gate[0] != nullptr ) ? 0 : 1; + + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + } + + phase = phase ^ 1; + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + } + } + + return power; + } + +#pragma region multioutput + /* Experimental code */ + void compute_multioutput_match() + { + stopwatch t( st.time_multioutput ); + + if ( library.num_multioutput_gates() == 0 ) + return; + + /* compute cuts: first simple method without proper matching */ + cut_enumeration_params multi_ps; + multi_ps.minimize_truth_table = false; + multi_cuts_t multi_cuts = fast_cut_enumeration( ntk, multi_ps ); + + /* cuts leaves classes */ + multi_hash_t multi_cuts_classes; + multi_cuts_classes.reserve( 2000 ); + + /* Multi-output matching */ + multi_enumerate_matches( multi_cuts, multi_cuts_classes ); + + multi_single_matches_t multi_node_match_local; + multi_node_match_local.reserve( multi_cuts_classes.size() ); + + multi_compute_matches( multi_cuts, multi_cuts_classes, multi_node_match_local ); + + if ( ps.remove_overlapping_multicuts ) + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + else + multi_filter_and_match( multi_cuts, multi_node_match_local ); /* it also adds the tuple for node mapping */ + } + + void multi_init_topo_order() + { + /* create and initialize a choice view to store the tuples */ + choice_view choice_ntk{ ntk }; + multi_add_choices( choice_ntk ); + + ntk.incr_trav_id(); + ntk.incr_trav_id(); + + /* add constants and CIs */ + const auto c0 = ntk.get_node( ntk.get_constant( false ) ); + topo_order.push_back( c0 ); + ntk.set_visited( c0, ntk.trav_id() ); + + if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() ) + { + topo_order.push_back( c1 ); + ntk.set_visited( c1, ntk.trav_id() ); + } + + ntk.foreach_ci( [&]( auto const& n ) { + if ( ntk.visited( n ) != ntk.trav_id() ) + { + topo_order.push_back( n ); + ntk.set_visited( n, ntk.trav_id() ); + } + } ); + + /* sort topologically */ + ntk.foreach_co( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + return; + multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + } + + /* Experimental code resticted to only half adders and full adders */ + void multi_enumerate_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes ) + { + static_assert( max_multioutput_cut_size > 1 && max_multioutput_cut_size < 7 ); + + uint32_t counter = 0; + multi_leaves_set_t leaves = { 0 }; + + ntk.foreach_gate( [&]( auto const& n ) { + uint32_t cut_index = 0; + for ( auto& cut : multi_cuts.cuts( ntk.node_to_index( n ) ) ) + { + kitty::static_truth_table tt = multi_cuts.truth_table( *cut ); + /* reduce support for matching ID */ + uint64_t tt_id = ( cut->size() < 3 ) ? ( tt._bits & 0xF ) : tt._bits; + uint64_t id = library.get_multi_function_id( tt_id ); + + if ( !id ) + { + ++cut_index; + continue; + } + + ( *cut )->data.id = id; + + multi_match_data data; + data.node_index = ntk.node_to_index( n ); + data.cut_index = cut_index; + leaves[2] = 0; + uint32_t i = 0; + for ( auto l : *cut ) + leaves[i++] = l; + + /* add to hash table */ + multi_cuts_classes[leaves].push_back( data ); + + ++cut_index; + } + } ); + } + + /* Experimental code */ + void multi_compute_matches( multi_cuts_t const& multi_cuts, multi_hash_t& multi_cuts_classes, multi_single_matches_t& multi_node_match_local ) + { + ntk.clear_values(); + + /* copy set and sort by gate size: improve, too slow */ + std::vector> class_list; + class_list.reserve( multi_cuts_classes.size() ); + for ( auto& it : multi_cuts_classes ) + { + /* insert multiple occurring cuts */ + if ( it.second.size() > 1 ) + class_list.push_back( it ); + } + + std::stable_sort( class_list.begin(), class_list.end(), [&]( auto const& a, auto const& b ) { + return a.first[2] > b.first[2]; + } ); + + /* combine and match: specific code for 2-output cells */ + for ( auto it : class_list ) + { + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + multi_match_data data_i = it.second[i]; + uint32_t index_i = data_i.node_index; + uint32_t cut_index_i = data_i.cut_index; + auto const& cut_i = multi_cuts.cuts( index_i )[cut_index_i]; + + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + multi_match_data data_j = it.second[j]; + uint32_t index_j = data_j.node_index; + uint32_t cut_index_j = data_j.cut_index; + auto const& cut_j = multi_cuts.cuts( index_j )[cut_index_j]; + + /* not compatible -> TODO: change */ + if ( cut_i->data.id == cut_j->data.id ) + continue; + + /* check compatibility */ + if ( !multi_check_partally_dangling( index_i, index_j, cut_i ) ) + continue; + + multi_node_match_local.push_back( { data_i, data_j } ); + } + } + } + } + + /* Experimental code */ + template + void multi_filter_and_match( multi_cuts_t const& multi_cuts, multi_single_matches_t const& multi_node_match_local ) + { + multi_cut_set.reserve( multi_node_match_local.size() ); + multi_node_match.reserve( multi_node_match_local.size() ); + + ntk.incr_trav_id(); + + for ( auto& pair : multi_node_match_local ) + { + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + uint32_t cut_index2 = pair[1].cut_index; + multi_cut_t const& cut1 = multi_cuts.cuts( index1 )[cut_index1]; + multi_cut_t const& cut2 = multi_cuts.cuts( index2 )[cut_index2]; + + assert( index1 < index2 ); + + /* remove incompatible multi-output cuts */ + bool is_new = true; + uint32_t insertion_index = multi_node_match.size(); + if constexpr ( OverlapFilter ) + { + if ( multi_gate_check_overlapping( index1, index2, cut1 ) ) + continue; + } + else + { + if ( multi_gate_check_incompatible( index1, index2, is_new, insertion_index ) ) + continue; + // if ( is_new && multi_gate_check_overlapping( index1, index2, cut1 ) ) + // continue; + } + + /* copy cuts */ + cut_t new_cut1, new_cut2; + new_cut1.set_leaves( cut1.begin(), cut1.end() ); + new_cut2.set_leaves( cut2.begin(), cut2.end() ); + new_cut1->function = kitty::extend_to<6>( multi_cuts.truth_table( cut1 ) ); + new_cut2->function = kitty::extend_to<6>( multi_cuts.truth_table( cut2 ) ); + + /* Multi-output Boolean matching, continue if no match */ + std::array cut_pair = { new_cut1, new_cut2 }; + if ( !multi_compute_cut_data( cut_pair ) ) + continue; + + /* mark multioutput gate */ + if constexpr ( OverlapFilter ) + { + multi_gate_mark_visited( index1, index2, cut1 ); + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = multi_node_match.size(); + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = multi_node_match.size(); + } + else + { + // multi_gate_mark_visited( index1, index2, cut1 ); + multi_gate_mark_compatibility( index1, index2, insertion_index ); + } + + /* add cut */ + multi_cut_set.push_back( cut_pair ); + + /* re-index data */ + multi_match_data new_data1, new_data2; + new_data1.node_index = index1; + new_data1.cut_index = multi_cut_set.size() - 1; + new_data2.node_index = index2; + new_data2.cut_index = multi_cut_set.size() - 1; + multi_match_t p = { new_data1, new_data2 }; + + /* add cuts to the correct bucket */ + if ( is_new ) + { + multi_node_match.push_back( { p } ); + } + else + { + multi_node_match[insertion_index].push_back( p ); + } + } + } + + bool multi_compute_cut_data( std::array& cut_tuple ) + { + std::array, max_multioutput_output_size> tts; + std::array, max_multioutput_output_size> tts_order; + std::array order = {}; + std::array phase = { 0 }; + std::array phase_order; + + std::iota( order.begin(), order.end(), 0 ); + + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + tts[i] = kitty::extend_to<6>( cut_tuple[i]->function ); + if ( ( tts[i]._bits & 1 ) == 1 ) + { + tts[i] = ~tts[i]; + phase[i] = 1; + } + } + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return tts[a] < tts[b]; + } ); + + std::transform( order.begin(), order.end(), tts_order.begin(), [&]( size_t a ) { + return tts[a]; + } ); + + std::transform( order.begin(), order.end(), phase_order.begin(), [&]( uint8_t a ) { + return phase[a]; + } ); + + auto const multigates_match = library.get_multi_supergates( tts_order ); + + /* Ignore not matched cuts */ + if ( multigates_match == nullptr ) + return false; + + /* add cut matches */ + for ( auto i = 0; i < max_multioutput_output_size; ++i ) + { + cut_tuple[order[i]]->supergates[0] = nullptr; + cut_tuple[order[i]]->supergates[1] = nullptr; + cut_tuple[order[i]]->ignore = false; + std::vector> const* multigate = &( ( *multigates_match )[i] ); + cut_tuple[order[i]]->supergates[phase_order[i]] = multigate; + } + + return true; + } + + inline bool multi_check_partally_dangling( uint32_t index1, uint32_t index2, multi_cut_t const& cut1 ) + { + bool valid = true; + + /* check containment of cut1 in cut2 and viceversa */ + if ( index1 > index2 ) + { + std::swap( index1, index2 ); + } + + ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 ) + { + valid = false; + } + return valid; + } ); + + if ( !valid ) + return false; + + if ( !is_contained_mffc( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut1 ) ) + return false; + + return true; + } + + inline bool multi_gate_check_overlapping( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + bool contained = false; + + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + contained = multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + contained |= multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_gate_check_incompatible( uint32_t index1, uint32_t index2, bool& is_new, uint32_t& data_index ) + { + /* check cut assigned cut outputs, specialized code for 2 outputs */ + if ( !node_tuple_match[index1].has_info && !node_tuple_match[index2].has_info ) + return false; + + if ( node_tuple_match[index1].has_info && node_tuple_match[index2].has_info ) + { + uint32_t current_assignment = node_tuple_match[index1].index; + if ( current_assignment != node_tuple_match[index2].index ) + return true; + is_new = false; + data_index = current_assignment; + return false; + } + + return true; + } + + inline void multi_gate_mark_compatibility( uint32_t index1, uint32_t index2, uint32_t mark_value ) + { + node_tuple_match[index1].has_info = 1; + node_tuple_match[index1].lowest_index = 1; + node_tuple_match[index1].index = mark_value; + node_tuple_match[index2].has_info = 1; + node_tuple_match[index2].highest_index = 1; + node_tuple_match[index2].index = mark_value; + } + + inline void multi_gate_mark_visited( uint32_t index1, uint32_t index2, multi_cut_t const& cut ) + { + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + /* mark */ + multi_mark_visited_rec( ntk.index_to_node( index1 ) ); + multi_mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + template + bool multi_mark_visited_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + if constexpr ( MARK ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + + bool contained = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + contained |= multi_mark_visited_rec( ntk.get_node( f ) ); + + if constexpr ( !MARK ) + { + if ( contained ) + return false; + } + + return true; + } ); + + return contained; + } + + bool is_contained_mffc( node root, node n, multi_cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + bool valid = true; + tmp_visited.clear(); + dereference_node_rec( root ); + + if ( ntk.fanout_size( n ) == 0 ) + valid = false; + + for ( uint64_t g : tmp_visited ) + ntk.incr_fanout_size( ntk.index_to_node( g ) ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return valid; + } + + void dereference_node_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + node g = ntk.get_node( f ); + if ( ntk.decr_fanout_size( g ) == 0 ) + { + dereference_node_rec( g ); + } + tmp_visited.push_back( ntk.node_to_index( g ) ); + } ); + } + + void multi_add_choices( choice_view& choice_ntk ) + { + for ( auto& field : multi_node_match ) + { + auto& pair = field.front(); + uint32_t index1 = pair[0].node_index; + uint32_t index2 = pair[1].node_index; + uint32_t cut_index1 = pair[0].cut_index; + cut_t const& cut = multi_cut_set[cut_index1][0]; + + /* don't add choice if in TFI, set TFI bit */ + if ( multi_is_in_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ) ) + { + /* if there is a path of length > 1 linking node 1 and 2, save as TFI node */ + uint32_t in_tfi = multi_is_in_direct_tfi( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ) ) ? 0 : 1; + for ( auto& match : field ) + match[0].in_tfi = in_tfi; + /* add a TFI dependency */ + ntk.set_value( ntk.index_to_node( index1 ), index2 ); + // multi_set_tfi_dependency( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ); + continue; + } + + choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) ); + + assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 ); + } + } + + bool multi_topo_sort_rec( choice_view& choice_ntk, node const& n ) + { + /* is permanently marked? */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( n ) == ntk.trav_id() - 1 ) + return false; + + /* get the representative (smallest index) */ + node repr = choice_ntk.get_choice_representative( n ); + + /* loop detected: backtrack to remove the cause */ + if ( ntk.visited( repr ) == ntk.trav_id() - 1 ) + return false; + + /* solve the TFI dependency first */ + node dependency_node = ntk.index_to_node( ntk.value( n ) ); + if ( dependency_node > 0 && ntk.visited( dependency_node ) != ntk.trav_id() - 1 ) + { + if ( !multi_topo_sort_rec( choice_ntk, dependency_node ) ) + return false; + assert( ntk.visited( n ) == ntk.trav_id() ); + return true; + } + + /* for all the choices */ + uint32_t i = 0; + bool check = true; + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( g ) != ntk.trav_id() ); + assert( ntk.visited( g ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( g, ntk.trav_id() - 1 ); + + /* mark children */ + ntk.foreach_fanin( g, [&]( auto const& f ) { + check = multi_topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + return check; + } ); + + /* cycle detected: backtrack to the last choice jump */ + if ( !check ) + { + /* revert visited */ + ntk.set_visited( g, ntk.trav_id() - 2 ); + if ( i > 0 && n == repr ) + { + /* fix cycle: remove multi-output match */ + choice_ntk.foreach_choice( repr, [&]( auto const& p ) { + node_tuple_match[ntk.node_to_index( p )].data = 0; + return true; + } ); + choice_ntk.remove_choice( g ); + check = true; + } + return false; + } + + ++i; + return true; + } ); + + if ( !check ) + { + return false; + } + + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited */ + assert( ntk.visited( g ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( g, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( g ); + + return true; + } ); + + return true; + } + + inline bool multi_is_in_tfi( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + multi_mark_visited_rec( root ); + bool contained = ntk.visited( n ) == ntk.trav_id(); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + inline bool multi_is_in_direct_tfi( node const& root, node const& n ) + { + bool contained = false; + + ntk.foreach_fanin( root, [&]( auto const& f ) { + if ( ntk.get_node( f ) == n ) + contained = true; + } ); + + return contained; + } + + inline void multi_set_tfi_dependency( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + + /* add a TFI dependencies */ + ntk.set_value( n, ntk.node_to_index( root ) ); + ntk.set_visited( n, ntk.trav_id() ); + multi_set_tfi_dependency_rec( root, ntk.node_to_index( root ) ); + + /* reset root's dependency info */ + ntk.set_value( root, 0 ); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + } + + void multi_set_tfi_dependency_rec( node const& n, uint32_t const dependency_info ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + ntk.set_value( n, dependency_info ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + multi_set_tfi_dependency_rec( ntk.get_node( f ), dependency_info ); + } ); + } +#pragma endregion + +private: + Ntk const& ntk; + tech_library const& library; + emap_params const& ps; + emap_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + uint32_t inv{ 0 }; /* current inverter count */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + uint32_t lib_inv_id; + + /* lib buffer info */ + float lib_buf_area; + float lib_buf_delay; + uint32_t lib_buf_id; + + std::vector> topo_order; + node_match_t node_match; + std::vector node_tuple_match; + std::vector switch_activity; + std::vector tmp_visited; + + /* cut computation */ + std::vector cuts; /* compressed representation of cuts */ + cut_merge_t lcuts; /* cut merger container */ + cut_set_t temp_cuts; /* temporary cut set container */ + truth_compute_t ltruth; /* truth table merger container */ + support_t lsupport; /* support merger container */ + uint32_t cuts_total{ 0 }; /* current computed cuts */ + + /* multi-output matching */ + multi_cut_set_t multi_cut_set; /* set of multi-output cuts */ + multi_matches_t multi_node_match; /* matched multi-output gates */ + + time_point time_begin; +}; + +} /* namespace detail */ + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a block network that supports multi-output cells. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +cell_view emap( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_block(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a k-LUT network. Each LUT abstacts a gate of the technology library. + * + * The novelties of this mapper are contained in 2 publications: + * - A. Tempia Calvino and G. De Micheli, "Technology Mapping Using Multi-Output Library Cells," ICCAD, 2023. + * - G. Radi, A. Tempia Calvino, and G. De Micheli, "In Medio Stat Virtus: Combining Boolean and Pattern Matching," ASP-DAC, 2024. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_klut( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_klut(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the best implementation in the technology library. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view emap_node_map( Ntk const& ntk, tech_library const& library, emap_params const& ps = {}, emap_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + emap_stats st; + detail::emap_impl p( ntk, library, ps, st ); + auto res = p.run_node_map(); + + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology node mapping. + * + * This function implements a simple technology mapping algorithm. + * The algorithm maps each node to the first implementation in the technology library. + * + * The input must be a binding_view with the gates correctly loaded. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_binding` + * + * \param ntk Network + * + */ +template +void emap_load_mapping( Ntk& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + + /* build the library map */ + using lib_t = std::unordered_map>; + lib_t tt_to_gate; + + for ( auto const& g : ntk.get_library() ) + { + tt_to_gate[g.function] = g.id; + } + + ntk.foreach_gate( [&]( auto const& n ) { + if ( auto it = tt_to_gate.find( ntk.node_function( n ) ); it != tt_to_gate.end() ) + { + ntk.add_binding( n, it->second ); + } + else + { + std::cout << fmt::format( "[e] node mapping for node {} failed: no match in the tech library\n", ntk.node_to_index( n ) ); + } + } ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/equivalence_checking.hpp b/include/mockturtle/algorithms/equivalence_checking.hpp new file mode 100644 index 0000000..686cbfc --- /dev/null +++ b/include/mockturtle/algorithms/equivalence_checking.hpp @@ -0,0 +1,325 @@ +/* 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 equivalence_checking.hpp + \brief Combinational equivalence checking + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "functional_reduction.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/stopwatch.hpp" +#include "../networks/klut.hpp" +#include "cnf.hpp" + +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for equivalence_checking. + * + * The data structure `equivalence_checking_params` holds configurable + * parameters with default arguments for `equivalence_checking`. + */ +struct equivalence_checking_params +{ + /*! \brief Conflict limit for SAT solver. + * + * The default limit is 0, which means the number of conflicts is not used + * as a resource limit. + */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Whether to apply functional reduction before SAT solving. */ + bool functional_reduction{ true }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for equivalence_checking. + * + * The data structure `equivalence_checking_stats` provides data collected by + * running `equivalence_checking`. + */ +struct equivalence_checking_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{}; + + /*! \brief Counter-example, in case miter is not equivalent. */ + std::vector counter_example; + + void report() const + { + if ( counter_example.size() > 0 ) + { + std::cout << "[i] Networks are not equivalent under input assignment: "; + for ( auto i = 0u; i < counter_example.size(); ++i ) + std::cout << "pi" << i << "=" << counter_example[i] << " "; + std::cout << "\n"; + } + + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class equivalence_checking_impl +{ +public: + equivalence_checking_impl( Ntk const& miter, equivalence_checking_params const& ps, equivalence_checking_stats& st ) + : miter_( miter ), + ps_( ps ), + st_( st ) + { + } + + std::optional run() + { + stopwatch<> t( st_.time_total ); + + percy::bsat_wrapper solver; + int output; + + if ( ps_.functional_reduction ) + { + Ntk opt = miter_.clone(); + if constexpr ( !std::is_same_v ) + { + functional_reduction( opt ); + opt = cleanup_dangling( opt ); + } + + if ( opt.num_gates() == 0 ) + { + return opt.po_at( 0 ) == opt.get_constant( false ); + } + + output = generate_cnf( opt, [&]( auto const& clause ) { + solver.add_clause( clause ); + } )[0]; + } + else + { + output = generate_cnf( miter_, [&]( auto const& clause ) { + solver.add_clause( clause ); + } )[0]; + } + + const auto res = solver.solve( &output, &output + 1, ps_.conflict_limit ); + + switch ( res ) + { + default: + return std::nullopt; + case percy::synth_result::success: + { + st_.counter_example.clear(); + for ( auto i = 1u; i <= miter_.num_pis(); ++i ) + { + st_.counter_example.push_back( solver.var_value( i ) ); + } + return false; + } + case percy::synth_result::failure: + return true; + } + } + +private: + Ntk const& miter_; + equivalence_checking_params const& ps_; + equivalence_checking_stats& st_; +}; + +template +class equivalence_checking_impl_bill +{ +public: + equivalence_checking_impl_bill( Ntk const& miter, equivalence_checking_params const& ps, equivalence_checking_stats& st ) + : miter_( miter ), + ps_( ps ), + st_( st ) + { + } + + std::optional run() + { + stopwatch<> t( st_.time_total ); + + bill::solver solver; + bill::lit_type output = convert_to_cnf( miter_, solver ); + + const auto res = solver.solve( {output}, ps_.conflict_limit ); + + switch ( res ) + { + default: + return std::nullopt; + case bill::result::states::satisfiable: + { + st_.counter_example.clear(); + for ( auto i = 1u; i <= miter_.num_pis(); ++i ) + { + st_.counter_example.push_back( solver.get_model().model().at( i ) == bill::lbool_type::true_ ); + } + return false; + } + case bill::result::states::unsatisfiable: + return true; + } + } + +private: + bill::lit_type convert_to_cnf( Ntk const& ntk, bill::solver& solver ) + { + node_map literals( ntk ); + + literals[ntk.get_constant( false )] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + literals[ntk.get_constant( true )] = lit_not( literals[ntk.get_constant( false )] ); + } + solver.add_clause( {~literals[ntk.get_constant( false )]} ); + + ntk.foreach_pi( [&]( auto const& n ) { + literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + } ); + ntk.foreach_gate( [&]( auto const& n ) { + literals[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + } ); + + if constexpr ( has_EXCDC_interface_v ) + { + ntk.add_EXCDC_clauses( solver ); + } + + return generate_cnf( ntk, [&]( bill::result::clause_type const& clause ) { + solver.add_clause( clause ); + }, literals )[0]; + } + +private: + Ntk const& miter_; + equivalence_checking_params const& ps_; + equivalence_checking_stats& st_; +}; + +} // namespace detail + +/*! \brief Combinational equivalence checking. + * + * This function expects as input a miter circuit that can be generated, e.g., + * with the function `miter`. It returns an optional which is `nullopt`, if no + * solution can be found (this happens when a resource limit is set using the + * function's parameters). Otherwise it returns `true`, if the miter is + * equivalent or `false`, if the miter is not equivalent. In the latter case + * the counter example is written to the statistics pointer as a + * `std::vector` following the same order as the primary inputs. + * + * \param miter Miter network + * \param ps Parameters + * \param st Statistics + */ +template +std::optional equivalence_checking( Ntk const& miter, equivalence_checking_params const& ps = {}, equivalence_checking_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + if ( miter.num_pos() != 1u ) + { + std::cout << "[e] miter network must have a single output\n"; + return std::nullopt; + } + + equivalence_checking_stats st; + detail::equivalence_checking_impl impl( miter, ps, st ); + const auto result = impl.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +template +std::optional equivalence_checking_bill( Ntk const& miter, equivalence_checking_params const& ps = {}, equivalence_checking_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + if ( miter.num_pos() != 1u ) + { + std::cout << "[e] miter network must have a single output\n"; + return std::nullopt; + } + + equivalence_checking_stats st; + detail::equivalence_checking_impl_bill impl( miter, ps, st ); + const auto result = impl.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return result; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/equivalence_classes.hpp b/include/mockturtle/algorithms/equivalence_classes.hpp new file mode 100644 index 0000000..e13efa3 --- /dev/null +++ b/include/mockturtle/algorithms/equivalence_classes.hpp @@ -0,0 +1,156 @@ +/* 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 equivalence_classes.hpp + \brief Synthesis routines based on equivalence classes + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Applies a sequence of transformations to a network + * + * A synthesis function `synthesis_fn` computes a network into `dest` and + * computes an output signal. Both the inputs and the outputs to that synthesis + * function are transformed according to `transformations`, a sequence of + * spectral transformations. The vector `leaves` are the original inputs to the + * output function, which is returned in terms of a signal into the network. + * + * The signature of `synthesis_fn` is `signal(Ntk&, std::vector> const&)`. + * + * \param dest Destination network for synthesis + * \param transformations Sequence of spectral operations (see kitty) + * \param leaves Original inputs, which might be transformed + * \param synthesis_fn Synthesis function to create the inner function (without transformations) + * + \verbatim embed:rst + + .. note:: + + An example on how to transform the AND function into the MAJ function is + provided as test. + \endverbatim + */ +template +signal apply_spectral_transformations( Ntk& dest, std::vector const& transformations, std::vector> const& leaves, SynthesisFn&& synthesis_fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor method" ); + static_assert( std::is_invocable_r_v, SynthesisFn, Ntk&, std::vector> const&>, "SynthesisFn does not have expected signature" ); + + auto _leaves = leaves; + std::vector> _final_xors; + bool _output_neg = false; + + for ( auto const& t : transformations ) + { + switch ( t._kind ) + { + default: + assert( false ); + case kitty::detail::spectral_operation::kind::permutation: + { + const auto v1 = kitty::detail::log2[t._var1]; + const auto v2 = kitty::detail::log2[t._var2]; + std::swap( _leaves[v1], _leaves[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::input_negation: + { + const auto v1 = kitty::detail::log2[t._var1]; + _leaves[v1] = dest.create_not( _leaves[v1] ); + } + break; + case kitty::detail::spectral_operation::kind::output_negation: + _output_neg = !_output_neg; + break; + case kitty::detail::spectral_operation::kind::spectral_translation: + { + const auto v1 = kitty::detail::log2[t._var1]; + const auto v2 = kitty::detail::log2[t._var2]; + _leaves[v1] = dest.create_xor( _leaves[v1], _leaves[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::disjoint_translation: + { + const auto v1 = kitty::detail::log2[t._var1]; + _final_xors.push_back( _leaves[v1] ); + } + break; + } + } + + _final_xors.push_back( synthesis_fn( dest, _leaves ) ); + const auto output = dest.create_nary_xor( _final_xors ); + return _output_neg ? dest.create_not( output ) : output; +} + +/*! \brief Applies NPN transformations to a network + * + * A synthesis function `synthesis_fn` computes a network into `dest` and + * computes an output signal. Both the inputs and the outputs to that synthesis + * function are transformed according to `phase` and `perm`, based on NPN + * classification. The vector `leaves` are the original inputs to the + * output function, which is returned in terms of a signal into the network. + * + * The signature of `synthesis_fn` is `signal(Ntk&, std::vector> const&)`. + * + * \param dest Destination network for synthesis + * \param phase Input and output complementation + * \param perm Input permutation + * \param leaves Original inputs, which might be transformed + * \param synthesis_fn Synthesis function to create the inner function (without transformations) + */ +template +signal apply_npn_transformations( Ntk& dest, uint32_t phase, std::vector const& perm, std::vector> const& leaves, SynthesisFn&& synthesis_fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor method" ); + static_assert( std::is_invocable_r_v, SynthesisFn, Ntk&, std::vector> const&>, "SynthesisFn does not have expected signature" ); + + std::vector> _leaves( perm.size() ); + std::transform( perm.begin(), perm.end(), _leaves.begin(), [&]( auto const& i ) { return ( phase >> i ) & 1 ? dest.create_not( leaves[i] ) : leaves[i]; } ); + + const auto f = synthesis_fn( dest, _leaves ); + return ( phase >> leaves.size() ) & 1 ? dest.create_not( f ) : f; +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/exact_mc_synthesis.hpp b/include/mockturtle/algorithms/exact_mc_synthesis.hpp new file mode 100644 index 0000000..2c195c1 --- /dev/null +++ b/include/mockturtle/algorithms/exact_mc_synthesis.hpp @@ -0,0 +1,638 @@ +/* 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 exact_mc_synthesis.hpp + \brief SAT-based XAG synthesis based on MC + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../algorithms/simulation.hpp" +#include "../generators/sorting.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/xag.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cnf_view.hpp" +#include "cnf.hpp" + +namespace mockturtle +{ + +struct exact_mc_synthesis_params +{ + /* \brief Minimum number of AND gates. */ + uint32_t min_and_gates{ 0u }; + + /*! \brief Use CEGAR based solving strategy. */ + bool use_cegar{ false }; + + /*! \brief Use subset symmetry breaking. */ + bool break_subset_symmetries{ true }; + + /*! \brief Use multi-level subset symmetry breaking. */ + bool break_multi_level_subset_symmetries{ true }; + + /*! \brief Use symmetric variables. */ + bool break_symmetric_variables{ true }; + + /*! \brief User-specified variable symmetries. */ + std::vector> custom_symmetric_variables; + + /*! \brief Ensure to use all gates and essential variables. */ + bool ensure_to_use_gates{ true }; + + /*! \brief Heuristic XOR bound (based on sorter network). */ + std::optional heuristic_xor_bound{}; + + /*! \brief Updates XOR bound heuristic after each found solution. */ + bool auto_update_xor_bound{ false }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Use conflict limit only when searching for multiple solutions + * + * The conflict limit will be ignored for the first call. + */ + bool ignore_conflict_limit_for_first_solution{ false }; + + /*! \brief Show progress (in CEGAR). */ + bool progress{ false }; + + /*! \brief Write DIMACS file, everytime solve is called. */ + std::optional write_dimacs{}; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose */ + bool very_verbose{ false }; +}; + +struct exact_mc_synthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{}; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_solving{}; + + /*! \brief Total number of variables. */ + uint32_t num_vars{}; + + /*! \brief Total number of clauses. */ + uint32_t num_clauses{}; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] solving time = {:>5.2f} secs\n", to_seconds( time_solving ) ); + fmt::print( "[i] total vars = {}\n", num_vars ); + fmt::print( "[i] total clauses = {}\n", num_clauses ); + } +}; + +namespace detail +{ + +template +struct exact_mc_synthesis_impl +{ + using problem_network_t = cnf_view; + + exact_mc_synthesis_impl( kitty::dynamic_truth_table const& func, uint32_t num_solutions, exact_mc_synthesis_params const& ps, exact_mc_synthesis_stats& st ) + : num_vars_( func.num_vars() ), + func_( kitty::get_bit( func, 0 ) ? ~func : func ), + invert_( kitty::get_bit( func, 0 ) ), + heuristic_xor_bound_( ps.heuristic_xor_bound ), + num_solutions_( num_solutions ), + ps_( ps ), + st_( st ) + { + } + + std::vector run() + { + stopwatch<> t( st_.time_total ); + + std::vector ntks; + const auto degree = kitty::polynomial_degree( func_ ); + uint32_t num_ands = std::max( ps_.min_and_gates, degree == 0u ? degree : degree - 1u ); + + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "try with {} AND gates\n", num_ands ); + } + + cnf_view_params cvps; + cvps.write_dimacs = ps_.write_dimacs; + problem_network_t pntk( cvps ); + reset( pntk ); + + for ( auto i = 0u; i < num_ands; ++i ) + { + add_gate( pntk ); + } + add_output( pntk ); + if ( ps_.heuristic_xor_bound || ps_.auto_update_xor_bound ) + { + add_xor_counter( pntk ); + } + + // TODO use LUT mapping before CNF generation + if ( const auto sol = ps_.use_cegar ? solve_with_cegar( pntk ) : solve_direct( pntk ); sol ) + { + ntks.push_back( *sol ); + if ( ps_.very_verbose ) + { + debug_solution( pntk ); + } + while ( ntks.size() < num_solutions_ ) + { + block( pntk ); + if ( const auto result = solve( pntk, false ); result && *result ) + { + ntks.push_back( extract_network( pntk ) ); + if ( ps_.very_verbose ) + { + debug_solution( pntk ); + fmt::print( "[i] found {} solutions so far\n", ntks.size() ); + } + } + else + { + break; + } + } + return ntks; + } + ++num_ands; + } + } + +private: + std::optional solve_direct( problem_network_t& pntk ) + { + prune_search_space( pntk ); + + for ( auto b = 1u; b < func_.num_bits(); ++b ) + { + constrain_assignment( pntk, b ); + } + + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + if ( const auto result = solve( pntk, true ); result && *result ) + { + return extract_network( pntk ); + } + else + { + return std::nullopt; + } + } + + std::optional solve_with_cegar( problem_network_t& pntk ) + { + prune_search_space( pntk ); + + uint32_t num_ands = static_cast( ltfi_vars_.size() ) / 2, bctr = 0u; + progress_bar pbar( static_cast( func_.num_bits() ), "exact_mc_synthesis |{}| ANDs = {} asserted bits = {} SAT solving time = {:.2f} secs", ps_.progress ); + while ( true ) + { + pbar( bctr, num_ands, bctr, to_seconds( st_.time_solving ) ); + if ( const auto result = solve( pntk, true ); result && *result ) + { + const auto sol = extract_network( pntk ); + default_simulator sim( num_vars_ ); + const auto simulated = simulate( sol, sim )[0u]; + if ( const auto bit = kitty::find_first_bit_difference( func_, simulated ); bit == -1 ) + { + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + return sol; + } + else + { + constrain_assignment( pntk, static_cast( bit ) ); + bctr++; + } + } + else + { + st_.num_vars += pntk.num_vars(); + st_.num_clauses += pntk.num_clauses(); + return std::nullopt; + } + } + } + + std::optional solve( problem_network_t& pntk, bool first ) + { + stopwatch<> t_sat( st_.time_solving ); + bill::result::clause_type assumptions; + pntk.foreach_po( [&]( auto const& f ) { + assumptions.push_back( pntk.lit( f ) ); + } ); + if ( heuristic_xor_bound_ ) + { + if ( int32_t pos = static_cast( xor_counter_.size() ) - *heuristic_xor_bound_ - 1; pos >= 0 ) + { + assumptions.push_back( pntk.lit( !xor_counter_[pos] ) ); + } + } + const auto res = pntk.solve( assumptions, ps_.ignore_conflict_limit_for_first_solution && first ? 0u : ps_.conflict_limit ); + + if ( ps_.auto_update_xor_bound && res && *res ) + { + heuristic_xor_bound_ = count_xors( pntk ) - 1u; + } + + return res; + } + +private: + Ntk extract_network( problem_network_t& pntk ) + { + Ntk xag; + std::vector> nodes( num_vars_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return xag.create_pi(); } ); + + const auto extract_ltfi = [&]( std::vector> const& ltfi_vars ) -> signal { + std::vector> ltfi; + for ( auto j = 0u; j < ltfi_vars.size(); ++j ) + { + if ( pntk.model_value( ltfi_vars[j] ) ) + { + ltfi.push_back( nodes[j] ); + } + } + return xag.create_nary_xor( ltfi ); + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2; ++i ) + { + nodes.push_back( xag.create_and( extract_ltfi( ltfi_vars_[2 * i] ), extract_ltfi( ltfi_vars_[2 * i + 1] ) ) ); + } + + const auto c = extract_ltfi( ltfi_vars_.back() ); + xag.create_po( invert_ ? xag.create_not( c ) : c ); + + return xag; + } + + void block( problem_network_t& pntk ) + { + std::vector> blocked_lits; + for ( auto const& ltfi : ltfi_vars_ ) + { + for ( auto const& l : ltfi ) + { + blocked_lits.push_back( l ^ pntk.model_value( l ) ); + } + } + pntk.add_clause( blocked_lits ); + } + + void reset( problem_network_t const& pntk ) + { + // TODO: can we make this more iterative? + ltfi_vars_.clear(); + truth_vars_.clear(); + truth_vars_.resize( func_.num_bits() ); + + /* pre-assign truth_vars_ with primary inputs */ + for ( auto i = 0u; i < num_vars_; ++i ) + { + const auto var_tt = kitty::nth_var( num_vars_, i ); + for ( auto b = 0u; b < func_.num_bits(); ++b ) + { + truth_vars_[b].push_back( pntk.get_constant( kitty::get_bit( var_tt, b ) ) ); + } + } + } + + void add_gate( problem_network_t& pntk ) + { + uint32_t gate_index = static_cast( ltfi_vars_.size() ) / 2; + + // add select variables + for ( auto j = 0u; j < 2u; ++j ) + { + ltfi_vars_.push_back( std::vector>( num_vars_ + gate_index ) ); + std::generate( ltfi_vars_.back().begin(), ltfi_vars_.back().end(), [&]() { return pntk.create_pi(); } ); + } + } + + void add_output( problem_network_t& pntk ) + { + ltfi_vars_.push_back( std::vector>( num_vars_ + ltfi_vars_.size() / 2 ) ); + std::generate( ltfi_vars_.back().begin(), ltfi_vars_.back().end(), [&]() { return pntk.create_pi(); } ); + } + + void constrain_assignment( problem_network_t& pntk, uint32_t bit ) + { + const auto create_xor_clause = [&]( std::vector> const& ltfi_vars ) -> signal { + std::vector> ltfi( ltfi_vars.size() ); + for ( auto j = 0u; j < ltfi.size(); ++j ) + { + ltfi[j] = pntk.create_and( ltfi_vars[j], truth_vars_[bit][j] ); + } + return pntk.create_nary_xor( ltfi ); + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2; ++i ) + { + truth_vars_[bit].push_back( pntk.create_and( create_xor_clause( ltfi_vars_[2 * i] ), create_xor_clause( ltfi_vars_[2 * i + 1] ) ) ); + } + + const auto po_signal = create_xor_clause( ltfi_vars_.back() ); + pntk.create_po( kitty::get_bit( func_, bit ) ? po_signal : pntk.create_not( po_signal ) ); + } + + void prune_search_space( problem_network_t& pntk ) + { + // At least one element in LTFI + for ( auto const& ltfi : ltfi_vars_ ) + { + pntk.add_clause( ltfi ); + } + + // linear TFIs are no subset of each other + if ( ps_.break_subset_symmetries ) + { + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + auto const& ltfi1 = ltfi_vars_[2 * i]; + auto const& ltfi2 = ltfi_vars_[2 * i + 1]; + + std::vector> ands( ltfi1.size() ); + std::vector> ands2( ltfi1.size() ); + for ( auto j = 0u; j < ltfi1.size(); ++j ) + { + ands[j] = pntk.create_and( ltfi1[j], pntk.create_not( ltfi2[j] ) ); + ands2[j] = pntk.create_and( ltfi2[j], pntk.create_not( ltfi1[j] ) ); + } + pntk.add_clause( ands ); + pntk.add_clause( ands2 ); + } + } + + // left linear TFI is lexicographically smaller than right one + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + auto const& ltfi2 = ltfi_vars_[2 * i]; + auto const& ltfi1 = ltfi_vars_[2 * i + 1]; + + auto n = ltfi1.size(); + std::vector> as( n - 1u ); + std::generate( as.begin(), as.end(), [&]() { return pntk.create_pi(); } ); + + pntk.add_clause( !ltfi1[0], ltfi2[0] ); + pntk.add_clause( !ltfi1[0], as[0] ); + pntk.add_clause( ltfi2[0], as[0] ); + + for ( auto k = 1u; k < n - 1; ++k ) + { + pntk.add_clause( !ltfi1[k], ltfi2[k], !as[k - 1] ); + pntk.add_clause( !ltfi1[k], as[k], !as[k - 1] ); + pntk.add_clause( ltfi2[k], as[k], !as[k - 1] ); + } + pntk.add_clause( !ltfi1.back(), !as.back() ); + pntk.add_clause( ltfi2.back(), !as.back() ); + } + + // break on multi-level subset relation + if ( ps_.break_multi_level_subset_symmetries ) + { + for ( auto ii = 0u; ii < ltfi_vars_.size(); ++ii ) + { + const auto& ltfi = ltfi_vars_[ii]; + for ( auto i = 0u; i < ii / 2u; ++i ) + { + const auto n = ltfi_vars_[2 * i].size(); + std::vector> ands_left, ands_right; + ands_left.push_back( ltfi[num_vars_ + i] ); + for ( auto k = 0u; k < n; ++k ) + { + ands_left.push_back( pntk.create_or( !ltfi[k], ltfi_vars_[2 * i][k] ) ); + ands_left.push_back( pntk.create_or( !ltfi[k], ltfi_vars_[2 * i + 1][k] ) ); + ands_right.push_back( pntk.create_xnor( ltfi[k], pntk.create_and( ltfi_vars_[2 * i][k], ltfi_vars_[2 * i + 1][k] ) ) ); + } + pntk.create_po( pntk.create_or( !pntk.create_nary_and( ands_left ), pntk.create_nary_and( ands_right ) ) ); + } + } + } + + // break on symmetric variables + if ( ps_.break_symmetric_variables ) + { + const auto break_symmetric_vars = [&]( auto j, auto jj ) { + if ( ps_.very_verbose ) + { + fmt::print( "[i] symmetry breaking based on symmetric variables {} and {}\n", j, jj ); + } + for ( auto ii = 0u; ii < ltfi_vars_.size(); ++ii ) + { + std::vector> clause; + clause.push_back( !ltfi_vars_[ii][jj] ); + for ( auto i = 0u; i <= ii; ++i ) + { + clause.push_back( ltfi_vars_[i][j] ); + } + pntk.add_clause( clause ); + } + }; + + for ( auto jj = 1u; jj < num_vars_; ++jj ) + { + for ( auto j = 0u; j < jj; ++j ) + { + if ( kitty::is_symmetric_in( func_, j, jj ) ) + { + break_symmetric_vars( j, jj ); + } + } + } + + for ( const auto& [j, jj] : ps_.custom_symmetric_variables ) + { + break_symmetric_vars( j, jj ); + } + } + + // ensure to use essential variables and gates + if ( ps_.ensure_to_use_gates ) + { + const auto num_ands = ltfi_vars_.size() / 2; + for ( auto j = 0u; j < num_vars_ + num_ands; ++j ) + { + if ( j < num_vars_ && !kitty::has_var( func_, j ) ) + { + continue; + } + + std::vector> clause; + for ( auto const& ltfi : ltfi_vars_ ) + { + if ( j < ltfi.size() ) + { + clause.push_back( ltfi[j] ); + } + } + pntk.add_clause( clause ); + } + } + } + + void add_xor_counter( problem_network_t& pntk ) + { + xor_counter_.clear(); + for ( auto const& ltfi : ltfi_vars_ ) + { + std::copy( ltfi.begin(), ltfi.end(), std::back_inserter( xor_counter_ ) ); + } + + insertion_sorting_network( static_cast( xor_counter_.size() ), [&]( auto a, auto b ) { + auto const aa = pntk.create_and( xor_counter_[a], xor_counter_[b] ); + auto const bb = pntk.create_or( xor_counter_[a], xor_counter_[b] ); + xor_counter_[a] = aa; + xor_counter_[b] = bb; + } ); + } + +private: + uint32_t count_xors( problem_network_t& pntk ) const + { + uint32_t ctr{}; + for ( auto const& ltfi : ltfi_vars_ ) + { + for ( auto const& l : ltfi ) + { + ctr += pntk.model_value( l ) ? 1u : 0u; + } + } + return ctr; + } + + void debug_solution( problem_network_t& pntk ) const + { + const auto num_ands = ltfi_vars_.size() / 2u; + const auto print_ltfi = [&]( std::vector> const& ltfi ) { + for ( auto const& f : ltfi ) + { + fmt::print( "{} ", (uint32_t)pntk.model_value( f ) ); + } + if ( auto padding = 2u * ( num_ands + num_vars_ - ltfi.size() ); padding > 0 ) + { + fmt::print( "{}", std::string( padding, ' ' ) ); + } + }; + + for ( auto i = 0u; i < ltfi_vars_.size() / 2u; ++i ) + { + fmt::print( "{:>2} = ", i + 1 ); + print_ltfi( ltfi_vars_[2 * i] ); + fmt::print( " " ); + print_ltfi( ltfi_vars_[2 * i + 1] ); + fmt::print( "\n" ); + } + fmt::print( " f = " ); + print_ltfi( ltfi_vars_.back() ); + fmt::print( "\n XORs = {}\n\n", count_xors( pntk ) ); + } + +private: + uint32_t num_vars_; + std::vector>> ltfi_vars_; + std::vector>> truth_vars_; + std::vector> xor_counter_; + kitty::dynamic_truth_table func_; + bool invert_{ false }; + std::optional heuristic_xor_bound_; + uint32_t num_solutions_; + exact_mc_synthesis_params const& ps_; + exact_mc_synthesis_stats& st_; +}; + +} // namespace detail + +template +Ntk exact_mc_synthesis( kitty::dynamic_truth_table const& func, exact_mc_synthesis_params const& ps = {}, exact_mc_synthesis_stats* pst = nullptr ) +{ + exact_mc_synthesis_stats st; + const auto xag = detail::exact_mc_synthesis_impl{ func, 1u, ps, st }.run().front(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return xag; +} + +template +std::vector exact_mc_synthesis_multiple( kitty::dynamic_truth_table const& func, uint32_t num_solutions, exact_mc_synthesis_params const& ps = {}, exact_mc_synthesis_stats* pst = nullptr ) +{ + exact_mc_synthesis_stats st; + const auto xags = detail::exact_mc_synthesis_impl{ func, num_solutions, ps, st }.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + + return xags; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/exorcism.hpp b/include/mockturtle/algorithms/exorcism.hpp new file mode 100644 index 0000000..f91cbde --- /dev/null +++ b/include/mockturtle/algorithms/exorcism.hpp @@ -0,0 +1,82 @@ +/* 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 exorcism.hpp + \brief Wrapper for ABC's exorcism + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace abc::exorcism +{ +extern int Abc_ExorcismMain( Vec_Wec_t* vEsop, int nIns, int nOuts, std::function const& onCube, int Quality, int Verbosity, int nCubesMax, int fUseQCost ); +} + +namespace mockturtle +{ + +inline std::vector exorcism( std::vector const& esop, uint32_t num_vars ) +{ + auto vesop = abc::exorcism::Vec_WecAlloc( esop.size() ); + + for ( auto const& cube : esop ) + { + auto vcube = abc::exorcism::Vec_WecPushLevel( vesop ); + for ( auto i = 0u; i < num_vars; ++i ) + { + if ( !cube.get_mask( i ) ) + continue; + abc::exorcism::Vec_IntPush( vcube, cube.get_bit( i ) ? 2 * i : 2 * i + 1 ); + } + abc::exorcism::Vec_IntPush( vcube, -1 ); + } + + std::vector exorcism_esop; + abc::exorcism::Abc_ExorcismMain( + vesop, num_vars, 1, [&]( uint32_t bits, uint32_t mask ) { exorcism_esop.emplace_back( bits, mask ); }, 2, 0, 4 * esop.size(), 0 ); + + abc::exorcism::Vec_WecFree( vesop ); + + return exorcism_esop; +} + +inline std::vector exorcism( kitty::dynamic_truth_table const& func ) +{ + return exorcism( kitty::esop_from_optimum_pkrm( func ), func.num_vars() ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/experimental/boolean_optimization.hpp b/include/mockturtle/algorithms/experimental/boolean_optimization.hpp new file mode 100644 index 0000000..3fde2eb --- /dev/null +++ b/include/mockturtle/algorithms/experimental/boolean_optimization.hpp @@ -0,0 +1,356 @@ +/* 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 boolean_optimization.hpp + \brief A general logic optimization framework using Boolean methods + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../traits.hpp" +#include "../../utils/progress_bar.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../utils/null_utils.hpp" +#include "../../views/topo_view.hpp" + +#include + +namespace mockturtle::experimental +{ + +template +struct boolean_optimization_params +{ + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Whether to use new nodes as pivots. */ + bool optimize_new_nodes{ false }; + + /*! \brief Whether to run in dry-run mode (call `report` instead of `update_ntk`). */ + bool dry_run{ false }; + + /*! \brief Whether to print verbosely in dry-run mode. Ignored if `dry_run` is `false`. */ + bool dry_run_verbose{ true }; + + /*! \brief Parameter object for the windowing engine. */ + WinParams wps; + + /*! \brief Parameter object for the resynthesis engine. */ + ResynParams rps; +}; + +template +struct boolean_optimization_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime of structural analysis and simulation. */ + stopwatch<>::duration time_windowing{ 0 }; + + /*! \brief Accumulated runtime of resynthesis. */ + stopwatch<>::duration time_resynthesis{ 0 }; + + /*! \brief Accumulated runtime of updating network. */ + stopwatch<>::duration time_update{ 0 }; + + /*! \brief Total number of gain. */ + uint32_t estimated_gain{ 0 }; + + /*! \brief Initial network size (before resubstitution). */ + uint32_t initial_size{ 0 }; + + /*! \brief Number of constructed resynthesis problems. */ + uint32_t num_problems{ 0u }; + + /*! \brief Number of solutions found. */ + uint32_t num_solutions{ 0u }; + + /*! \brief Statistics object for the windowing engine. */ + WinStats wst; + + /*! \brief Statistics object for the resynthesis engine. */ + ResynStats rst; + + void report() const + { + // clang-format off + fmt::print( "[i] Boolean optimization top-level report\n" ); + fmt::print( "Estimated gain: {:8d} ({:.2f}%)\n", estimated_gain, ( 100.0 * estimated_gain ) / initial_size ); + fmt::print( "#problems = {}, #solutions = {} ({:.2f}%)\n", num_problems, num_solutions, float( num_solutions ) / float( num_problems ) ); + fmt::print( "======== Runtime Breakdown ========\n" ); + fmt::print( "Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Windowing : {:>5.2f} secs\n", to_seconds( time_windowing ) ); + fmt::print( " Resynthesis : {:>5.2f} secs\n", to_seconds( time_resynthesis ) ); + fmt::print( " Update ntk : {:>5.2f} secs\n", to_seconds( time_update ) ); + fmt::print( "========= Windowing Stats =========\n" ); + wst.report(); + fmt::print( "======== Resynthesis Stats ========\n" ); + rst.report(); + fmt::print( "===================================\n\n" ); + // clang-format on + } +}; + +namespace detail +{ + +/*! \brief Logic optimization using Boolean methods. + * + * \tparam Ntk Network type. + * \tparam Windowing Implementation of a windowing algorithm that creates + * a resynthesis problem to be solved. + * \tparam ResynSolver Implementation of a resynthesis algorithm that + * solves the resynthesis problem created by `Windowing`. + */ +template +class boolean_optimization_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using problem_t = typename Windowing::problem_t; + using res_t = typename ResynSolver::res_t; + using params_t = boolean_optimization_params; + using stats_t = boolean_optimization_stats; + + explicit boolean_optimization_impl( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), windowing( ntk, ps.wps, st.wst ), resyn( ntk, ps.rps, st.rst ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( std::is_same_v, "Types of resynthesis problem of Windowing and ResynSolver do not match" ); + } + + ~boolean_optimization_impl() + {} + + void run() + { + stopwatch t( st.time_total ); + progress_bar pbar{ ntk.size(), "B-opt |{0}| node = {1:>4} cand = {2:>4} est. gain = {3:>5}", ps.progress }; + + /* initialize */ + call_with_stopwatch( st.time_windowing, [&]() { + windowing.init(); + } ); + call_with_stopwatch( st.time_resynthesis, [&]() { + resyn.init(); + } ); + + st.initial_size = ntk.num_gates(); + topo_view{ ntk }.foreach_gate( [&]( auto const n, auto i ) { // TODO: maybe problematic + if ( !ps.optimize_new_nodes && i >= st.initial_size ) + { + return false; /* terminate */ + } + pbar( i, i, candidates, st.estimated_gain ); + + /* construct a resynthesis problem; usually by creating a window around the root node */ + auto prob = call_with_stopwatch( st.time_windowing, [&]() { + return windowing( n ); + } ); + if ( !prob ) + { + return true; /* next */ + } + ++st.num_problems; + + /* solve the resynthesis problem; usually by finding a (re)substitution */ + auto res = call_with_stopwatch( st.time_resynthesis, [&]() { + return resyn( *prob ); + } ); + if ( !res ) + { + return true; /* next */ + } + ++st.num_solutions; + + /* update progress bar */ + candidates++; + st.estimated_gain += windowing.gain( *prob, *res ); + + /* update network or report choice */ + bool cont = true; + if ( !ps.dry_run ) + { + cont = call_with_stopwatch( st.time_update, [&]() { + return windowing.update_ntk( *prob, *res ); + } ); + } + else if ( ps.dry_run_verbose ) + { + cont = windowing.report( *prob, *res ); + } + + return cont; + } ); + } + +private: + Ntk& ntk; + + params_t const& ps; + stats_t& st; + + Windowing windowing; + ResynSolver resyn; + + /* temporary statistics for progress bar */ + uint32_t candidates{ 0 }; +}; /* boolean_optimization_impl */ + +template +struct null_problem +{ + using node = typename Ntk::node; + node pivot; +}; + +/*! \brief A windowing implementation that creates windows of only the pivot node. + * + * This class is an example to demonstrate the interfaces required by + * the `Windowing` template argument of class `boolean_optimization_impl`. + * It is designed to be used together with `null_resynthesis`. + */ +template +class null_windowing +{ +public: + using problem_t = null_problem; + using res_t = typename Ntk::signal; + using params_t = null_params; + using stats_t = null_stats; + using node = typename Ntk::node; + + explicit null_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ) + { + (void)ps; + (void)st; + } + + void init() + {} + + std::optional operator()( node const& n ) + { + return problem_t{ n }; + } + + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + (void)prob; + (void)res; + return 0u; + } + + bool update_ntk( problem_t const& prob, res_t const& res ) + { + ntk.substitute_node( prob.pivot, res ); + return true; + } + + bool report( problem_t const& prob, res_t const& res ) + { + fmt::print( "[i] substitute node {} with signal {}{}\n", prob.pivot, ntk.is_complemented( res ) ? "!" : "", ntk.get_node( res ) ); + return true; + } + +private: + Ntk& ntk; +}; + +/*! \brief A resynthesis implementation that returns the pivot node itself. + * + * This class is an example to demonstrate the interfaces required by + * the `ResynSolver` template argument of class `boolean_optimization_impl`. + * It is designed to be used together with `null_windowing`. + */ +template +class null_resynthesis +{ +public: + using problem_t = null_problem; + using res_t = typename Ntk::signal; + using params_t = null_params; + using stats_t = null_stats; + + explicit null_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ) + { + (void)ps; + (void)st; + } + + void init() + {} + + std::optional operator()( problem_t& prob ) + { + return ntk.make_signal( prob.pivot ); + } + +private: + Ntk const& ntk; +}; + +} /* namespace detail */ + +template, typename stats_t = boolean_optimization_stats> +void null_optimization( Ntk& ntk, params_t const& ps = {}, stats_t* pst = nullptr ) +{ + stats_t st; + + using windowing_t = typename detail::null_windowing; + using resyn_t = typename detail::null_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( ntk, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp b/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp new file mode 100644 index 0000000..a5ea472 --- /dev/null +++ b/include/mockturtle/algorithms/experimental/cost_generic_resub.hpp @@ -0,0 +1,426 @@ +/* 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 cost_generic_resub.hpp + \brief generic widnowing algorithm with customized cost function + + \author Hanyu Wang +*/ + +#pragma once + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/cost_view.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../../views/topo_view.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../reconv_cut.hpp" +#include "../simulation.hpp" +#include "boolean_optimization.hpp" +#include "cost_resyn.hpp" +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +/*! \brief Parameters for cost. + */ +struct costfn_windowing_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to normalize the truth tables. + * + * For some enumerative resynthesis engines, if the truth tables + * are normalized, some cases can be eliminated and thus improves + * efficiency. When this option is turned off, be sure to use an + * implementation of resynthesis that does not make this assumption; + * otherwise, quality degradation may be observed. + * + * Normalization is typically only useful for enumerative methods + * and for smaller solutions (i.e. when `max_inserts` < 2). Turning + * on normalization may result in larger runtime overhead when there + * are many divisors or when the truth tables are long. + */ + bool normalize{ false }; +}; + +struct costfn_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Accumulated runtime for don't care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Total number of leaves. */ + uint64_t num_leaves{ 0u }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] costfn_windowing report\n" ); + fmt::print( " tot. #leaves = {:5d}, tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_leaves, num_divisors, sum_mffc_size ); + fmt::print( " avg. #leaves = {:>5.2f}, avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_leaves ) / float( num_windows ), float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( " MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs : {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( " Simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " Dont cares: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + // clang-format on + } +}; + +namespace detail +{ +template +struct cost_aware_problem +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + signal root; + std::vector divs; + std::vector div_ids; /* positions of divisor truth tables in `tts` */ + std::vector div_id_to_node; /* maps IDs in `div_ids` to the corresponding node */ + std::vector tts; + TT care; + uint32_t mffc_size; + uint32_t max_cost{ std::numeric_limits::max() }; +}; + +template +class costfn_windowing +{ +public: + using problem_t = cost_aware_problem; + using params_t = costfn_windowing_params; + using stats_t = costfn_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit costfn_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), cps( { ps.max_pis } ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_divisors, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ), + sim( ntk, win.tts, ps.max_pis ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + } + + void init() + { + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + /* compute a cut and collect supported nodes */ + std::vector leaves = call_with_stopwatch( st.time_cuts, [&]() { + return reconvergence_driven_cut>( ntk, { n }, cps ).first; + } ); + std::vector supported; + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_supported_nodes( n, leaves, supported ); + } ); + + /* simulate */ + call_with_stopwatch( st.time_sim, [&]() { + sim.simulate( leaves, supported ); + } ); + + /* mark MFFC nodes and collect divisors */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, leaves, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + call_with_stopwatch( st.time_divs, [&]() { + collect_divisors( leaves, supported ); + } ); + + /* normalize */ + call_with_stopwatch( st.time_sim, [&]() { + if ( ps.normalize ) + { + win.root = normalize_truth_tables() ? !ntk.make_signal( n ) : ntk.make_signal( n ); + } + else + { + win.root = ntk.make_signal( n ); + } + } ); + + /* compute don't cares */ + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + win.care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + win.care = ~kitty::create( ps.max_pis ); + } + } ); + + /* compute cost */ + win.max_cost = ntk.get_cost( n, win.divs ); + + st.num_windows++; + st.num_leaves += leaves.size(); + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return 1; /* cannot predict the final cost */ + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, std::begin( prob.divs ), std::end( prob.divs ), res, [&]( signal const& g ) { + ntk.substitute_node( ntk.get_node( prob.root ), ntk.is_complemented( prob.root ) ? !g : g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root signal {}{}\n", to_index_list_string( res ), ntk.is_complemented( prob.root ) ? "!" : "", ntk.get_node( prob.root ) ); + return true; + } + +private: + void collect_divisors( std::vector const& leaves, std::vector const& supported ) + { + win.divs.clear(); + win.div_ids.clear(); + + uint32_t i{ 1 }; + for ( auto const& l : leaves ) + { + win.div_ids.emplace_back( i++ ); + win.divs.emplace_back( ntk.make_signal( l ) ); + } + + i = ps.max_pis + 1; + for ( auto const& n : supported ) + { + if ( ntk.value( n ) != mffc_marker ) /* not in MFFC, not root */ + { + win.div_ids.emplace_back( i ); + win.divs.emplace_back( ntk.make_signal( n ) ); + } + ++i; + } + assert( i == win.tts.size() ); + } + + bool normalize_truth_tables() + { + assert( win.divs.size() == win.div_ids.size() ); + for ( auto i = 0u; i < win.divs.size(); ++i ) + { + if ( kitty::get_bit( win.tts.at( win.div_ids.at( i ) ), 0 ) ) + { + win.tts.at( win.div_ids.at( i ) ) = ~win.tts.at( win.div_ids.at( i ) ); + win.divs.at( i ) = !win.divs.at( i ); + } + } + + if ( kitty::get_bit( win.tts.back(), 0 ) ) + { + win.tts.back() = ~win.tts.back(); + return true; + } + else + { + return false; + } + } + +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + reconvergence_driven_cut_parameters const cps; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + divisor_collector divs_mgr; + window_simulator sim; + uint32_t mffc_marker{ 0u }; + std::shared_ptr::modified_event_type> lazy_update_event; +}; /* costfn_windowing */ + +template +class costfn_resynthesis +{ +public: + using problem_t = cost_aware_problem; + using res_t = typename ResynEngine::index_list_t; + using params_t = typename ResynEngine::params; + using stats_t = typename ResynEngine::stats; + + explicit costfn_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), engine( ntk, ps, st ) + { + static_assert( has_cost_v, "Ntk does not implement the get_cost method" ); + } + + void init() + { + } + + std::optional operator()( problem_t& prob ) + { + return engine( prob.tts.back(), prob.care, prob.divs, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_cost ); + } + +private: + Ntk const& ntk; + typename ResynEngine::stats rst; + ResynEngine engine; +}; /* costfn_resynthesis */ + +} /* namespace detail */ + +using cost_generic_resub_params = boolean_optimization_params; +using cost_generic_resub_stats = boolean_optimization_stats; + +/*! \brief Cost-generic resubstitution algorithm. + * + * This algorithm creates a reconvergence-driven window for each node in the + * network, collects divisors, and builds the resynthesis problem. A search core + * then collects all the resubstitution candidates with the same functionality as + * the target. The candidate with the lowest cost will then replace the MFFC + * of the window. + * + * \param ntk Network + * \param cost_fn Customized cost function + * \param ps Optimization params + * \param pst Optimization statistics + */ +template +void cost_generic_resub( Ntk& ntk, CostFn cost_fn, cost_generic_resub_params const& ps, cost_generic_resub_stats* pst = nullptr ) +{ + fanout_view fntk( ntk ); + cost_view viewed( fntk, cost_fn ); + using Viewed = decltype( viewed ); + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::costfn_windowing; + using engine_t = cost_resyn; + using resyn_t = typename detail::costfn_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + cost_generic_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle::experimental \ No newline at end of file diff --git a/include/mockturtle/algorithms/experimental/cost_resyn.hpp b/include/mockturtle/algorithms/experimental/cost_resyn.hpp new file mode 100644 index 0000000..54035f1 --- /dev/null +++ b/include/mockturtle/algorithms/experimental/cost_resyn.hpp @@ -0,0 +1,1289 @@ +/* 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 cost_resyn.hpp + \brief Solver of cost-aware resynthesis problem. + Given a resynthesis problem and the cost function, returns + the solution with (1) correct functionality (2) lower cost. + + This solver is cost-generic. + + \author Hanyu Wang +*/ + +#pragma once + +#include "../../algorithms/cleanup.hpp" +#include "../../utils/index_list.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct cost_resyn_params +{ + /* maximum number of feasible solutions to collect */ + uint32_t max_solutions{ 1000u }; +}; + +struct cost_resyn_stats +{ + /* time for cost view the solution network */ + stopwatch<>::duration time_eval{ 0 }; + + /* time for searching the equivalent network */ + stopwatch<>::duration time_search{ 0 }; + + /* number of solutions */ + uint32_t num_solutions{ 0 }; + + /* number of problems */ + uint32_t num_problems{ 0 }; + + /* number of solution with 0,1,2,3 insertions */ + uint32_t num_resub[4]{ 0 }; + + /* size of the forest of feasible solutions */ + uint32_t size_forest{ 0 }; + + /* number of root (feasible solutions) */ + uint32_t num_roots{ 0 }; + + /* number of total gains */ + uint32_t num_gain{ 0 }; + + /* data */ + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] Evaluation : {:>5.2f} secs\n", to_seconds( time_eval ) ); + fmt::print( "[i] Searching : {:>5.2f} secs\n", to_seconds( time_search ) ); + fmt::print( "[i] # Problem : {}\n", num_problems ); + fmt::print( "[i] Avg. forest size : {:>5.2f}\n", (float)size_forest / num_problems ); + fmt::print( "[i] Avg. num solution: {:>5.2f}\n", (float)num_roots / num_problems ); + fmt::print( "[i] Opt. ratio : {:>5.2f}%\n", (float)num_solutions / num_problems * 100 ); + fmt::print( "[i] 0 - resub : {:>5.2f}\n", (float)num_resub[0] / num_problems ); + fmt::print( "[i] 1 - resub : {:>5.2f}\n", (float)num_resub[1] / num_problems ); + fmt::print( "[i] 2 - resub : {:>5.2f}\n", (float)num_resub[2] / num_problems ); + fmt::print( "[i] 3 - resub : {:>5.2f}\n", (float)num_resub[3] / num_problems ); + fmt::print( "[i] Gain : {:>5.2f}\n", (float)num_gain / num_problems ); + } +}; + +template +class cost_resyn +{ +public: + using params = cost_resyn_params; + using stats = cost_resyn_stats; + using signal = typename Ntk::signal; + using node = typename Ntk::node; + using context_t = typename Ntk::context_t; + using index_list_t = large_xag_index_list; + using truth_table_t = TT; + +private: + struct unate_lit + { + unate_lit( uint32_t l ) + : lit( l ) + { + } + + bool operator==( unate_lit const& other ) const + { + return lit == other.lit; + } + + uint32_t lit; + uint32_t score{ 0 }; + }; + struct fanin_pair + { + fanin_pair( uint32_t l1, uint32_t l2 ) + : lit1( l1 < l2 ? l1 : l2 ), lit2( l1 < l2 ? l2 : l1 ) + { + } + + fanin_pair( uint32_t l1, uint32_t l2, bool is_xor ) + : lit1( l1 > l2 ? l1 : l2 ), lit2( l1 > l2 ? l2 : l1 ) + { + (void)is_xor; + } + + bool operator==( fanin_pair const& other ) const + { + return lit1 == other.lit1 && lit2 == other.lit2; + } + + uint32_t lit1, lit2; + uint32_t score{ 0 }; + }; + + inline TT const& get_div( uint32_t idx ) const + { + return ( *ptts )[divisors[idx]]; + } + + uint32_t eval_result( Ntk& forest, index_list_t const& il ) + { + uint32_t eval = 0u; + // insert il to forest, this might not be applicable to cost related to fanout size! + insert( forest, std::begin( forest_leaves ), std::end( forest_leaves ), il, [&]( signal const& g ) { + forest.incr_trav_id(); + eval = forest.get_cost( forest.get_node( g ), forest_leaves ); + } ); + return eval; // the cost of the whole network + } + + bool update_result( Ntk& forest, index_list_t const& il ) + { + st.num_roots += 1u; + uint32_t curr_cost = eval_result( forest, il ); + if ( curr_cost < best_cost ) + { + best_cost = curr_cost; + index_list = il; + return true; + } + return false; + } + + bool push_solution( index_list_t const& il ) + { + ils.emplace_back( il ); /* push the solution to the solution set */ + return ils.size() < ps.max_solutions; /* continue if capacity allows */ + } + + template + void collect_unate_pairs_detail( uint32_t div1, uint32_t div2 ) + { + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + else if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + } + + /* Sort the unate literals by the number of minterms in the intersection. + - For `pos_unate_lits`, `on_off` = 1, sort by intersection with on-set; + - For `neg_unate_lits`, `on_off` = 0, sort by intersection with off-set + */ + void sort_unate_lits( std::vector& pos_unate_lits, uint32_t on_off ) + { + for ( auto& l : pos_unate_lits ) + { + l.score = kitty::count_ones( ( l.lit & 0x1 ? ~get_div( l.lit >> 1 ) : get_div( l.lit >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( pos_unate_lits.begin(), pos_unate_lits.end(), [&]( unate_lit const& l1, unate_lit const& l2 ) { + return l1.score > l2.score; // descending order + } ); + } + + void sort_unate_pairs( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto& p : unate_pairs ) + { + p.score = ( p.lit1 > p.lit2 ) ? kitty::count_ones( ( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) ^ ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) ) & on_off_sets[on_off] ) + : kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( unate_pairs.begin(), unate_pairs.end(), [&]( fanin_pair const& p1, fanin_pair const& p2 ) { + return p1.score > p2.score; // descending order + } ); + } + + std::optional find_and_detail( std::vector const& pos_unate_lits, uint32_t on_off ) + { + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + uint32_t const& lit1 = pos_unate_lits[i].lit; + if ( pos_unate_lits[i].score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < pos_unate_lits.size(); ++j ) + { + uint32_t const& lit2 = pos_unate_lits[j].lit; + if ( pos_unate_lits[i].score + pos_unate_lits[j].score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? get_div( lit2 >> 1 ) : ~get_div( lit2 >> 1 ); + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + il.add_output( new_lit + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + template + std::optional find_and_and_helper( std::vector& pos_unate_lits, std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + uint32_t const& lit1 = pos_unate_lits[i].lit; + for ( auto j = 0u; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( pos_unate_lits[i].score + pair2.score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + TT ntt2; + if constexpr ( is_xor ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + uint32_t new_lit1; + if constexpr ( is_xor ) + { + new_lit1 = il.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + new_lit1 = il.add_and( pair2.lit1, pair2.lit2 ); + } + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ^ 0x1 ); + il.add_output( new_lit2 + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + template + std::optional find_and_and_and_helper( std::vector& unate_pairs_1, std::vector& unate_pairs_2, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_pairs_1.size(); ++i ) + { + fanin_pair const& pair1 = unate_pairs_1[i]; + if ( pair1.score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_pairs_2.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs_2[j]; + if ( pair1.score + pair2.score < num_bits[on_off] ) + { + break; + } + TT ntt1, ntt2; + if constexpr ( left_xor ) + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) ^ ( pair1.lit2 & 0x1 ? ~get_div( pair1.lit2 >> 1 ) : get_div( pair1.lit2 >> 1 ) ); + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + } + if constexpr ( right_xor ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + uint32_t fanin_lit1, fanin_lit2; + if constexpr ( left_xor ) + { + fanin_lit1 = il.add_xor( pair1.lit1, pair1.lit2 ); + } + else + { + fanin_lit1 = il.add_and( pair1.lit1, pair1.lit2 ); + } + if constexpr ( right_xor ) + { + fanin_lit2 = il.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + fanin_lit2 = il.add_and( pair2.lit1, pair2.lit2 ); + } + uint32_t const output_lit = il.add_and( fanin_lit1 ^ 0x1, fanin_lit2 ^ 0x1 ); + il.add_output( output_lit + on_off ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + void prepare_clear() + { + pos_unate_lits.clear(); + neg_unate_lits.clear(); + binate_divs.clear(); + pos_unate_pairs.clear(); + neg_unate_pairs.clear(); + pos_unate_xor_pairs.clear(); + neg_unate_xor_pairs.clear(); + mem_xor.clear(); + mem_xor_xor.clear(); + mem_xor_and.clear(); + has_xor_xor = false; + has_xor = false; + has_xor_and = false; + has_unateness = false; + has_and_pairs = false; + has_xor_pairs = false; + has_lit_xor = false; + has_init = false; + + index_list = std::nullopt; + forest_leaves.clear(); + candidates.clear(); + forest_root = std::nullopt; + div_costs.clear(); + isConst = false; + + ils.clear(); + } + + void prepare_task() + { + assert( has_init == false ); + num_bits[0] = kitty::count_ones( on_off_sets[0] ); /* off-set */ + num_bits[1] = kitty::count_ones( on_off_sets[1] ); /* on-set */ + has_init = true; + } + + void prepare_unateness() + { + assert( has_unateness == false && "already have unateness" ); + if ( has_init == false ) + { + prepare_task(); + } + for ( auto v = 1u; v < divisors.size(); ++v ) + { + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 ); + unateness[0] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( ( v << 1 ) | 0x1 ); + unateness[1] = true; + } + /* check intersection with on-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 ); + unateness[2] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( ( v << 1 ) | 0x1 ); + unateness[3] = true; + } + /* useless unate literal */ + if ( ( unateness[0] && unateness[2] ) || ( unateness[1] && unateness[3] ) ) + { + pos_unate_lits.pop_back(); + neg_unate_lits.pop_back(); + } + /* binate divisor */ + else if ( !unateness[0] && !unateness[1] && !unateness[2] && !unateness[3] ) + { + binate_divs.emplace_back( v ); + } + } + sort_unate_lits( pos_unate_lits, 1 ); + sort_unate_lits( neg_unate_lits, 0 ); + has_unateness = true; + } + + void prepare_xor() + { + assert( has_xor == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + mem_xor[on_off_sets[1] ^ get_div( i )] = i; + } + has_xor = true; + } + + void prepare_xor_xor() + { + assert( has_xor_xor == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + mem_xor_xor[get_div( i ) ^ get_div( j ) ^ on_off_sets[1]] = i * divisors.size() + j; + } + } + has_xor_xor = true; + } + + void prepare_xor_and() + { + assert( has_xor_and == false ); + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + mem_xor_and[tt ^ on_off_sets[1]] = ( ( i << 1 ) + on_off_1 ) * 2 * divisors.size() + ( ( j << 1 ) + on_off_2 ); + } + } + } + } + has_xor_and = true; + } + + void prepare_and_pairs() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + collect_unate_pairs_detail<1, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<1, 0>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 0>( binate_divs[i], binate_divs[j] ); + } + }; + sort_unate_pairs( pos_unate_pairs, 1 ); + sort_unate_pairs( neg_unate_pairs, 0 ); + has_and_pairs = true; + } + + void prepare_xor_pairs() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + auto const tt_xor = get_div( binate_divs[i] ) ^ get_div( binate_divs[j] ); + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_xor_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_xor_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_xor_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_xor_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + } + } + } + sort_unate_pairs( pos_unate_xor_pairs, 1 ); + sort_unate_pairs( neg_unate_xor_pairs, 0 ); + has_xor_pairs = true; + } + + std::optional find_wire() + { + if ( has_init == false ) + { + prepare_task(); + } + if ( num_bits[0] == 0 ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + isConst = true; + } + if ( num_bits[1] == 0 ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( 0 ); + if ( !push_solution( il ) ) + return std::nullopt; + isConst = true; + } + for ( auto v = 1u; v < divisors.size(); ++v ) + { + if ( get_div( v ) == on_off_sets[1] ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( v << 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( get_div( v ) == on_off_sets[0] ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( ( v << 1 ) + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + return std::nullopt; + } + + std::optional find_and() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + return find_and_detail( neg_unate_lits, 0 ); + } + + std::optional find_or() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + return find_and_detail( pos_unate_lits, 1 ); + } + + std::optional find_xor() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); ++i ) + { + auto const tt = get_div( i ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( il.add_xor( ( i << 1 ), mem_xor[tt] << 1 ) ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + il.add_output( il.add_xor( ( i << 1 ) + 1, mem_xor[~tt] << 1 ) ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + return std::nullopt; + } + + std::optional find_or_and() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + return find_and_and_helper( pos_unate_lits, pos_unate_pairs, 1 ); + } + + std::optional find_and_or() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + return find_and_and_helper( neg_unate_lits, neg_unate_pairs, 0 ); + } + + std::optional find_and_and() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < pos_unate_lits.size(); ++i ) + { + if ( pos_unate_lits[i].score * 3 < num_bits[1] ) + { + break; + } + uint32_t const& lit1 = pos_unate_lits[i].lit; + for ( auto j = i + 1u; j < pos_unate_lits.size(); ++j ) + { + if ( pos_unate_lits[i].score + pos_unate_lits[j].score * 2 < num_bits[1] ) + { + break; + } + uint32_t const& lit2 = pos_unate_lits[j].lit; + auto const ntt1 = lit1 & 0x1 ? ~get_div( lit1 >> 1 ) : get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? ~get_div( lit2 >> 1 ) : get_div( lit2 >> 1 ); + TT const tt = ntt1 | ntt2; + for ( auto k = j + 1u; k < pos_unate_lits.size(); ++k ) + { + uint32_t const& lit3 = pos_unate_lits[k].lit; + if ( pos_unate_lits[i].score + pos_unate_lits[j].score + pos_unate_lits[k].score < num_bits[1] ) + { + break; + } + auto const ntt3 = lit3 & 0x1 ? ~get_div( lit3 >> 1 ) : get_div( lit3 >> 1 ); + if ( ( tt | ntt3 ) == on_off_sets[1] ) + { + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit3 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit2 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit2 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 + 1 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_or_or() + { + if ( has_unateness == false ) + { + prepare_unateness(); + } + for ( auto i = 0u; i < neg_unate_lits.size(); ++i ) + { + if ( neg_unate_lits[i].score * 3 < num_bits[0] ) + { + break; + } + uint32_t const& lit1 = neg_unate_lits[i].lit; + for ( auto j = i + 1u; j < neg_unate_lits.size(); ++j ) + { + if ( neg_unate_lits[i].score + neg_unate_lits[j].score * 2 < num_bits[0] ) + { + break; + } + uint32_t const& lit2 = neg_unate_lits[j].lit; + auto const ntt1 = lit1 & 0x1 ? ~get_div( lit1 >> 1 ) : get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? ~get_div( lit2 >> 1 ) : get_div( lit2 >> 1 ); + TT const tt = ntt1 | ntt2; + for ( auto k = j + 1u; k < neg_unate_lits.size(); ++k ) + { + uint32_t const& lit3 = neg_unate_lits[k].lit; + if ( neg_unate_lits[i].score + neg_unate_lits[j].score + neg_unate_lits[k].score < num_bits[0] ) + { + break; + } + + auto const ntt3 = lit3 & 0x1 ? ~get_div( lit3 >> 1 ) : get_div( lit3 >> 1 ); + if ( ( tt | ntt3 ) == on_off_sets[0] ) + { + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit3 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit1 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit2 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto const new_lit1 = il.add_and( ( lit2 ^ 0x1 ), ( lit3 ^ 0x1 ) ); + auto const new_lit2 = il.add_and( ( lit1 ^ 0x1 ), new_lit1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_and_xor() + { + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_helper( pos_unate_lits, pos_unate_xor_pairs, 1 ); + if ( !ret ) + ret = find_and_and_helper( neg_unate_lits, neg_unate_xor_pairs, 0 ); + return ret; + } + + std::optional find_xor_xor() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + auto const tt = get_div( i ) ^ get_div( j ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + if ( mem_xor[tt] == i ) + continue; + if ( mem_xor[tt] == j ) + continue; + index_list_t il; + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, mem_xor[tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( mem_xor[tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, j << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( j << 1 ), ( mem_xor[tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1, i << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + if ( mem_xor[~tt] == i ) + continue; + if ( mem_xor[~tt] == j ) + continue; + index_list_t il; + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, mem_xor[~tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( mem_xor[~tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, j << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + + // commutative + { + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( j << 1 ), ( mem_xor[~tt] << 1 ) ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, i << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_xor_xor() + { + if ( has_xor_xor == false ) + { + prepare_xor_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + auto const tt = get_div( i ) ^ get_div( j ); + if ( mem_xor_xor.find( tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[tt] % divisors.size() ) << 1, ( mem_xor_xor[tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_xor.find( ~tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_xor( ( i << 1 ), ( j << 1 ) ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[~tt] % divisors.size() ) << 1, ( mem_xor_xor[~tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2 ^ 0x1, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + return std::nullopt; + } + + std::optional find_xor_xor_and() + { + if ( has_xor_xor == false ) + { + prepare_xor_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor_xor.find( tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[tt] % divisors.size() ) << 1, ( mem_xor_xor[tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_xor.find( ~tt ) != mem_xor_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( ( mem_xor_xor[~tt] % divisors.size() ) << 1, ( mem_xor_xor[~tt] / divisors.size() ) << 1 ); + auto new_lit3 = il.add_xor( new_lit2 ^ 0x1, new_lit1 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_and() + { + if ( has_xor == false ) + { + prepare_xor(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor.find( tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( new_lit1, mem_xor[tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor.find( ~tt ) != mem_xor.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_xor( new_lit1 ^ 0x1, mem_xor[~tt] << 1 ); + il.add_output( new_lit2 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_xor_and_and() + { + if ( has_xor_and == false ) + { + prepare_xor_and(); + } + for ( auto i = 1u; i < divisors.size(); i++ ) + { + for ( auto j = i + 1; j < divisors.size(); j++ ) + { + for ( auto on_off_1 = 0u; on_off_1 < 2; on_off_1++ ) + { + for ( auto on_off_2 = 0u; on_off_2 < 2; on_off_2++ ) + { + auto const tt = ( on_off_1 ? ~get_div( i ) : get_div( i ) ) & ( on_off_2 ? ~get_div( j ) : get_div( j ) ); + if ( mem_xor_and.find( tt ) != mem_xor_and.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_and( mem_xor_and[tt] % ( 2 * divisors.size() ), mem_xor_and[tt] / ( 2 * divisors.size() ) ); + auto new_lit3 = il.add_xor( new_lit1, new_lit2 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + if ( mem_xor_and.find( ~tt ) != mem_xor_and.end() ) + { + index_list_t il; + il.clear(); + il.add_inputs( divisors.size() - 1 ); + auto new_lit1 = il.add_and( ( i << 1 ) + on_off_1, ( j << 1 ) + on_off_2 ); + auto new_lit2 = il.add_and( mem_xor_and[~tt] % ( 2 * divisors.size() ), mem_xor_and[~tt] / ( 2 * divisors.size() ) ); + auto new_lit3 = il.add_xor( new_lit1 ^ 0x1, new_lit2 ); + il.add_output( new_lit3 ); + if ( !push_solution( il ) ) + return std::nullopt; + } + } + } + } + } + return std::nullopt; + } + + std::optional find_and_and_and() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_pairs, pos_unate_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_pairs, neg_unate_pairs, 0 ); + return ret; + } + + std::optional find_and_and_xor() + { + if ( has_and_pairs == false ) + { + prepare_and_pairs(); + } + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_xor_pairs, pos_unate_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_xor_pairs, neg_unate_pairs, 0 ); + return ret; + } + + std::optional find_and_xor_xor() + { + if ( has_xor_pairs == false ) + { + prepare_xor_pairs(); + } + auto ret = find_and_and_and_helper( pos_unate_xor_pairs, pos_unate_xor_pairs, 1 ); + if ( !ret ) + ret = find_and_and_and_helper( neg_unate_xor_pairs, neg_unate_xor_pairs, 0 ); + return ret; + } + + struct core_func_t + { + std::function func; + uint32_t effort; + uint32_t score; + core_func_t( std::function func, uint32_t effort ) : func( func ), effort( effort ) + { + } + void operator()( cost_resyn* pcore ) + { + func( pcore ); + } + }; + + void sorted_core( Ntk& forest ) + { + for ( core_func_t& fn : fns ) + { + if ( ils.size() >= ps.max_solutions ) + break; + uint32_t nbefore = ils.size(); + call_with_stopwatch( st.time_search, [&]() { fn( this ); } ); + st.num_resub[fn.effort] += ils.size() - nbefore; + if ( isConst ) /* try to find more solution of constant will crash */ + break; + } + st.num_roots += ils.size(); + uint32_t ngain = 0u; + + for ( index_list_t const& il : ils ) + { + if ( best_cost > eval_result( forest, il ) ) + ngain = std::max( best_cost - eval_result( forest, il ), ngain ); + call_with_stopwatch( st.time_eval, [&]() { update_result( forest, il ); } ); + } + st.num_gain += ngain; + } + +public: + explicit cost_resyn( Ntk const& ntk, params const& ps, stats& st ) noexcept + : ntk( ntk ), ps( ps ), st( st ) + { + fns.clear(); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_wire(); }, 0 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or(); }, 1 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_or(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or_or(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_or_and(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_xor(); }, 2 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_and_and(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor_and(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_xor_xor_xor(); }, 3 ); // bad efficiency / gain trade-off + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_xor_xor(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and_xor(); }, 3 ); + fns.emplace_back( []( cost_resyn* _core ) { _core->find_and_and_and(); }, 3 ); + divisors.reserve( 200u ); + } + + template + std::optional operator()( TT const& target, TT const& care, std::vector const& divs, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_cost = std::numeric_limits::max() ) + { + ptts = &tts; + on_off_sets[0] = ~target & care; + on_off_sets[1] = target & care; + + divisors.resize( 1 ); /* clear previous data and reserve 1 dummy node for constant */ + while ( begin != end ) + { + divisors.emplace_back( *begin ); + ++begin; + } + + best_cost = max_cost; + prepare_clear(); + + // prepare solution forest + Ntk forest; // create an empty network + + for ( signal div : divs ) + { + signal const& s = forest.create_pi(); + node n = forest.get_node( s ); + forest_leaves.emplace_back( s ); + context_t div_cost = ntk.get_context( ntk.get_node( div ) ); + forest.set_context( n, div_cost ); + div_costs.emplace_back( div_cost ); + } + + sorted_core( forest ); + + st.num_problems += 1u; + if ( index_list ) + { + st.num_solutions += 1u; + st.size_forest += forest.num_gates(); + } + + return index_list; + } + +private: + std::array on_off_sets; + std::array num_bits; /* number of bits in on-set and off-set */ + + const std::vector* ptts; + std::vector divisors; + std::vector div_costs; + std::array tts_xors; + std::unordered_map> mem_xor; + std::unordered_map> mem_xor_xor; + std::unordered_map> mem_xor_and; + bool has_xor; + bool has_xor_xor; + bool has_xor_and; + bool has_unateness; + bool has_and_pairs; + bool has_xor_pairs; + bool has_init; + bool has_lit_xor; + /* positive unate: not overlapping with off-set + negative unate: not overlapping with on-set */ + std::vector pos_unate_lits, neg_unate_lits; + std::vector binate_divs; + std::vector pos_unate_pairs, neg_unate_pairs; + std::vector pos_unate_xor_pairs, neg_unate_xor_pairs; + + Ntk const& ntk; + std::vector forest_leaves; + std::vector candidates; // the output signals with correct functionality + std::optional forest_root; + params const& ps; + stats& st; + + std::optional index_list; + std::vector ils; + uint32_t best_cost; + + std::vector fns; + bool isConst; +}; + +} // namespace mockturtle::experimental diff --git a/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp b/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp new file mode 100644 index 0000000..f6385da --- /dev/null +++ b/include/mockturtle/algorithms/experimental/decompose_multioutput.hpp @@ -0,0 +1,397 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file decompose_multioutput.hpp + \brief Decomposes the multi-output gates into single output + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../../utils/node_map.hpp" +#include "../../views/topo_view.hpp" +#include "../cleanup.hpp" + +namespace mockturtle +{ + +struct decompose_multioutput_params +{ + bool set_multioutput_as_dont_touch{ false }; +}; + +namespace detail +{ + +template +void decompose_multioutput_impl( NtkSrc const& ntk, NtkDest& dest, LeavesIterator begin, LeavesIterator end, std::unordered_map>& old_to_new, decompose_multioutput_params const& ps ) +{ + /* constants */ + old_to_new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old_to_new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + /* create inputs in the same order */ + auto it = begin; + ntk.foreach_pi( [&]( auto node ) { + old_to_new[ntk.make_signal( node )] = *it++; + } ); + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto node ) { + old_to_new[ntk.make_signal( node )] = *it++; + } ); + } + assert( it == end ); + (void)end; + + /* foreach node in topological order */ + topo_view topo{ ntk }; + topo.foreach_node( [&]( auto node ) { + if ( ntk.is_constant( node ) || ntk.is_ci( node ) ) + return; + + /* collect children */ + std::vector> children; + ntk.foreach_fanin( node, [&]( auto child, auto ) { + const auto child_no_complement = child ^ ntk.is_complemented( child ); + const auto f = old_to_new[child_no_complement]; + + assert( dest.get_node( f ) != dest.get_node( dest.get_constant( false ) ) ); + assert( dest.get_node( f ) != dest.get_node( dest.get_constant( true ) ) ); + + children.push_back( f ^ ntk.is_complemented( child ) ); + } ); + + /* clone node */ + if ( ntk.is_multioutput( node ) ) + { + for ( auto i = 0; i < ntk.num_outputs( node ); ++i ) + { + auto f = ntk.make_signal( node, i ); + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( f ) ) + { + old_to_new[f] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( f ) ) + { + old_to_new[f] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( f ) ) + { + old_to_new[f] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( f ) ) + { + old_to_new[f] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( f ) ) + { + old_to_new[f] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( f ) ) + { + old_to_new[f] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_function_v && has_create_node_v ) + { + old_to_new[f] = dest.create_node( children, ntk.node_function_pin( node, i ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + + /* set dont touch */ + if constexpr ( has_select_dont_touch_v ) + { + if ( ps.set_multioutput_as_dont_touch ) + dest.select_dont_touch( dest.get_node( old_to_new[f] ) ); + } + + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( f ) ) + { + dest.set_name( old_to_new[f], ntk.get_name( f ) ); + } + if ( ntk.has_name( !f ) ) + { + dest.set_name( !old_to_new[f], ntk.get_name( !f ) ); + } + } + } + } + else + { + auto f = ntk.make_signal( node ); + if constexpr ( std::is_same_v ) + { + old_to_new[f] = dest.clone_node( ntk, node, children ); + } + else + { + do + { + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v, "NtkDest cannot create AND gates" ); + if ( ntk.is_and( node ) ) + { + old_to_new[f] = dest.create_and( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_or_v ) + { + static_assert( has_create_or_v, "NtkDest cannot create OR gates" ); + if ( ntk.is_or( node ) ) + { + old_to_new[f] = dest.create_or( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v, "NtkDest cannot create XOR gates" ); + if ( ntk.is_xor( node ) ) + { + old_to_new[f] = dest.create_xor( children[0], children[1] ); + break; + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v, "NtkDest cannot create MAJ gates" ); + if ( ntk.is_maj( node ) ) + { + old_to_new[f] = dest.create_maj( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_ite_v ) + { + static_assert( has_create_ite_v, "NtkDest cannot create ITE gates" ); + if ( ntk.is_ite( node ) ) + { + old_to_new[f] = dest.create_ite( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v, "NtkDest cannot create XOR3 gates" ); + if ( ntk.is_xor3( node ) ) + { + old_to_new[f] = dest.create_xor3( children[0], children[1], children[2] ); + break; + } + } + if constexpr ( has_is_nary_and_v ) + { + static_assert( has_create_nary_and_v, "NtkDest cannot create n-ary AND gates" ); + if ( ntk.is_nary_and( node ) ) + { + old_to_new[f] = dest.create_nary_and( children ); + break; + } + } + if constexpr ( has_is_nary_or_v ) + { + static_assert( has_create_nary_or_v, "NtkDest cannot create n-ary OR gates" ); + if ( ntk.is_nary_or( node ) ) + { + old_to_new[f] = dest.create_nary_or( children ); + break; + } + } + if constexpr ( has_is_nary_xor_v ) + { + static_assert( has_create_nary_xor_v, "NtkDest cannot create n-ary XOR gates" ); + if ( ntk.is_nary_xor( node ) ) + { + old_to_new[f] = dest.create_nary_xor( children ); + break; + } + } + if constexpr ( has_is_function_v && has_create_node_v ) + { + old_to_new[f] = dest.create_node( children, ntk.node_function( node ) ); + break; + } + std::cerr << "[e] something went wrong, could not copy node " << ntk.node_to_index( node ) << "\n"; + } while ( false ); + } + /* copy name */ + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( f ) ) + { + dest.set_name( old_to_new[f], ntk.get_name( f ) ); + } + if ( ntk.has_name( !f ) ) + { + dest.set_name( !old_to_new[f], ntk.get_name( !f ) ); + } + } + } + } ); + + /* POs */ + ntk.foreach_po( [&]( auto const& po ) { + const auto po_no_complement = po ^ ntk.is_complemented( po ); + auto const f = old_to_new[po_no_complement]; + dest.create_po( f ^ ntk.is_complemented( po ) ); + } ); + + /* RIs */ + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + dest.create_ri( old_to_new[f ^ ntk.is_complemented( f )] ^ ntk.is_complemented( f ) ); + } ); + } + + /* CO names */ + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + ntk.foreach_co( [&]( auto co, auto index ) { + (void)co; + if ( ntk.has_output_name( index ) ) + { + dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } ); + } +} + +} // namespace detail + +/*! \brief Decomposes the multi-output gates into single output. + * + * This method reconstructs a network decomposing the multi-output gates into + * single output gates. Moreover, it omits all dangling nodes. + * + \verbatim embed:rst + + .. note:: + + This method returns the cleaned up network as a return value. It does + *not* modify the input network. + \endverbatim + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_multioutput` + */ +template +[[nodiscard]] NtkDest decompose_multioutput( NtkSrc const& ntk, decompose_multioutput_params const& ps = {} ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_node_to_index_v, "NtkSrc does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "NtkSrc does not implement the is_pi method" ); + static_assert( has_is_constant_v, "NtkSrc does not implement the is_constant method" ); + static_assert( has_clone_node_v, "NtkDest does not implement the clone_node method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_is_complemented_v, "NtkSrc does not implement the is_complemented method" ); + static_assert( has_is_multioutput_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_node_function_pin_v, "NtkSource does not implement the node_function_pin" ); + static_assert( has_num_outputs_v, "NtkSource does not implement the has_num_outputs" ); + + NtkDest dest; + + std::vector> cis; + detail::clone_inputs( ntk, dest, cis, false ); + + std::unordered_map> old_to_new; + detail::decompose_multioutput_impl( ntk, dest, cis.begin(), cis.end(), old_to_new, ps ); + + return dest; +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/experimental/sim_resub.hpp b/include/mockturtle/algorithms/experimental/sim_resub.hpp new file mode 100644 index 0000000..cd86c54 --- /dev/null +++ b/include/mockturtle/algorithms/experimental/sim_resub.hpp @@ -0,0 +1,545 @@ +/* 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 sim_resub.hpp + \brief Simulation-guided resubstitution + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../io/write_patterns.hpp" +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../circuit_validator.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../pattern_generation.hpp" +#include "../resyn_engines/aig_enumerative.hpp" +#include "../resyn_engines/mig_enumerative.hpp" +#include "../resyn_engines/mig_resyn.hpp" +#include "../resyn_engines/xag_resyn.hpp" +#include "../simulation.hpp" +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct breadth_first_windowing_params +{ + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 50 }; + + /*! \brief Maximum number of TFI nodes to collect. */ + uint32_t max_tfi{ uint32_t( max_divisors * 0.5 ) }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ std::numeric_limits::max() }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; +}; + +struct simulation_guided_resynthesis_params +{ + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 1024 random pattern + 1x stuck-at patterns will be generated. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of clauses of the SAT solver. */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Random seed for the SAT solver (influences the randomness of counter-examples). */ + uint32_t random_seed{ 1 }; + + /*! \brief Maximum number of trials to call the resub functor. */ + uint32_t max_trials{ 100 }; + + /*! \brief Whether to utilize ODC, and how many levels. 0 = no. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; +}; + +struct breadth_first_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] breadth_first_windowing report\n" ); + fmt::print( " tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_divisors, sum_mffc_size ); + fmt::print( " avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " MFFC: {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs: {:>5.2f} secs\n", to_seconds( time_divs ) ); + // clang-format on + } +}; + +struct simulation_guided_resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for pattern generation. */ + stopwatch<>::duration time_patgen{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for computing ODCs. */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Time for saving patterns. */ + stopwatch<>::duration time_patsave{ 0 }; + + /*! \brief Number of calls to the resynthesis engine. */ + uint32_t num_calls{ 0 }; + + /*! \brief Number of solutions found by the resynthesis engine. */ + uint32_t num_sols{ 0 }; + + /*! \brief Number of patterns used. */ + uint32_t num_pats{ 0 }; + + /*! \brief Number of valid solutions. */ + uint32_t num_valid{ 0 }; + + /*! \brief Number of counter-examples. */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] simulation_guided_resynthesis report\n" ); + fmt::print( " initial #pats = {} + #CEXs = {} --> {}\n", num_pats, num_cex, num_pats + num_cex ); + fmt::print( " #resyn calls = {}, #solutions = {} ({:.2f}%)\n", num_calls, num_sols, float( num_sols ) / float( num_calls ) * 100 ); + fmt::print( " #valid = {} ({:.2f}%), #CEXs = {} ({:.2f}%), #TOs = {} ({:.2f}%)\n", num_valid, float( num_valid ) / float( num_sols ) * 100, num_cex, float( num_cex ) / float( num_sols ) * 100, num_timeout, float( num_timeout ) / float( num_sols ) * 100 ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Pattern gen.: {:>5.2f} secs [Called in init() -- should be subtracted]\n", to_seconds( time_patgen ) ); + fmt::print( " Simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " SAT : {:>5.2f} secs\n", to_seconds( time_sat ) ); + fmt::print( " Resynthesis : {:>5.2f} secs\n", to_seconds( time_resyn ) ); + fmt::print( " ODC comp. : {:>5.2f} secs\n", to_seconds( time_odc ) ); + fmt::print( " Save patterns : {:>5.2f} secs [Called in destructor -- not included in total runtime]\n", to_seconds( time_patsave ) ); + // clang-format on + } +}; + +namespace detail +{ + +template +struct breadth_first_window +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + node root; + std::vector divs; + uint32_t mffc_size; + uint32_t max_size{ std::numeric_limits::max() }; + // uint32_t max_level{std::numeric_limits::max()}; +}; + +template +class breadth_first_windowing +{ +public: + using problem_t = breadth_first_window; + using params_t = breadth_first_windowing_params; + using stats_t = breadth_first_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit breadth_first_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_tfi, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + } + + void init() + { + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + /* collect TFI nodes with BFS and supported "wing" nodes */ + win.root = n; + win.divs.clear(); + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_tfi_and_wings( n, win.divs ); + } ); + + /* compute and mark MFFC nodes */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, {}, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + + /* exclude MFFC node in divs */ + uint32_t counter{ 0 }; + for ( int32_t i = 0; i < win.divs.size(); ++i ) + { + if ( ntk.value( win.divs.at( i ) ) == mffc_marker ) + { + ++counter; + win.divs[i] = win.divs.back(); + win.divs.pop_back(); + --i; + } + } + + /* all MFFC nodes should be in TFI (thus collected in divs) */ + assert( counter == win.mffc_size ); + win.max_size = std::min( win.mffc_size - 1, ps.max_inserts ); + + st.num_windows++; + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return prob.mffc_size - res.num_gates(); + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, prob.divs.begin(), prob.divs.end(), res, [&]( signal const& g ) { + ntk.substitute_node( prob.root, g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root node {}\n", to_index_list_string( res ), prob.root ); + return true; + } + +private: +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + uint32_t mffc_marker{ 0u }; + divisor_collector divs_mgr; +}; /* breadth_first_windowing */ + +template +class simulation_guided_resynthesis +{ +public: + using problem_t = breadth_first_window; + using res_t = typename ResynEngine::index_list_t; + using params_t = simulation_guided_resynthesis_params; + using stats_t = simulation_guided_resynthesis_stats; + + using node = typename Ntk::node; + using TT = kitty::partial_truth_table; + using validator_t = circuit_validator; + + explicit simulation_guided_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), engine( rst ), + validator( ntk, { ps.max_clauses, ps.odc_levels, ps.conflict_limit, ps.random_seed } ), tts( ntk ) + {} + + ~simulation_guided_resynthesis() + { + if ( ps.save_patterns ) + { + call_with_stopwatch( st.time_patsave, [&]() { + write_patterns( sim, *ps.save_patterns ); + } ); + } + + if ( add_event ) + { + ntk.events().release_add_event( add_event ); + } + } + + void init() + { + add_event = ntk.events().register_add_event( [&]( const auto& n ) { + tts.resize(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } ); + + /* prepare simulation patterns */ + call_with_stopwatch( st.time_patgen, [&]() { + if ( ps.pattern_filename ) + { + sim = partial_simulator( *ps.pattern_filename ); + } + else + { + sim = partial_simulator( ntk.num_pis(), 1024 ); + pattern_generation( ntk, sim ); + } + } ); + st.num_pats = sim.num_bits(); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + std::optional operator()( problem_t& prob ) + { + for ( auto j = 0u; j < ps.max_trials; ++j ) + { + check_tts( prob.root ); + for ( auto const& d : prob.divs ) + { + check_tts( d ); + } + + TT const care = call_with_stopwatch( st.time_odc, [&]() { + return ( ps.odc_levels == 0 ) ? sim.compute_constant( true ) : ~observability_dont_cares( ntk, prob.root, sim, tts, ps.odc_levels ); + } ); + + const auto res = call_with_stopwatch( st.time_resyn, [&]() { + ++st.num_calls; + return engine( tts[prob.root], care, std::begin( prob.divs ), std::end( prob.divs ), tts, prob.max_size ); + } ); + + if ( res ) + { + ++st.num_sols; + auto const& id_list = *res; + auto valid = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( prob.root, prob.divs, id_list ); + } ); + if ( valid ) + { + if ( *valid ) + { + ++st.num_valid; + if constexpr ( UseODC ) + { + /* restart the solver -- clear constructed CNF */ + call_with_stopwatch( st.time_sat, [&]() { + validator.update(); + } ); + } + return id_list; + } + else + { + ++st.num_cex; + call_with_stopwatch( st.time_sim, [&]() { + sim.add_pattern( validator.cex ); + } ); + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + continue; + } + } + else /* timeout */ + { + ++st.num_timeout; + return std::nullopt; + } + } + else /* functor can not find any potential resubstitution */ + { + return std::nullopt; + } + } /* limit on number of trials exceeded */ + return std::nullopt; + } + +private: + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + +private: + Ntk const& ntk; + params_t const& ps; + stats_t& st; + typename ResynEngine::stats rst; + ResynEngine engine; + partial_simulator sim; + validator_t validator; + incomplete_node_map tts; + + std::shared_ptr::add_event_type> add_event; +}; /* simulation_guided_resynthesis */ + +} /* namespace detail */ + +using sim_resub_params = boolean_optimization_params; +using sim_resub_stats = boolean_optimization_stats; + +template +void simulation_xag_heuristic_resub( Ntk& ntk, sim_resub_params const& ps = {}, sim_resub_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not xag_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using windowing_t = typename detail::breadth_first_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::simulation_guided_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + sim_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void simulation_aig_heuristic_resub( Ntk& ntk, sim_resub_params const& ps = {}, sim_resub_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not aig_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using windowing_t = typename detail::breadth_first_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::simulation_guided_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + sim_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/experimental/window_resub.hpp b/include/mockturtle/algorithms/experimental/window_resub.hpp new file mode 100644 index 0000000..deedd3a --- /dev/null +++ b/include/mockturtle/algorithms/experimental/window_resub.hpp @@ -0,0 +1,834 @@ +/* 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 window_resub.hpp + \brief Windowing for small-window-based, enumeration-based (classical) resubstitution + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include "../../views/depth_view.hpp" +#include "../../views/fanout_view.hpp" +#include "../detail/resub_utils.hpp" +#include "../dont_cares.hpp" +#include "../reconv_cut.hpp" +#include "../resyn_engines/aig_enumerative.hpp" +#include "../resyn_engines/mig_enumerative.hpp" +#include "../resyn_engines/mig_resyn.hpp" +#include "../resyn_engines/xag_resyn.hpp" +#include "../simulation.hpp" + +#include "../../utils/include/percy.hpp" + +#include + +#include +#include +#include + +namespace mockturtle::experimental +{ + +struct complete_tt_windowing_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to update node levels lazily. */ + bool update_levels_lazily{ false }; + + /*! \brief Whether to prevent from increasing depth. */ + bool preserve_depth{ false }; + + /*! \brief Whether to normalize the truth tables. + * + * For some enumerative resynthesis engines, if the truth tables + * are normalized, some cases can be eliminated and thus improves + * efficiency. When this option is turned off, be sure to use an + * implementation of resynthesis that does not make this assumption; + * otherwise, quality degradation may be observed. + * + * Normalization is typically only useful for enumerative methods + * and for smaller solutions (i.e. when `max_inserts` < 2). Turning + * on normalization may result in larger runtime overhead when there + * are many divisors or when the truth tables are long. + */ + bool normalize{ false }; +}; + +struct complete_tt_windowing_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor collection. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Accumulated runtime for don't care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Total number of leaves. */ + uint64_t num_leaves{ 0u }; + + /*! \brief Total number of divisors. */ + uint64_t num_divisors{ 0u }; + + /*! \brief Number of constructed windows. */ + uint32_t num_windows{ 0u }; + + /*! \brief Total number of MFFC nodes. */ + uint64_t sum_mffc_size{ 0u }; + + void report() const + { + // clang-format off + fmt::print( "[i] complete_tt_windowing report\n" ); + fmt::print( " tot. #leaves = {:5d}, tot. #divs = {:5d}, sum |MFFC| = {:5d}\n", num_leaves, num_divisors, sum_mffc_size ); + fmt::print( " avg. #leaves = {:>5.2f}, avg. #divs = {:>5.2f}, avg. |MFFC| = {:>5.2f}\n", float( num_leaves ) / float( num_windows ), float( num_divisors ) / float( num_windows ), float( sum_mffc_size ) / float( num_windows ) ); + fmt::print( " ===== Runtime Breakdown =====\n" ); + fmt::print( " Total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( " Cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( " MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( " Divs : {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( " Simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( " Dont cares: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + // clang-format on + } +}; + +template +struct resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Total number of problems received. */ + uint32_t num_probs{ 0u }; + + /*! \brief Total number of solutions found. */ + uint32_t num_sols{ 0u }; + + /*! \brief Summed sizes of solutions. */ + uint32_t sum_sol_size{ 0u }; + + /*! \brief Summed ratios of solution size over max size. */ + float sum_ratio{ 0.0 }; + uint32_t sum_overhead{ 0u }; + + /*! \brief Summed max_size of all problems. */ + uint32_t sum_max_size{ 0u }; + + /*! \brief Summed max_size of solved problems. */ + uint32_t sum_max_size_solved{ 0u }; + + /*! \brief Statistics object for the resynthesis engine. */ + EngineStats rst; + + void report() const + { + // clang-format off + fmt::print( "[i] resynthesis report\n" ); + fmt::print( " tot. #problems = {:5d}, tot. #solutions = {:5d}, #sols/#probs = {:>5.2f} (%)\n", num_probs, num_sols, float( num_sols ) / float( num_probs ) * 100.0 ); + fmt::print( " avg. |H| = {:>5.2f}, avg. |H|/max = {:>7.4f}, avg. overhead = {:>5.2f}\n", float( sum_sol_size ) / float( num_sols ), sum_ratio / float( num_sols ), sum_overhead / float( num_sols ) ); + fmt::print( " avg. max size (all problems) = {:>5.2f}, avg. max size (solved) = {:>5.2f}\n", float( sum_max_size ) / float( num_probs ), float( sum_max_size_solved ) / float( num_sols ) ); + fmt::print( " Total runtime: {:>5.2f} secs\n", to_seconds( time_total ) ); + // clang-format on + } +}; + +namespace detail +{ + +template +struct small_window +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + signal root; + uint32_t num_leaves; + std::vector divs; + std::vector div_ids; /* positions of divisor truth tables in `tts` */ + std::vector tts; + TT care; + uint32_t mffc_size; + uint32_t max_size{ std::numeric_limits::max() }; + uint32_t max_level{ std::numeric_limits::max() }; +}; + +template +class complete_tt_windowing +{ +public: + using problem_t = small_window; + using params_t = complete_tt_windowing_params; + using stats_t = complete_tt_windowing_stats; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit complete_tt_windowing( Ntk& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), ps( ps ), st( st ), cps( { ps.max_pis } ), mffc_mgr( ntk ), + divs_mgr( ntk, divisor_collector_params( { ps.max_divisors, ps.max_divisors, ps.skip_fanout_limit_for_divisors } ) ), + sim( ntk, win.tts, ps.max_pis ) + { + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + if constexpr ( !has_level_v ) + { + assert( !ps.preserve_depth && "Ntk does not have depth interface" ); + assert( !ps.update_levels_lazily && "Ntk does not have depth interface" ); + } + } + + ~complete_tt_windowing() + { + if constexpr ( has_level_v ) + { + if ( lazy_update_event ) + { + mockturtle::detail::release_lazy_level_update_events( ntk, lazy_update_event ); + } + } + } + + void init() + { + if constexpr ( has_level_v ) + { + if ( ps.update_levels_lazily ) + { + lazy_update_event = mockturtle::detail::register_lazy_level_update_events( ntk ); + } + } + } + + std::optional> operator()( node const& n ) + { + stopwatch t( st.time_total ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return std::nullopt; /* skip nodes with too many fanouts */ + } + + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + win.max_level = ntk.level( n ) - 1; + divs_mgr.set_max_level( win.max_level ); + } + } + + /* compute a cut and collect supported nodes */ + std::vector leaves = call_with_stopwatch( st.time_cuts, [&]() { + return reconvergence_driven_cut>( ntk, { n }, cps ).first; + } ); + std::vector supported; + call_with_stopwatch( st.time_divs, [&]() { + divs_mgr.collect_supported_nodes( n, leaves, supported ); + } ); + + /* simulate */ + call_with_stopwatch( st.time_sim, [&]() { + sim.simulate( leaves, supported ); + } ); + + /* mark MFFC nodes and collect divisors */ + ++mffc_marker; + win.mffc_size = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.call_on_mffc_and_count( n, leaves, [&]( node const& n ) { + ntk.set_value( n, mffc_marker ); + } ); + } ); + call_with_stopwatch( st.time_divs, [&]() { + collect_divisors( leaves, supported ); + } ); + + /* normalize */ + call_with_stopwatch( st.time_sim, [&]() { + if ( ps.normalize ) + { + win.root = normalize_truth_tables() ? !ntk.make_signal( n ) : ntk.make_signal( n ); + } + else + { + win.root = ntk.make_signal( n ); + } + } ); + + /* compute don't cares */ + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + win.care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + win.care = ~kitty::create( ps.max_pis ); + } + } ); + + win.max_size = std::min( win.mffc_size - 1, ps.max_inserts ); + + st.num_windows++; + st.num_leaves += leaves.size(); + st.num_divisors += win.divs.size(); + st.sum_mffc_size += win.mffc_size; + + return win; + } + + template + uint32_t gain( problem_t const& prob, res_t const& res ) const + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + return prob.mffc_size - res.num_gates(); + } + + template + bool update_ntk( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + insert( ntk, std::begin( prob.divs ), std::end( prob.divs ), res, [&]( signal const& g ) { + ntk.substitute_node( ntk.get_node( prob.root ), ntk.is_complemented( prob.root ) ? !g : g ); + } ); + return true; /* continue optimization */ + } + + template + bool report( problem_t const& prob, res_t const& res ) + { + static_assert( is_index_list_v, "res_t is not an index_list (windowing engine and resynthesis engine do not match)" ); + assert( res.num_pos() == 1 ); + fmt::print( "[i] found solution {} for root signal {}{}\n", to_index_list_string( res ), ntk.is_complemented( prob.root ) ? "!" : "", ntk.get_node( prob.root ) ); + return true; + } + +private: + void collect_divisors( std::vector const& leaves, std::vector const& supported ) + { + win.divs.clear(); + win.div_ids.clear(); + + uint32_t i{ 1 }; + for ( auto const& l : leaves ) + { + win.div_ids.emplace_back( i++ ); + win.divs.emplace_back( ntk.make_signal( l ) ); + } + win.num_leaves = leaves.size(); + + i = ps.max_pis + 1; + for ( auto const& n : supported ) + { + if ( ntk.value( n ) != mffc_marker ) /* not in MFFC, not root */ + { + win.div_ids.emplace_back( i ); + win.divs.emplace_back( ntk.make_signal( n ) ); + } + ++i; + } + assert( i == win.tts.size() ); + } + + bool normalize_truth_tables() + { + assert( win.divs.size() == win.div_ids.size() ); + for ( auto i = 0u; i < win.divs.size(); ++i ) + { + if ( kitty::get_bit( win.tts.at( win.div_ids.at( i ) ), 0 ) ) + { + win.tts.at( win.div_ids.at( i ) ) = ~win.tts.at( win.div_ids.at( i ) ); + win.divs.at( i ) = !win.divs.at( i ); + } + } + + if ( kitty::get_bit( win.tts.back(), 0 ) ) + { + win.tts.back() = ~win.tts.back(); + return true; + } + else + { + return false; + } + } + +private: + Ntk& ntk; + problem_t win; + params_t const& ps; + stats_t& st; + reconvergence_driven_cut_parameters const cps; + typename mockturtle::detail::node_mffc_inside mffc_mgr; // TODO: namespaces can be removed when we move out of experimental:: + divisor_collector divs_mgr; + window_simulator sim; + uint32_t mffc_marker{ 0u }; + std::shared_ptr::modified_event_type> lazy_update_event; +}; /* complete_tt_windowing */ + +template +class complete_tt_resynthesis +{ +public: + using problem_t = small_window; + using res_t = typename ResynEngine::index_list_t; + using params_t = null_params; + using stats_t = resynthesis_stats; + + explicit complete_tt_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), st( st ), engine( st.rst ) + {} + + void init() + {} + + std::optional operator()( problem_t& prob ) + { + ++st.num_probs; + st.sum_max_size += prob.max_size; + auto const res = call_with_stopwatch( st.time_total, [&](){ + if constexpr ( preserve_depth ) + { + return engine( prob.tts.back(), prob.care, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_size, prob.max_level ); + } + else + { + return engine( prob.tts.back(), prob.care, std::begin( prob.div_ids ), std::end( prob.div_ids ), prob.tts, prob.max_size ); + } + }); + if ( res ) + { + ++st.num_sols; + st.sum_max_size_solved += prob.max_size; + st.sum_sol_size += res->num_gates(); + if ( prob.max_size == 0 ) + st.sum_ratio += 1; + else + st.sum_ratio += float( res->num_gates() ) / float( prob.max_size ); + } + return res; + } + +private: + Ntk const& ntk; + stats_t& st; + ResynEngine engine; +}; /* complete_tt_resynthesis */ + +template> +class exact_resynthesis +{ +public: + using problem_t = small_window; + using res_t = index_list_t; + using params_t = null_params; + using stats_t = resynthesis_stats; + + explicit exact_resynthesis( Ntk const& ntk, params_t const& ps, stats_t& st ) + : ntk( ntk ), st( st ) + {} + + void init() + { + _allow_xor = false; + } + + std::optional operator()( problem_t& prob ) + { + ++st.num_probs; + st.sum_max_size += prob.max_size; + auto c = call_with_stopwatch( st.time_total, [&](){ + return solve( prob ); + }); + + if ( c && c->get_nr_steps() <= prob.max_size ) + { + ++st.num_sols; + st.sum_max_size_solved += prob.max_size; + st.sum_sol_size += c->get_nr_steps(); + if ( prob.max_size == 0 ) + st.sum_ratio += 1; + else + st.sum_ratio += float( c->get_nr_steps() ) / float( prob.max_size ); + //return translate( prob, *c ); + } + return std::nullopt; + } + +private: + std::optional solve( problem_t& prob ) + { + percy::spec spec; + if ( !_allow_xor ) + { + spec.set_primitive( percy::AIG ); + } + spec.fanin = 2; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + spec.max_nr_steps = prob.max_size; + + TT const& target = prob.tts.back(); + spec[0] = target; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( ~prob.care ) ) + { + spec.set_dont_care( 0, ~prob.care ); + with_dont_cares = true; + } + + /* add divisors */ + for ( auto i = prob.num_leaves; i < prob.divs.size(); ++i ) + { + spec.add_function( prob.tts[prob.div_ids[i]] ); + } + + //if ( !with_dont_cares && _ps.cache ) + //{ + // const auto it = _ps.cache->find( target ); + // if ( it != _ps.cache->end() ) + // { + // return it->second; + // } + //} + //if ( !with_dont_cares && _ps.blacklist_cache ) + //{ + // const auto it = _ps.blacklist_cache->find( target ); + // if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + // { + // return std::nullopt; + // } + //} + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + //if ( !with_dont_cares && _ps.blacklist_cache ) + //{ + // ( *_ps.blacklist_cache )[target] = ( result == percy::timeout ) ? _ps.conflict_limit : 0; + //} + return std::nullopt; + } + + assert( kitty::to_hex( c.simulate()[0u] & prob.care ) == kitty::to_hex( target & prob.care ) ); + + //if ( !with_dont_cares && _ps.cache ) + //{ + // ( *_ps.cache )[target] = c; + //} + return c; + } + + index_list_t translate( problem_t& prob, percy::chain const& c ) + { + index_list_t il( prob.divs.size() ); + /* Doesn't work well yet + std::vector negated( prob.divs.size() + c->get_nr_steps() + 1, false ); + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + auto const v1 = c->get_step( i )[0] + 1; // +1 : zero-based to one-based indices + auto const v2 = c->get_step( i )[1] + 1; + auto const l1 = negated[v1] ? ( v1 << 1 ) ^ 0x1 : v1 << 1; + auto const l2 = negated[v2] ? ( v2 << 1 ) ^ 0x1 : v2 << 1; + + switch ( c->get_operator( i )._bits[0] ) + { + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( c->get_operator( i ) ) << "\n"; + assert( false ); + break; + case 0x8: + il.add_and( l1, l2 ); + break; + case 0x4: + il.add_and( l1 ^ 0x1, l2 ); + break; + case 0x2: + il.add_and( l1, l2 ^ 0x1 ); + break; + case 0xe: + negated[il.add_and( l1 ^ 0x1, l2 ^ 0x1 ) >> 1] = true; + break; + case 0x6: + il.add_xor( l1, l2 ); + break; + } + } + + auto const last_lit = ( prob.divs.size() + c->get_nr_steps() ) << 1; + il.add_output( c->is_output_inverted( 0 ) ? last_lit ^ 0x1 : last_lit ); + */ + return il; + } + +private: + Ntk const& ntk; + bool _allow_xor; + struct + { + using cache_map_t = std::unordered_map>; + using cache_t = std::shared_ptr; + + using blacklist_cache_map_t = std::unordered_map>; + using blacklist_cache_t = std::shared_ptr; + + cache_t cache; + blacklist_cache_t blacklist_cache; + + bool add_alonce_clauses{ true }; + bool add_colex_clauses{ true }; + bool add_lex_clauses{ false }; + bool add_lex_func_clauses{ true }; + bool add_nontriv_clauses{ true }; + bool add_noreapply_clauses{ true }; + bool add_symvar_clauses{ true }; + int conflict_limit{ 1000 }; + + percy::SolverType solver_type = percy::SLV_BSAT2; + percy::EncoderType encoder_type = percy::ENC_SSV; + percy::SynthMethod synthesis_method = percy::SYNTH_STD; + } _ps; + stats_t& st; +}; /* exact_resynthesis */ + +} /* namespace detail */ + +using window_resub_params = boolean_optimization_params; +using window_resub_stats = boolean_optimization_stats>; +using window_resub_stats_xag = boolean_optimization_stats>; +using window_resub_stats_aig_enum = boolean_optimization_stats>; +using window_resub_stats_mig = boolean_optimization_stats>; + +template +void window_xag_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_xag* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not xag_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_xag st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_aig_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_xag* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk::base_type is not aig_network" ); + + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = xag_resyn_decompose>; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_xag st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_aig_enumerative_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_aig_enum* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + window_resub_stats_aig_enum st; + + using TT = typename kitty::static_truth_table<8>; + using windowing_t = typename detail::complete_tt_windowing; + if ( ps.wps.normalize ) + { + using engine_t = aig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( viewed, ps, st ); + p.run(); + } + else + { + using engine_t = aig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + opt_t p( viewed, ps, st ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_mig_heuristic_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats_mig* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = mig_resyn_topdown; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats_mig st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +template +void window_mig_enumerative_resub( Ntk& ntk, window_resub_params const& ps = {}, window_resub_stats* pst = nullptr ) +{ + using ViewedNtk = depth_view>; + fanout_view fntk( ntk ); + ViewedNtk viewed( fntk ); + + using TT = typename kitty::dynamic_truth_table; + using windowing_t = typename detail::complete_tt_windowing; + using engine_t = mig_enumerative_resyn; + using resyn_t = typename detail::complete_tt_resynthesis; + using opt_t = typename detail::boolean_optimization_impl; + + window_resub_stats st; + opt_t p( viewed, ps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle::experimental */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/explorer.hpp b/include/mockturtle/algorithms/explorer.hpp new file mode 100644 index 0000000..1e27c6e --- /dev/null +++ b/include/mockturtle/algorithms/explorer.hpp @@ -0,0 +1,996 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file explorer.hpp + \brief Implements the design space explorer engine + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "lut_mapper.hpp" +#include "collapse_mapped.hpp" +#include "klut_to_graph.hpp" +#include "cut_rewriting.hpp" +#include "refactoring.hpp" +#include "mig_algebraic_rewriting.hpp" +#include "mapper.hpp" +#include "rewrite.hpp" +#include "node_resynthesis/mig_npn.hpp" +#include "node_resynthesis/sop_factoring.hpp" +#include "resubstitution.hpp" +#include "aig_resub.hpp" +#include "mig_resub.hpp" +#include "sim_resub.hpp" +#include "cleanup.hpp" +#include "balancing.hpp" +#include "balancing/sop_balancing.hpp" +#include "aig_balancing.hpp" +#include "miter.hpp" +#include "equivalence_checking.hpp" +#include "aqfp/buffer_insertion.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../views/mapping_view.hpp" +#include "../io/write_verilog.hpp" +#include "../io/write_aiger.hpp" +#include "../io/verilog_reader.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/abc.hpp" + +#include + +#define explorer_debug 0 + +namespace mockturtle +{ + +struct explorer_params +{ + /*! \brief Number of iterations to run with different random seed, restarting from the original + * network (including the first iteration). */ + uint32_t num_restarts{1u}; + + /*! \brief Initial random seed used to generate random seeds randomly. */ + uint32_t random_seed{0u}; + + /*! \brief Maximum number of steps in each iteration. */ + uint32_t max_steps{100000u}; + + /*! \brief Maximum number of steps without improvement in each iteration. */ + uint32_t max_steps_no_impr{1000000u}; + + /*! \brief Number of compressing scripts to run per step. */ + uint32_t compressing_scripts_per_step{3u}; + + /*! \brief Timeout per iteration in seconds. */ + uint32_t timeout{30u}; + + /*! \brief Be verbose. */ + bool verbose{false}; + + /*! \brief Be very verbose. */ + bool very_verbose{false}; +}; + +struct explorer_stats +{ + stopwatch<>::duration time_total{0}; + stopwatch<>::duration time_evaluate{0}; +}; + +template +using script_t = std::function; + +template +using cost_fn_t = std::function; + +template +std::function size_cost_fn = []( Ntk const& ntk ){ return ntk.num_gates(); }; + +template +class explorer +{ +public: + using RandEngine = std::default_random_engine; + + explorer( explorer_params const& ps, explorer_stats& st, cost_fn_t const& cost_fn = size_cost_fn ) + : _ps( ps ), _st( st ), cost( cost_fn ) + { + } + + void add_decompressing_script( script_t const& algo, float weight = 1.0 ) + { + decompressing_scripts.emplace_back( std::make_pair( algo, total_weights_dec ) ); + total_weights_dec += weight; + } + + void add_compressing_script( script_t const& algo, float weight = 1.0 ) + { + compressing_scripts.emplace_back( std::make_pair( algo, total_weights_com ) ); + total_weights_com += weight; + } + + Ntk run( Ntk const& ntk ) + { + stopwatch t( _st.time_total ); + if ( decompressing_scripts.size() == 0 ) + { + std::cerr << "[e] No decompressing script provided.\n"; + return ntk; + } + if ( compressing_scripts.size() == 0 ) + { + std::cerr << "[e] No compressing script provided.\n"; + return ntk; + } + + RandEngine rnd( _ps.random_seed ); + auto init_cost = call_with_stopwatch( _st.time_evaluate, [&](){ return cost( ntk ); } ); + Ntk best = ntk.clone(); + auto best_cost = init_cost; + for ( auto i = 0u; i < _ps.num_restarts; ++i ) + { + Ntk current = ntk.clone(); + auto new_cost = run_one_iteration( current, rnd(), init_cost ); + if ( new_cost < best_cost ) + { + best = current.clone(); + best_cost = new_cost; + } + if ( _ps.verbose ) + fmt::print( "[i] best cost in restart {}: {}, overall best cost: {}\n", i, new_cost, best_cost ); + } + return best; + } + +private: + uint32_t run_one_iteration( Ntk& ntk, uint32_t seed, uint32_t init_cost ) + { + if ( _ps.verbose ) + { + fmt::print( "\n[i] new restart using seed {}, original cost = {}\n", seed, init_cost ); + } + + stopwatch<>::duration elapsed_time{0}; + RandEngine rnd( seed ); + Ntk best = ntk.clone(); + auto best_cost = init_cost; + uint32_t last_update{0u}; + for ( auto i = 0u; i < _ps.max_steps; ++i ) + { + #if explorer_debug + Ntk backup = ntk.clone(); + #endif + + { + stopwatch t( elapsed_time ); + decompress( ntk, rnd, i ); + compress( ntk, rnd, i ); + } + auto new_cost = call_with_stopwatch( _st.time_evaluate, [&](){ return cost( ntk ); } ); + if ( _ps.very_verbose ) + fmt::print( "[i] after step {}, cost = {}\n", i, new_cost ); + + #if explorer_debug + if ( !*equivalence_checking( *miter( ntk, best ) ) ) + { + write_verilog( backup, "debug.v" ); + write_verilog( ntk, "wrong.v" ); + fmt::print( "NEQ at step {}!\n", i ); + break; + } + #endif + + if ( new_cost < best_cost ) + { + best = ntk.clone(); + best_cost = new_cost; + last_update = i; + if ( _ps.verbose ) + { + fmt::print( "[i] updated new best at step {}: {}\n", i, best_cost ); + } + } + if ( i - last_update >= _ps.max_steps_no_impr ) + { + if ( _ps.verbose ) + fmt::print( "[i] break restart at step {} after {} steps without improvement (elapsed time: {} secs)\n", i, _ps.max_steps_no_impr, to_seconds( elapsed_time ) ); + break; + } + if ( to_seconds( elapsed_time ) >= _ps.timeout ) + { + if ( _ps.verbose ) + fmt::print( "[i] break restart at step {} after timeout of {} secs\n", i, to_seconds( elapsed_time ) ); + break; + } + } + std::cout << std::flush; + ntk = best; + return best_cost; + } + + void decompress( Ntk& ntk, RandEngine& rnd, uint32_t i ) + { + std::uniform_real_distribution<> dis( 0.0, total_weights_dec ); + float r = dis( rnd ); + for ( auto it = decompressing_scripts.rbegin(); it != decompressing_scripts.rend(); ++it ) + { + if ( r >= it->second ) + { + it->first( ntk, i, rnd() ); + break; + } + } + } + + void compress( Ntk& ntk, RandEngine& rnd, uint32_t i ) + { + std::uniform_real_distribution<> dis( 0.0, total_weights_com ); + for ( auto j = 0u; j < _ps.compressing_scripts_per_step; ++j ) + { + float r = dis( rnd ); + for ( auto it = compressing_scripts.rbegin(); it != compressing_scripts.rend(); ++it ) + { + if ( r >= it->second ) + { + it->first( ntk, i, rnd() ); + break; + } + } + } + } + +private: + const explorer_params _ps; + explorer_stats& _st; + + std::vector, float>> decompressing_scripts; + float total_weights_dec{0.0}; + std::vector, float>> compressing_scripts; + float total_weights_com{0.0}; + + cost_fn_t cost; +}; + +mig_network explore_mig( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + mapping_view mapped{ _ntk }; + lut_map( mapped, mps ); + const auto klut = *collapse_mapped_network( mapped ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn{ true }; + exact_library exact_lib( resyn ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +#ifdef ENABLE_ABC +mig_network deepsyn_mig_v1( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with &if using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20; &st", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5)); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + klut_network klut = lut_map( _ntk, mps ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resyn2rs using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + //std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + std::string script = "&put; resyn2rs; &get"; + + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with remapping using random value {}\n", rand ); + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with rewriting using random value {}\n", rand ); + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x1; + exact_library exact_lib( resyn, eps ); + rewrite_params rps; + rps.use_dont_cares = rand & 0x1; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resub using random value {}\n", rand ); + resubstitution_params rps; + rps.max_inserts = (rand >> 1) & 0x7; + rps.max_pis = (rand >> 4) & 0x3 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_mig_v2( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with &if using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20; &st", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5)); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (rand & 0x3); //3 + (i % 4); + klut_network klut = lut_map( _ntk, mps ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with resyn2rs using random value {}\n", rand ); + aig_network aig = cleanup_dangling( _ntk ); + //std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + std::string script = "&put; resyn2rs; &get"; + + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = rand & 0x2; + exact_library exact_lib( resyn, eps ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; // high-effort remap + mps.use_dont_cares = rand & 0x2; + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "compressing with Ale flow using random value {}\n", rand ); + //_ntk = cleanup_dangling( _ntk ); + + mig_npn_resynthesis resyn{ true }; + exact_library_params eps; + eps.np_classification = false; + eps.compute_dc_classes = true; + exact_library exact_lib( resyn, eps ); + + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = true; // high-effort remap + + rewrite_params rps; + + resubstitution_params rsps; + rsps.max_inserts = 20; + rsps.max_pis = 8; + + mps.use_dont_cares = rand & 0x8; + _ntk = map( _ntk, exact_lib, mps ); + mps.use_dont_cares = rand & 0xf; + _ntk = map( _ntk, exact_lib, mps ); + mps.use_dont_cares = rand & 0x10; + _ntk = map( _ntk, exact_lib, mps ); + + rps.use_dont_cares = rand & 0x1; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + rps.use_dont_cares = rand & 0x2; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + rps.use_dont_cares = rand & 0x4; + rewrite( _ntk, exact_lib, rps ); + _ntk = cleanup_dangling( _ntk ); + + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rsps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_mig_depth( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + cost_fn_t depth_cost = []( Ntk const& _ntk ){ + depth_view d{ _ntk }; + return d.depth(); + }; + + explorer_stats st; + explorer expl( ps, st, depth_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{} -m; &if -K {}; &mfs -e -W 20 {}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + _ntk = cleanup_dangling( _ntk ); + }, 0.3 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + std::string script = "&put; resyn2rs; &get"; + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn{ true }; + exact_library exact_lib( resyn ); + map_params mps; + mps.skip_delay_round = false; + //mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + }, 0.5 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = ( rand & 0x1 ) ? 8 : 10; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_factoring resyn; + refactoring( _ntk, resyn ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + return expl.run( ntk ); +} + +aig_network deepsyn_aig( aig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = aig_network; + + explorer_stats st; + explorer expl( ps, st ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20{}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + _ntk = call_abc_script( _ntk, script ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + _ntk = call_abc_script( _ntk, script ); + } ); + + return expl.run( ntk ); +} + +mig_network deepsyn_aqfp( mig_network const& ntk, explorer_params const ps = {}, explorer_stats * pst = nullptr ) +{ + using Ntk = mig_network; + + cost_fn_t aqfp_cost = []( Ntk const& _ntk ){ + buffer_insertion_params bps; + bps.assume.balance_cios = true; + bps.assume.splitter_capacity = 4; + bps.assume.ci_phases = {0}; + bps.assume.num_phases = 1; + bps.scheduling = buffer_insertion_params::better_depth; + bps.optimization_effort = buffer_insertion_params::one_pass; + buffer_insertion buf_inst( _ntk, bps ); + auto numbufs = buf_inst.dry_run(); + + //return (_ntk.num_gates() * 6 + numbufs * 2); + return (_ntk.num_gates() * 6 + numbufs * 2) * buf_inst.depth(); + }; + + explorer_stats st; + explorer expl( ps, st, aqfp_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + + std::string script = fmt::format( + "&dch{}; &if -a -K {}; &mfs -e -W 20 -L 20{}", + (rand & 0x1) ? " -f" : "", + 2 + (i % 5), + ((rand >> 2) & 0x1) ? "; &fx; &st" : ""); + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + std::string script = (rand & 0x1) ? "; &c2rs" : "; &dc2"; + aig = call_abc_script( aig, script ); + + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + _ntk = map( aig, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + mig_npn_resynthesis resyn2{ true }; + exact_library exact_lib( resyn2 ); + map_params mps; + mps.skip_delay_round = true; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( _ntk, exact_lib, mps ); + } ); + + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x3; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + } ); + + // balancing + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + } ); + + // algebraic depth optimization + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + } ); + + auto res = expl.run( ntk ); + if ( pst ) + *pst = st; + return res; +} +#endif + +void compress2rs_aig( aig_network& aig ) +{ + xag_npn_resynthesis resyn; + cut_rewriting_params cps; + cps.cut_enumeration_ps.cut_size = 4; + resubstitution_params rps; + //sop_rebalancing balance_fn; + //balancing_params bps; + //bps.cut_enumeration_ps.cut_size = 6u; + aig_balancing_params abps; + refactoring_params fps; + sop_factoring resyn2; + + /*abps.minimize_levels = true;*/ aig_balance( aig, abps ); // "b -l" + rps.max_pis = 6; rps.max_inserts = 1; /*rps.preserve_depth = true;*/ aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 6 -l" + /*cps.preserve_depth = true;*/ aig = cut_rewriting( aig, resyn, cps ); // "rw -l" + rps.max_pis = 6; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 6 -N 2 -l" + /*fps.preserve_depth = true;*/ refactoring( aig, resyn2, fps ); aig = cleanup_dangling( aig ); // "rf -l" + rps.max_pis = 8; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 8 -l" + aig_balance( aig, abps ); // "b -l" + rps.max_pis = 8; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 8 -N 2 -l" + aig = cut_rewriting( aig, resyn, cps ); // "rw -l" + rps.max_pis = 10; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 10 -l" + cps.allow_zero_gain = true; aig = cut_rewriting( aig, resyn, cps ); // "rwz -l" + rps.max_pis = 10; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 10 -N 2 -l" + aig_balance( aig, abps ); // "b -l" + rps.max_pis = 12; rps.max_inserts = 1; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 12 -l" + fps.allow_zero_gain = true; refactoring( aig, resyn2, fps ); aig = cleanup_dangling( aig ); // "rfz -l" + rps.max_pis = 12; rps.max_inserts = 2; aig_resubstitution( aig, rps ); aig = cleanup_dangling( aig ); // "rs -K 12 -N 2 -l" + aig = cut_rewriting( aig, resyn, cps ); // "rwz -l" + aig_balance( aig, abps ); // "b -l" +} + +mig_network explore_aqfp( mig_network const& ntk, explorer_params const ps = {} ) +{ + using Ntk = mig_network; + + cost_fn_t aqfp_cost = []( Ntk const& _ntk ){ + buffer_insertion_params bps; + bps.assume.balance_cios = true; + bps.assume.splitter_capacity = 4; + bps.assume.ci_phases = {0}; + bps.scheduling = buffer_insertion_params::better_depth; + bps.optimization_effort = buffer_insertion_params::none; + buffer_insertion buf_inst( _ntk, bps ); + + return _ntk.num_gates() * 6 + buf_inst.dry_run() * 2; + }; + + explorer_stats st; + explorer expl( ps, st, aqfp_cost ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with k-LUT mapping using random value {}, k = {}\n", rand, 2 + (rand % 5) ); + lut_map_params mps; + mps.cut_enumeration_ps.cut_size = 3 + (i & 0x3); + mapping_view mapped{ _ntk }; + lut_map( mapped, mps ); + const auto klut = *collapse_mapped_network( mapped ); + + if ( (rand >> 2) & 0x1 ) + { + _ntk = convert_klut_to_graph( klut ); + } + else + { + sop_factoring resyn; + _ntk = node_resynthesis( klut, resyn ); + } + } ); + + expl.add_decompressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + //fmt::print( "decompressing with break-MAJ using random value {}\n", rand ); + std::mt19937 g( rand ); + _ntk.foreach_gate( [&]( auto n ){ + bool is_maj = true; + _ntk.foreach_fanin( n, [&]( auto fi ){ + if ( _ntk.is_constant( _ntk.get_node( fi ) ) ) + is_maj = false; + return; + }); + if ( !is_maj ) + return; + std::vector fanins; + _ntk.foreach_fanin( n, [&]( auto fi ){ + fanins.emplace_back( fi ); + }); + + std::shuffle( fanins.begin(), fanins.end(), g ); + _ntk.substitute_node( n, _ntk.create_or( _ntk.create_and( fanins[0], fanins[1] ), _ntk.create_and( fanins[2], !_ntk.create_and( !fanins[0], !fanins[1] ) ) ) ); + }); + }, 0.3 ); + + // high-effort AIG optimization + (50% chance high-effort) mapping + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + aig_network aig = cleanup_dangling( _ntk ); + compress2rs_aig( aig ); + + mig_npn_resynthesis resyn3{ true }; + exact_library exact_lib( resyn3 ); + map_params mps; + mps.skip_delay_round = false; + mps.required_time = std::numeric_limits::max(); + mps.area_flow_rounds = 1; + mps.enable_logic_sharing = rand & 0x1; /* high-effort remap */ + _ntk = map( aig, exact_lib, mps ); + } ); + + // high-effort MIG resub x1 + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + resubstitution_params rps; + rps.max_inserts = rand & 0x7; + rps.max_pis = (rand >> 3) & 0x1 ? 6 : 8; + depth_view depth_mig{ _ntk }; + fanout_view fanout_mig{ depth_mig }; + mig_resubstitution2( fanout_mig, rps ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + // balancing + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + sop_rebalancing balance_fn; + balancing_params bps; + bps.cut_enumeration_ps.cut_size = 6u; + _ntk = balancing( _ntk, {balance_fn}, bps ); + }, 0.5 ); + + // algebraic depth optimization + expl.add_compressing_script( []( Ntk& _ntk, uint32_t i, uint32_t rand ){ + depth_view depth_mig{ _ntk }; + mig_algebraic_depth_rewriting( depth_mig ); + _ntk = cleanup_dangling( _ntk ); + }, 0.5 ); + + return expl.run( ntk ); +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/extract_adders.hpp b/include/mockturtle/algorithms/extract_adders.hpp new file mode 100644 index 0000000..072aaf3 --- /dev/null +++ b/include/mockturtle/algorithms/extract_adders.hpp @@ -0,0 +1,971 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file extract_adders.hpp + \brief Maps adders in the network + + \author Alessandro Tempia Calvino +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include "../networks/block.hpp" +#include "../networks/storage.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/choice_view.hpp" +#include "cut_enumeration.hpp" + +namespace mockturtle +{ + +struct extract_adders_params +{ + extract_adders_params() + { + cut_enumeration_ps.cut_limit = 49; + cut_enumeration_ps.minimize_truth_table = false; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 49. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Map inverted (NAND2-XNOR2, MIN3-XNOR3) */ + bool map_inverted{ false }; + + /*! \brief Filter HAs/FAs using MFFC inclusion */ + bool use_mffc_filter{ true }; + + /*! \brief Be verbose */ + bool verbose{ false }; +}; + +struct extract_adders_stats +{ + /*! \brief Computed cuts. */ + uint32_t cuts_total{ 0 }; + + /*! \brief Gates count. */ + uint32_t and2{ 0 }; + uint32_t maj3{ 0 }; + uint32_t xor2{ 0 }; + uint32_t xor3{ 0 }; + + /*! \brief Hashed classes. */ + uint32_t num_classes{ 0 }; + + /*! \brief Hash size. */ + uint32_t mapped_ha{ 0 }; + uint32_t mapped_fa{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Cuts = {}\t And2 = {}\t Xor2 = {}\t Maj3 = {}\t Xor3 = {}\n", + cuts_total, and2, xor2, maj3, xor3 ); + std::cout << fmt::format( "[i] Classes = {} \tMapped HA = {}\t Mapped FA:{}\n", num_classes, mapped_ha, mapped_fa ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +struct triple_hash +{ + uint64_t operator()( const std::array& p ) const + { + uint64_t seed = hash_block( p[0] ); + + hash_combine( seed, hash_block( p[1] ) ); + hash_combine( seed, hash_block( p[2] ) ); + + return seed; + } +}; + +struct cut_enumeration_fa_cut +{ + /* stats */ + bool is_xor{ false }; +}; + +template +class extract_adders_impl +{ +public: + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + using leaves_hash_t = phmap::flat_hash_map, std::vector, triple_hash>; + using match_pair_t = std::pair; + using matches_t = std::vector; + using block_map = node_map, Ntk>; + +public: + explicit extract_adders_impl( Ntk& ntk, extract_adders_params const& ps, extract_adders_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps ) ), + cuts_classes(), + half_adders(), + full_adders(), + node_match( ntk.size(), UINT32_MAX ) + { + cuts_classes.reserve( 2000 ); + tmp_visited.reserve( 20 ); + } + + block_network run() + { + stopwatch t( st.time_total ); + + auto [res, old2new] = initialize_map_network(); + create_classes(); + match_adders(); + map(); + topo_sort(); + finalize( res, old2new ); + + return res; + } + +private: + void create_classes() + { + uint32_t counter = 0; + std::array leaves = { 0, 0, 0 }; + + st.cuts_total = cuts.total_cuts(); + + ntk.foreach_gate( [&]( auto const& n ) { + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + kitty::static_truth_table<3> tt = cuts.truth_table( *cut ); + + bool to_add = false; + if ( cut->size() == 2 ) + { + /* check for and2 */ + for ( uint32_t func : and2func ) + { + if ( tt._bits == func ) + { + ++st.and2; + to_add = true; + break; + } + } + + /* check for xor2 */ + for ( uint32_t func : xor2func ) + { + if ( tt._bits == func ) + { + ++st.xor2; + ( *cut )->data.is_xor = true; + to_add = true; + break; + } + } + } + else if ( cut->size() == 3 ) + { + /* check for maj3 */ + for ( uint32_t func : maj3func ) + { + if ( tt._bits == func ) + { + ++st.maj3; + to_add = true; + break; + } + } + + /* check xor3 */ + for ( uint32_t func : xor3func ) + { + if ( tt._bits == func ) + { + ++st.xor3; + ( *cut )->data.is_xor = true; + to_add = true; + break; + } + } + } + + if ( !to_add ) + { + ++cut_index; + continue; + } + + uint64_t data = ( static_cast( ntk.node_to_index( n ) ) << 16 ) | cut_index; + leaves[2] = 0; + uint32_t i = 0; + for ( auto l : *cut ) + leaves[i++] = l; + + /* add to hash table */ + auto& v = cuts_classes[leaves]; + v.push_back( data ); + + ++cut_index; + } + } ); + + st.num_classes = cuts_classes.size(); + } + + void match_adder2( std::pair, std::vector> const& it ) + { + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + uint64_t data_i = it.second[i]; + uint32_t index_i = data_i >> 16; + uint32_t cut_index_i = data_i & UINT16_MAX; + auto const& cut_i = cuts.cuts( index_i )[cut_index_i]; + + /* TODO: find unique matches */ + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + uint64_t data_j = it.second[j]; + uint32_t index_j = data_j >> 16; + uint32_t cut_index_j = data_j & UINT16_MAX; + auto const& cut_j = cuts.cuts( index_j )[cut_index_j]; + + /* not compatible */ + if ( cut_i->data.is_xor == cut_j->data.is_xor ) + continue; + + /* check compatibility */ + if ( !check_adder( index_i, index_j, cut_i ) ) + continue; + + assert( cut_i.size() == 2 ); + assert( cut_j.size() == 2 ); + + half_adders.push_back( { data_i, data_j } ); + } + } + } + + void match_adders() + { + half_adders.reserve( cuts_classes.size() ); + full_adders.reserve( cuts_classes.size() ); + ntk.clear_values(); + + for ( auto& it : cuts_classes ) + { + /* not matched */ + if ( it.second.size() < 2 ) + continue; + + /* half adder */ + if ( it.first[2] == 0 ) + { + match_adder2( it ); + continue; + } + + for ( uint32_t i = 0; i < it.second.size() - 1; ++i ) + { + uint64_t data_i = it.second[i]; + uint32_t index_i = data_i >> 16; + uint32_t cut_index_i = data_i & UINT16_MAX; + auto const& cut_i = cuts.cuts( index_i )[cut_index_i]; + + /* TODO: find unique matches */ + for ( uint32_t j = i + 1; j < it.second.size(); ++j ) + { + uint64_t data_j = it.second[j]; + uint32_t index_j = data_j >> 16; + uint32_t cut_index_j = data_j & UINT16_MAX; + auto const& cut_j = cuts.cuts( index_j )[cut_index_j]; + + /* not compatible */ + if ( cut_i->data.is_xor == cut_j->data.is_xor ) + continue; + + /* check compatibility */ + if ( !check_adder( index_i, index_j, cut_i ) ) + continue; + + assert( cut_i.size() == 3 ); + assert( cut_j.size() == 3 ); + + full_adders.push_back( { data_i, data_j } ); + } + } + } + } + + void map() + { + selected.reserve( full_adders.size() + half_adders.size() ); + + ntk.incr_trav_id(); + + for ( uint32_t i = 0; i < full_adders.size(); ++i ) + { + auto& pair = full_adders[i]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + cut_t const& cut = cuts.cuts( index1 )[cut_index1]; + + /* remove overlapping multi-output gates */ + if ( !gate_mark( index1, index2, cut ) ) + continue; + + selected.push_back( 2 * i ); + node_match[std::max( index1, index2 )] = 2 * i; + node_match[std::min( index1, index2 )] = UINT32_MAX - 1; + + ++st.mapped_fa; + } + + for ( uint32_t i = 0; i < half_adders.size(); ++i ) + { + auto& pair = half_adders[i]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + cut_t const& cut = cuts.cuts( index1 )[cut_index1]; + + if ( !gate_mark( index1, index2, cut ) ) + continue; + + selected.push_back( 2 * i + 1 ); + node_match[std::max( index1, index2 )] = 2 * i + 1; + node_match[std::min( index1, index2 )] = UINT32_MAX - 1; + + ++st.mapped_ha; + } + } + + void topo_sort() + { + topo_order.reserve( ntk.size() ); + + /* add map choices */ + choice_view choice_ntk{ ntk }; + add_choices( choice_ntk ); + + ntk.incr_trav_id(); + ntk.incr_trav_id(); + + /* add constants and CIs */ + const auto c0 = ntk.get_node( ntk.get_constant( false ) ); + ntk.set_visited( c0, ntk.trav_id() ); + + if ( const auto c1 = ntk.get_node( ntk.get_constant( true ) ); ntk.visited( c1 ) != ntk.trav_id() ) + { + ntk.set_visited( c1, ntk.trav_id() ); + } + + ntk.foreach_ci( [&]( auto const& n ) { + if ( ntk.visited( n ) != ntk.trav_id() ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + } ); + + /* sort topologically */ + ntk.foreach_co( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + return; + topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + } + + void add_choices( choice_view& choice_ntk ) + { + for ( uint32_t index : selected ) + { + auto& pair = ( index & 1 ) ? half_adders[index >> 1] : full_adders[index >> 1]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + + if ( index1 > index2 ) + std::swap( index1, index2 ); + + choice_ntk.add_choice( ntk.index_to_node( index1 ), ntk.index_to_node( index2 ) ); + + assert( choice_ntk.count_choices( ntk.index_to_node( index1 ) ) == 2 ); + } + } + + inline bool check_adder( uint32_t index1, uint32_t index2, cut_t const& cut ) + { + bool valid = true; + + /* check containment of cut1 in cut2 and vice versa */ + if ( index1 > index2 ) + { + std::swap( index1, index2 ); + } + + ntk.foreach_fanin( ntk.index_to_node( index2 ), [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.node_to_index( g ) == index1 && ntk.fanout_size( g ) == 1 ) + { + valid = false; + } + return valid; + } ); + + if ( !valid ) + return false; + + /* check containment when node is reachable from middle nodes with multiple fanouts */ + return check_adder_tfi_valid( ntk.index_to_node( index2 ), ntk.index_to_node( index1 ), cut ); + } + + inline bool gate_mark( uint32_t index1, uint32_t index2, cut_t const& cut ) + { + bool contained = false; + + /* mark leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + contained = mark_visited_rec( ntk.index_to_node( index1 ) ); + contained |= mark_visited_rec( ntk.index_to_node( index2 ) ); + + if ( contained ) + { + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + return false; + } + + /* mark*/ + mark_visited_rec( ntk.index_to_node( index1 ) ); + mark_visited_rec( ntk.index_to_node( index2 ) ); + + /* unmark leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return true; + } + + template + bool mark_visited_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return true; + + if constexpr ( MARK ) + { + ntk.set_visited( n, ntk.trav_id() ); + } + + bool contained = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + contained |= mark_visited_rec( ntk.get_node( f ) ); + + if constexpr ( !MARK ) + { + if ( contained ) + return false; + } + + return true; + } ); + + return contained; + } + + inline bool check_adder_tfi_valid( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + bool valid = true; + if ( ps.use_mffc_filter ) + { + tmp_visited.clear(); + dereference_node_rec( root ); + + if ( ntk.fanout_size( n ) == 0 ) + valid = false; + + for ( auto g : tmp_visited ) + ntk.incr_fanout_size( g ); + } + else + { + ntk.incr_trav_id(); + check_adder_tfi_valid_rec( root, root, n, valid ); + } + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return valid; + } + + bool check_adder_tfi_valid_rec( node const& n, node const& root, node const& target, bool& valid ) + { + /* leaf */ + if ( ntk.value( n ) ) + return false; + + /* already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return false; + + ntk.set_visited( n, ntk.trav_id() ); + + if ( n == target ) + return true; + + bool found = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + found |= check_adder_tfi_valid_rec( ntk.get_node( f ), root, target, valid ); + return valid; + } ); + + if ( found && n != root && ntk.fanout_size( n ) > 1 ) + valid = false; + + return found; + } + + void dereference_node_rec( node const& n ) + { + /* leaf */ + if ( ntk.value( n ) ) + return; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + node g = ntk.get_node( f ); + if ( ntk.decr_fanout_size( g ) == 0 ) + { + dereference_node_rec( g ); + } + tmp_visited.push_back( g ); + } ); + } + + inline bool is_in_tfi( node const& root, node const& n, cut_t const& cut ) + { + /* reference cut leaves */ + for ( auto leaf : cut ) + { + ntk.incr_value( ntk.index_to_node( leaf ) ); + } + + ntk.incr_trav_id(); + mark_visited_rec( root ); + bool contained = ntk.visited( n ) == ntk.trav_id(); + + /* dereference leaves */ + for ( auto leaf : cut ) + { + ntk.decr_value( ntk.index_to_node( leaf ) ); + } + + return contained; + } + + void topo_sort_rec( choice_view& choice_ntk, node const& n ) + { + /* is permanently marked? */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + /* get the representative (smallest index) */ + node repr = choice_ntk.get_choice_representative( n ); + + /* multioutput gate */ + if ( choice_ntk.count_choices( repr ) > 1 ) + { + /* get the cut */ + uint32_t max_index = 0; + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( g ) != ntk.trav_id() ); + assert( ntk.visited( g ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( g, ntk.trav_id() - 1 ); + + max_index = std::max( max_index, ntk.node_to_index( g ) ); + return true; + } ); + + uint32_t cindex = node_match[max_index]; + auto& pair = ( cindex & 1 ) ? half_adders[cindex >> 1] : full_adders[cindex >> 1]; + cut_t const& cut = cuts.cuts( pair.first >> 16 )[pair.first & UINT16_MAX]; + + for ( auto l : cut ) + { + topo_sort_rec( choice_ntk, ntk.index_to_node( l ) ); + } + + choice_ntk.foreach_choice( repr, [&]( auto const& g ) { + /* ensure that the node is not visited */ + assert( ntk.visited( g ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( g, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( g ); + + return true; + } ); + } + else + { + /* ensure that the node is not visited or temporarily marked */ + assert( ntk.visited( n ) != ntk.trav_id() ); + assert( ntk.visited( n ) != ntk.trav_id() - 1 ); + + /* mark node temporarily */ + ntk.set_visited( n, ntk.trav_id() - 1 ); + + /* mark cut leaves */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + topo_sort_rec( choice_ntk, ntk.get_node( f ) ); + } ); + + /* ensure that the node is not visited */ + assert( ntk.visited( n ) != ntk.trav_id() ); + + /* mark node n permanently */ + ntk.set_visited( n, ntk.trav_id() ); + + /* visit node */ + topo_order.push_back( n ); + } + } + + std::pair initialize_map_network() + { + block_network dest; + block_map old2new( ntk ); + + old2new[ntk.get_node( ntk.get_constant( false ) )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + old2new[ntk.get_node( ntk.get_constant( true ) )] = dest.get_constant( true ); + + ntk.foreach_ci( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + return { dest, old2new }; + } + + void finalize( block_network& res, block_map& old2new ) + { + for ( auto const& n : topo_order ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + continue; + + /* is a multioutput gate root? */ + if ( node_match[ntk.node_to_index( n )] == UINT32_MAX ) + { + finalize_simple_gate( res, old2new, n ); + } + else if ( node_match[ntk.node_to_index( n )] < UINT32_MAX - 1 ) + { + finalize_multi_gate( res, old2new, n ); + } + } + + /* create POs */ + ntk.foreach_co( [&]( auto const& f ) { + res.create_po( ntk.is_complemented( f ) ? !old2new[f] : old2new[f] ); + } ); + } + + inline void finalize_simple_gate( block_network& res, block_map& old2new, node const& n ) + { + kitty::dynamic_truth_table tt = ntk.node_function( n ); + + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + auto s = old2new[f] ^ ntk.is_complemented( f ); + children.push_back( s ); + } ); + + old2new[n] = res.create_node( children, tt ); + } + + inline void finalize_multi_gate( block_network& res, block_map& old2new, node const& n ) + { + uint32_t index = node_match[ntk.node_to_index( n )]; + assert( index < UINT32_MAX - 1 ); + + /* extract the match */ + if ( index & 1 ) + finalize_multi_gate_ha( res, old2new, n, index >> 1 ); + else + finalize_multi_gate_fa( res, old2new, n, index >> 1 ); + } + + inline void finalize_multi_gate_ha( block_network& res, block_map& old2new, node const& n, uint32_t index ) + { + auto& pair = half_adders[index]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + uint32_t cut_index2 = pair.second & UINT16_MAX; + cut_t const& cut1 = cuts.cuts( index1 )[cut_index1]; + cut_t const& cut2 = cuts.cuts( index2 )[cut_index2]; + + kitty::static_truth_table<3> tt1 = cuts.truth_table( cut1 ); + kitty::static_truth_table<3> tt2 = cuts.truth_table( cut2 ); + bool xor_is_1 = false; + + /* find the XOR2 */ + xor_is_1 = cut1->data.is_xor; + + /* find the negation vector of AND2 and XOR2*/ + kitty::static_truth_table<3> tt = xor_is_1 ? tt2 : tt1; + uint32_t neg_and = 0; + for ( uint32_t func : and2func ) + { + if ( tt._bits == func ) + break; + ++neg_and; + } + + tt = xor_is_1 ? tt1 : tt2; + uint32_t neg_xor = 0; + for ( uint32_t func : xor2func ) + { + if ( tt._bits == func ) + break; + ++neg_xor; + } + neg_xor ^= neg_and; + neg_xor = ( neg_xor & 1 ) ^ ( ( neg_xor >> 1 ) & 1 ) ^ ( ( neg_xor >> 2 ) & 1 ); + + /* normalize and create multioutput gate */ + std::array, 2> children; + uint32_t ctr = 0; + for ( auto l : cut1 ) + { + signal f = old2new[ntk.index_to_node( l )]; + bool phase = ( ( neg_and >> ctr ) & 1 ) ? true : false; + children[ctr] = f ^ phase; + ++ctr; + } + + if ( ps.map_inverted ) + { + signal ha = res.create_hai( children[0], children[1] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = ha ^ ( ( neg_and >> 2 ) ? false : true ); + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( ha ) ^ ( neg_xor ? false : true ); + return; + } + + signal ha = res.create_ha( children[0], children[1] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = ha ^ ( ( neg_and >> 2 ) ? true : false ); + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( ha ) ^ ( neg_xor ? true : false ); + } + + inline void finalize_multi_gate_fa( block_network& res, block_map& old2new, node const& n, uint32_t index ) + { + auto& pair = full_adders[index]; + uint32_t index1 = pair.first >> 16; + uint32_t index2 = pair.second >> 16; + uint32_t cut_index1 = pair.first & UINT16_MAX; + uint32_t cut_index2 = pair.second & UINT16_MAX; + cut_t const& cut1 = cuts.cuts( index1 )[cut_index1]; + cut_t const& cut2 = cuts.cuts( index2 )[cut_index2]; + + kitty::static_truth_table<3> tt1 = cuts.truth_table( cut1 ); + kitty::static_truth_table<3> tt2 = cuts.truth_table( cut2 ); + + bool xor_is_1 = false; + + /* find the XOR3 */ + xor_is_1 = cut1->data.is_xor; + + /* find the phase and permutation of MAJ3 and XOR3*/ + kitty::static_truth_table<3> tt = xor_is_1 ? tt2 : tt1; + uint32_t neg_maj = 0; + for ( uint32_t func : maj3func ) + { + if ( tt._bits == func ) + break; + ++neg_maj; + } + + if ( ps.map_inverted ) + { + neg_maj = ( ~neg_maj ) & 0x7; + } + + tt = xor_is_1 ? tt1 : tt2; + uint32_t neg_xor = 0; + for ( uint32_t func : xor3func ) + { + if ( tt._bits == func ) + break; + ++neg_xor; + } + neg_xor ^= neg_maj; + neg_xor = ( neg_xor & 1 ) ^ ( ( neg_xor >> 1 ) & 1 ) ^ ( ( neg_xor >> 2 ) & 1 ); + + /* normalize and create the multioutput gate */ + std::array, 3> children; + uint32_t ctr = 0; + for ( auto l : cut1 ) + { + signal f = old2new[ntk.index_to_node( l )]; + bool phase = ( ( neg_maj >> ctr ) & 1 ) ? true : false; + children[ctr] = f ^ phase; + ++ctr; + } + + if ( ps.map_inverted ) + { + signal fa = res.create_fai( children[0], children[1], children[2] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = fa; + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( fa ) ^ ( neg_xor ? false : true ); + return; + } + + signal fa = res.create_fa( children[0], children[1], children[2] ); + old2new[ntk.index_to_node( xor_is_1 ? index2 : index1 )] = fa; + old2new[ntk.index_to_node( xor_is_1 ? index1 : index2 )] = res.next_output_pin( fa ) ^ ( neg_xor ? true : false ); + } + +private: + Ntk& ntk; + extract_adders_params const& ps; + extract_adders_stats& st; + + network_cuts_t cuts; + leaves_hash_t cuts_classes; + matches_t half_adders; + matches_t full_adders; + std::vector selected; + std::vector node_match; + + std::vector> topo_order; + std::vector> tmp_visited; + + const std::array and2func = { 0x88, 0x44, 0x22, 0x11, 0x77, 0xbb, 0xdd, 0xee }; + const std::array maj3func = { 0xe8, 0xd4, 0xb2, 0x71, 0x8e, 0x4d, 0x2b, 0x17 }; + const std::array xor2func = { 0x66, 0x99 }; + const std::array xor3func = { 0x96, 0x69 }; +}; + +} /* namespace detail */ + +/*! \brief Adders extraction. + * + * This function extracts half and full adders from a network. + * It returns a `block_network` with extracted half and full adder + * blocks. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `foreach_gate` + * + * \param ntk Network + * \param ps Parameters + * \param pst Stats + * + */ +template +block_network extract_adders( Ntk& ntk, extract_adders_params const& ps = {}, extract_adders_stats* pst = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + + extract_adders_stats st; + + detail::extract_adders_impl p( ntk, ps, st ); + block_network res = p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + *pst = st; + + return res; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/extract_linear.hpp b/include/mockturtle/algorithms/extract_linear.hpp new file mode 100644 index 0000000..72b2611 --- /dev/null +++ b/include/mockturtle/algorithms/extract_linear.hpp @@ -0,0 +1,208 @@ +/* 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 extract_linear.hpp + \brief Extract linear subcircuit in XAGs + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../networks/xag.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Extract linear circuit from XAG + * + * Creates a new XAG that only contains the XOR gates of the original XAG. For + * each AND gate, the new XAG will contain one additional PI (for the AND + * output) and two additional POs (for the AND inputs) in the same order as the + * AND gates are traversed in topological order. + * + * Besides the new XAG, this function returns a vector of the size of all + * original AND gates with pointers to the signals referring to the AND's fanin + * and fanout (in that order). + */ +inline std::pair>> +extract_linear_circuit( xag_network const& xag ) +{ + xag_network dest; + std::vector> and_tuples; + node_map old_to_new( xag ); + + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + xag.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + topo_view topo{ xag }; + topo.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + if ( xag.is_and( n ) ) + { + std::array signal_tuple; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + signal_tuple[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + const auto and_pi = dest.create_pi(); + old_to_new[n] = and_pi; + signal_tuple[2] = and_pi; + and_tuples.push_back( signal_tuple ); + } + else /* if ( xag.is_xor( n ) ) */ + { + std::array children{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + old_to_new[n] = dest.create_xor( children[0], children[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xag.is_complemented( f ) ); + } ); + for ( auto const& [a, b, _] : and_tuples ) + { + (void)_; + dest.create_po( a ); + dest.create_po( b ); + } + + return { dest, and_tuples }; +} + +namespace detail +{ + +struct merge_linear_circuit_impl +{ +public: + merge_linear_circuit_impl( xag_network const& xag, uint32_t num_and_gates ) + : xag( xag ), + num_and_gates( num_and_gates ), + old_to_new( xag ), + and_pi( xag ) + { + } + + xag_network run() + { + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + + orig_pis = xag.num_pis() - num_and_gates; + orig_pos = xag.num_pos() - 2 * num_and_gates; + + xag.foreach_pi( [&]( auto const& n, auto i ) { + if ( i == orig_pis ) + return false; + + old_to_new[n] = dest.create_pi(); + return true; + } ); + + for ( auto i = 0u; i < num_and_gates; ++i ) + { + create_and( i ); + } + + xag.foreach_po( [&]( auto const& f, auto i ) { + if ( i == orig_pos ) + return false; + + dest.create_po( run_rec( xag.get_node( f ) ) ^ xag.is_complemented( f ) ); + return true; + } ); + + return dest; + } + +private: + xag_network::signal create_and( uint32_t index ) + { + if ( old_to_new.has( xag.pi_at( orig_pis + index ) ) ) + { + return old_to_new[xag.pi_at( orig_pis + index )]; + } + + const auto f1 = xag.po_at( orig_pos + 2u * index ); + const auto f2 = xag.po_at( orig_pos + 2u * index + 1u ); + const auto c1 = run_rec( xag.get_node( f1 ) ) ^ xag.is_complemented( f1 ); + const auto c2 = run_rec( xag.get_node( f2 ) ) ^ xag.is_complemented( f2 ); + return old_to_new[xag.pi_at( orig_pis + index )] = dest.create_and( c1, c2 ); + } + + xag_network::signal run_rec( xag_network::node const& n ) + { + if ( old_to_new.has( n ) ) + { + return old_to_new[n]; + } + + assert( xag.is_xor( n ) ); + std::array children{}; + xag.foreach_fanin( n, [&]( auto const& cf, auto i ) { + children[i] = run_rec( xag.get_node( cf ) ) ^ xag.is_complemented( cf ); + } ); + return old_to_new[n] = dest.create_xor( children[0], children[1] ); + } + +private: + xag_network dest; + xag_network const& xag; + uint32_t num_and_gates; + uint32_t orig_pis, orig_pos; + unordered_node_map old_to_new; + unordered_node_map and_pi; +}; + +} // namespace detail + +/*! \brief Re-insert AND gates in linear circuit + * + * Given an extracted linear circuit from `extract_linear_circuit` and the + * number of original AND gates, this function re-inserts the AND gates, + * assuming that they are represented as PI and PO pairs at the end of the + * original PIs and POs. + */ +inline xag_network merge_linear_circuit( xag_network const& xag, uint32_t num_and_gates ) +{ + return detail::merge_linear_circuit_impl( xag, num_and_gates ).run(); +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/functional_reduction.hpp b/include/mockturtle/algorithms/functional_reduction.hpp new file mode 100644 index 0000000..5649d78 --- /dev/null +++ b/include/mockturtle/algorithms/functional_reduction.hpp @@ -0,0 +1,488 @@ +/* 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 functional_reduction.hpp + \brief Functional reduction for any network type + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include + +#include "../io/write_patterns.hpp" +#include "circuit_validator.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +struct functional_reduction_params +{ + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Maximum number of iterations to run. 0 = repeat until no further improvement can be found. */ + uint32_t max_iterations{ 10 }; + + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 256 random patterns will be used. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of nodes in the transitive fanin cone (and their fanouts) to be compared to. */ + uint32_t max_TFI_nodes{ 1000 }; + + /*! \brief Maximum fanout count of a node in the transitive fanin cone to explore its fanouts. */ + uint32_t skip_fanout_limit{ 100 }; + + /*! \brief Conflict limit for the SAT solver. */ + uint32_t conflict_limit{ 100 }; + + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Initial number of (random) simulation patterns. */ + uint32_t num_patterns{ 256 }; + + /*! \brief Maximum number of simulation patterns. Discards all patterns and re-seeds with random patterns when exceeded. */ + uint32_t max_patterns{ 1024 }; +}; + +struct functional_reduction_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Number of accepted constant nodes. */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted functionally equivalent nodes. */ + uint32_t num_equ_accepts{ 0 }; + + /*! \brief Number of counter-examples (SAT calls). */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of successful node reductions (UNSAT calls). */ + uint32_t num_reduction{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + void report() const + { + // clang-format off + std::cout << "[i] Functional Reduction\n"; + std::cout << "[i] ======== Stats ========\n"; + std::cout << fmt::format( "[i] #constant = {:8d}\n", num_const_accepts ); + std::cout << fmt::format( "[i] #FE pairs = {:8d}\n", num_equ_accepts ); + std::cout << fmt::format( "[i] #SAT = {:8d}\n", num_cex ); + std::cout << fmt::format( "[i] #UNSAT = {:8d}\n", num_reduction ); + std::cout << fmt::format( "[i] #TIMEOUT = {:8d}\n", num_timeout ); + std::cout << "[i] ======== Runtime ========\n"; + std::cout << fmt::format( "[i] total : {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + std::cout << fmt::format( "[i] SAT solving: {:>5.2f} secs\n", to_seconds( time_sat ) ); + std::cout << "[i] =========================\n\n"; + // clang-format on + } +}; + +namespace detail +{ +template> +class functional_reduction_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = unordered_node_map; + + explicit functional_reduction_impl( Ntk& ntk, functional_reduction_params const& ps, validator_params const& vps, functional_reduction_stats& st ) + : ntk( ntk ), ps( ps ), st( st ), tts( ntk ), + sim( ps.pattern_filename ? partial_simulator( *ps.pattern_filename ) : partial_simulator( ntk.num_pis(), ps.num_patterns, std::rand() ) ), validator( ntk, vps ) + { + static_assert( !validator_t::use_odc_, "`circuit_validator::use_odc` flag should be turned off." ); + } + + ~functional_reduction_impl() + { + if ( ps.save_patterns ) + { + write_patterns( sim, *ps.save_patterns ); + } + } + + void run() + { + stopwatch t( st.time_total ); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + + /* remove constant nodes. */ + substitute_constants(); + + /* substitute functional equivalent nodes. */ + auto size_before = ntk.size(); + substitute_equivalent_nodes(); + uint32_t iterations{0}; + while ( ps.max_iterations && iterations++ <= ps.max_iterations && ntk.size() != size_before ) + { + size_before = ntk.size(); + substitute_equivalent_nodes(); + } + } + +private: + void substitute_constants() + { + progress_bar pbar{ ntk.size(), "FR-const |{0}| node = {1:>4} cand = {2:>4}", ps.progress }; + + auto zero = sim.compute_constant( false ); + auto one = sim.compute_constant( true ); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, candidates ); + + check_tts( n ); + bool const_value; + if ( tts[n] == zero ) + { + const_value = false; + } + else if ( tts[n] == one ) + { + const_value = true; + } + else /* not constant */ + { + return true; /* next */ + } + + /* update progress bar */ + candidates++; + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( n, const_value ); + } ); + if ( !res ) /* timeout */ + { + ++st.num_timeout; + return true; + } + else if ( !( *res ) ) /* SAT, cex found */ + { + found_cex(); + zero = sim.compute_constant( false ); + one = sim.compute_constant( true ); + } + else /* UNSAT, constant verified */ + { + ++st.num_reduction; + ++st.num_const_accepts; + /* update network */ + ntk.substitute_node( n, ntk.get_constant( const_value ) ); + } + return true; + } ); + } + + void substitute_equivalent_nodes() + { + progress_bar pbar{ ntk.size(), "FR-equ |{0}| node = {1:>4} cand = {2:>4}", ps.progress }; + ntk.foreach_gate( [&]( auto const& root, auto i ) { + pbar( i, i, candidates ); + + check_tts( root ); + auto tt = tts[root]; + auto ntt = ~tts[root]; + std::vector tfi; + bool keep_trying = true; + foreach_transitive_fanin( root, [&]( auto const& n ) { + tfi.emplace_back( n ); + if ( tfi.size() > ps.max_TFI_nodes ) + { + return false; + } + + keep_trying = try_node( tt, ntt, root, n ); + return keep_trying; + } ); + + if ( keep_trying ) /* didn't find a substitution in TFI cone, explore fanouts. */ + { + for ( auto j = 0u; j < tfi.size() && tfi.size() <= ps.max_TFI_nodes && keep_trying; ++j ) + { + auto& n = tfi.at( j ); + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit ) + { + continue; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( n, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() ) + { + return true; /* next fanout */ + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + if ( !all_fanins_visited ) + { + return true; /* next fanout */ + } + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + tfi.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + check_tts( p ); + keep_trying = try_node( tt, ntt, root, p ); + return keep_trying; + } ); + } + } + + return true; /* next */ + } ); + } + + bool try_node( kitty::partial_truth_table& tt, kitty::partial_truth_table& ntt, node const& root, node const& n ) + { + signal g; + if ( tt == tts[n] ) + { + g = ntk.make_signal( n ); + } + else if ( ntt == tts[n] ) + { + g = !ntk.make_signal( n ); + } + else /* not equivalent */ + { + return true; /* try next transitive fanin node */ + } + + /* update progress bar */ + candidates++; + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( root, g ); + } ); + if ( !res ) /* timeout */ + { + ++st.num_timeout; + return true; /* try next transitive fanin node */ + } + else if ( !( *res ) ) /* SAT, cex found */ + { + found_cex(); + check_tts( root ); + tt = tts[root]; + ntt = ~tts[root]; + return true; /* try next transitive fanin node */ + } + else /* UNSAT, equivalent node verified */ + { + ++st.num_reduction; + ++st.num_equ_accepts; + /* update network */ + ntk.substitute_node( root, g ); + return false; /* break `foreach_transitive_fanin` */ + } + } + + void found_cex() + { + ++st.num_cex; + sim.add_pattern( validator.cex ); + + if ( sim.num_bits() > ps.max_patterns ) + { + reseed_patterns(); + return; + } + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + + void reseed_patterns() + { + sim = partial_simulator( ntk.num_pis(), ps.num_patterns, std::rand() ); + tts.reset(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + template + void foreach_transitive_fanin( node const& n, Fn&& fn ) + { + ntk.incr_trav_id(); + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + return foreach_transitive_fanin_rec( ntk.get_node( f ), fn ); + } ); + } + + template + bool foreach_transitive_fanin_rec( node const& n, Fn&& fn ) + { + ntk.set_visited( n, ntk.trav_id() ); + if ( !fn( n ) ) + { + return false; + } + bool continue_loop = true; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + return true; + } /* skip visited node, continue looping. */ + + continue_loop = foreach_transitive_fanin_rec( ntk.get_node( f ), fn ); + return continue_loop; /* break `foreach_fanin` loop immediately when receiving `false`. */ + } ); + return continue_loop; /* return `false` only if `false` has ever been received from recursive calls. */ + } + +private: + Ntk& ntk; + functional_reduction_params const& ps; + functional_reduction_stats& st; + + TT tts; + partial_simulator sim; + validator_t validator; + + uint32_t candidates{ 0 }; +}; /* functional_reduction_impl */ + +} /* namespace detail */ + +/*! \brief Functional reduction. + * + * Removes constant nodes and substitute functionally equivalent nodes. + */ +template +void functional_reduction( Ntk& ntk, functional_reduction_params const& ps = {}, functional_reduction_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_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + validator_params vps; + vps.max_clauses = ps.max_clauses; + vps.conflict_limit = ps.conflict_limit; + + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + functional_reduction_stats st; + detail::functional_reduction_impl p( fanout_view, ps, vps, st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/gates_to_nodes.hpp b/include/mockturtle/algorithms/gates_to_nodes.hpp new file mode 100644 index 0000000..6dc6541 --- /dev/null +++ b/include/mockturtle/algorithms/gates_to_nodes.hpp @@ -0,0 +1,174 @@ +/* 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 gates_to_nodes.hpp + \brief Convert gate-based network into node-based network + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Translates a gate-based network into a node-based network. + * + * A node will be created in the node-based network for every gate based on the + * gate function. Possible complemented fanins are merged into the node + * function. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `foreach_pi` + * - `foreach_gate` + * - `foreach_fanin` + * - `get_constant` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `node_function` + * + * **Required network functions for return value (type NtkDest):** + * - `create_pi` + * - `create_po` + * - `create_node` + * - `create_not` + * - `get_constant` + * + * \param ntk Network + */ +template +NtkDest gates_to_nodes( NtkSource const& ntk ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "NtkSource does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + + NtkDest dest; + node_map, NtkSource> node_to_signal( ntk ); + + ntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = dest.create_pi(); + } ); + + node_to_signal[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_signal[ntk.get_constant( true )] = dest.get_constant( true ); + } + + ntk.foreach_gate( [&]( auto const& n ) { + std::vector> children; + auto func = ntk.node_function( n ); + ntk.foreach_fanin( n, [&]( auto const& c, auto i ) { + if ( ntk.is_complemented( c ) ) + { + kitty::flip_inplace( func, i ); + } + children.push_back( node_to_signal[c] ); + } ); + + node_to_signal[n] = dest.create_node( children, func ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& s ) { + dest.create_po( ntk.is_complemented( s ) ? dest.create_not( node_to_signal[s] ) : node_to_signal[s] ); + } ); + + return dest; +} + +/*! \brief Creates a new network with a single node per output. + * + * This method can be applied to networks with a small number of primary inputs, + * to collapse all the logic of an output into a single node. The returning + * network must support arbitrary node functions, e.g., `klut_network`. + */ +template +NtkDest single_node_network( NtkSrc const& src ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_node_v, "NtkDest does not implement the create_node method" ); + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( has_num_pis_v, "NtkSrc does not implement the num_pis method" ); + + NtkDest ntk; + std::vector> pis( src.num_pis() ); + std::generate( pis.begin(), pis.end(), [&]() { return ntk.create_pi(); } ); + + default_simulator sim( src.num_pis() ); + const auto tts = simulate( src, sim ); + + for ( auto tt : tts ) + { + const auto support = kitty::min_base_inplace( tt ); + const auto small_tt = kitty::shrink_to( tt, static_cast( support.size() ) ); + std::vector> children( support.size() ); + for ( auto i = 0u; i < support.size(); ++i ) + { + children[i] = pis[support[i]]; + } + ntk.create_po( ntk.create_node( children, small_tt ) ); + } + + return ntk; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/klut_to_graph.hpp b/include/mockturtle/algorithms/klut_to_graph.hpp new file mode 100644 index 0000000..4f82385 --- /dev/null +++ b/include/mockturtle/algorithms/klut_to_graph.hpp @@ -0,0 +1,131 @@ +/* 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 klut_to_graph.hpp + \brief Convert a k-LUT network into AIG, XAG, MIG or XMG. + + \author Andrea Costamagna +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../networks/xag.hpp" +#include "../networks/xmg.hpp" +#include "node_resynthesis.hpp" +#include "node_resynthesis/dsd.hpp" +#include "node_resynthesis/mig_npn.hpp" +#include "node_resynthesis/shannon.hpp" +#include "node_resynthesis/xag_npn.hpp" +#include "node_resynthesis/xmg_npn.hpp" + +namespace mockturtle +{ +namespace detail +{ + +/* declare the npn-resynthesis function to be used depending on the desired network type. */ +template +const auto set_npn_resynthesis_fn() +{ + using aig_npn_type = xag_npn_resynthesis; + using xag_npn_type = xag_npn_resynthesis; + using mig_npn_type = mig_npn_resynthesis; + using xmg_npn_type = xmg_npn_resynthesis; + + if constexpr ( std::is_same_v ) + return aig_npn_type{}; + else if constexpr ( std::is_same_v ) + return xag_npn_type{}; + else if constexpr ( std::is_same_v ) + return mig_npn_type{}; + else if constexpr ( std::is_same_v ) + return xmg_npn_type{}; +} + +} // namespace detail + +/*! \brief Convert a k-LUT network into AIG, XAG, MIG or XMG (out-of-place) + * + * This function is a wrapper function for resynthesizing a k-LUT network (type `NtkSrc`) into a + * new graph (of type `NtkDest`). The new data structure can be of type AIG, XAG, MIG or XMG. + * First the function attempts a Disjoint Support Decomposition (DSD), branching the network into subnetworks. + * As soon as DSD can no longer be done, there are two possibilities depending on the dimensionality of the + * subnetwork to be resynthesized. On the one hand, if the size of the associated support is lower or equal + * than 4, the solution can be recovered by exploiting the mapping of the subnetwork to its NPN-class. + * On the other hand, if the support size is higher than 4, A Shannon decomposition is performed, branching + * the network in further subnetworks with reduced support. + * Finally, once the threshold value of 4 is reached, the NPN mapping completes the graph definition. + * + * \tparam NtkDest Type of the destination network. Its base type should be either `aig_network`, `xag_network`, `mig_network`, or `xmg_network`. + * \tparam NtkSrc Type of the source network. Its base type should be `klut_network`. + * \param ntk_src Input k-lut network + * \return An equivalent AIG, XAG, MIG or XMG network + */ +template +NtkDest convert_klut_to_graph( NtkSrc const& ntk_src ) +{ + using NtkDestBase = typename NtkDest::base_type; + static_assert( std::is_same_v, "NtkSrc is not klut_network" ); + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "NtkDest is not an AIG, XAG, MIG, or XMG" ); + + uint32_t threshold{ 4 }; + auto fallback_npn = detail::set_npn_resynthesis_fn(); + shannon_resynthesis fallback_shannon( threshold, &fallback_npn ); + dsd_resynthesis resyn( fallback_shannon ); + return node_resynthesis( ntk_src, resyn ); +} + +/*! \brief Convert a k-LUT network into AIG, XAG, MIG or XMG (in-place) + * + * The algorithmic details are the same as the out-of-place version. + * + * \tparam NtkDest Type of the destination network. Its base type should be either `aig_network`, `xag_network`, `mig_network`, or `xmg_network`. + * \tparam NtkSrc Type of the source network. Its base type should be `klut_network`. + * \param ntk_dest An empty AIG, XAG, MIG or XMG network to be constructed in-place + * \param ntk_src Input k-lut network + */ +template +void convert_klut_to_graph( NtkDest& ntk_dest, NtkSrc const& ntk_src ) +{ + using NtkDestBase = typename NtkDest::base_type; + static_assert( std::is_same_v, "NtkSrc is not klut_network" ); + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "NtkDest is not an AIG, XAG, MIG, or XMG" ); + + uint32_t threshold{ 4 }; + auto fallback_npn = detail::set_npn_resynthesis_fn(); + shannon_resynthesis fallback_shannon( threshold, &fallback_npn ); + dsd_resynthesis resyn( fallback_shannon ); + node_resynthesis( ntk_dest, ntk_src, resyn ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/linear_resynthesis.hpp b/include/mockturtle/algorithms/linear_resynthesis.hpp new file mode 100644 index 0000000..a0e08de --- /dev/null +++ b/include/mockturtle/algorithms/linear_resynthesis.hpp @@ -0,0 +1,1008 @@ +/* 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 linear_resynthesis.hpp + \brief Resynthesize linear circuit + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../algorithms/simulation.hpp" +#include "../networks/xag.hpp" +#include "../traits.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cnf_view.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +class linear_sum_simulator +{ +public: + std::vector compute_constant( bool ) const { return {}; } + std::vector compute_pi( uint32_t index ) const { return { index }; } + std::vector compute_not( std::vector const& value ) const + { + assert( false && "No NOTs in linear forms allowed" ); + std::abort(); + return value; + } +}; + +class linear_matrix_simulator +{ +public: + linear_matrix_simulator( uint32_t num_inputs ) : num_inputs_( num_inputs ) {} + + std::vector compute_constant( bool ) const { return std::vector( num_inputs_, false ); } + std::vector compute_pi( uint32_t index ) const + { + std::vector row( num_inputs_, false ); + row[index] = true; + return row; + } + std::vector compute_not( std::vector const& value ) const + { + assert( false && "No NOTs in linear forms allowed" ); + std::abort(); + return value; + } + +private: + uint32_t num_inputs_; +}; + +class linear_xag : public xag_network +{ +public: + linear_xag( xag_network const& xag ) : xag_network( xag ) {} + + template + iterates_over_t> + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto set1 = *begin++; + auto set2 = *begin++; + + if ( c1.index < c2.index ) + { + assert( false ); + std::abort(); + return {}; + } + else + { + std::vector result; + auto it1 = set1.begin(); + auto it2 = set2.begin(); + + while ( it1 != set1.end() && it2 != set2.end() ) + { + if ( *it1 < *it2 ) + { + result.push_back( *it1++ ); + } + else if ( *it1 > *it2 ) + { + result.push_back( *it2++ ); + } + else + { + ++it1; + ++it2; + } + } + + if ( it1 != set1.end() ) + { + std::copy( it1, set1.end(), std::back_inserter( result ) ); + } + else if ( it2 != set2.end() ) + { + std::copy( it2, set2.end(), std::back_inserter( result ) ); + } + + return result; + } + } + + template + iterates_over_t> + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto set1 = *begin++; + auto set2 = *begin++; + + if ( c1.index < c2.index ) + { + assert( false ); + std::abort(); + return {}; + } + else + { + std::vector result( set1.size() ); + std::transform( set1.begin(), set1.end(), set2.begin(), result.begin(), std::not_equal_to{} ); + return result; + } + } +}; + +struct pair_hash +{ + template + std::size_t operator()( std::pair const& p ) const + { + return std::hash()( p.first ) ^ std::hash()( p.second ); + } +}; + +template +struct linear_resynthesis_paar_impl +{ +public: + using index_pair_t = std::pair; + + linear_resynthesis_paar_impl( Ntk const& xag ) : xag( xag ) {} + + Ntk run() + { + xag.foreach_pi( [&]( auto const& ) { + signals.push_back( dest.create_pi() ); + } ); + + extract_linear_equations(); + + while ( !occurrence_to_pairs.empty() ) + { + const auto p = *( occurrence_to_pairs.back().begin() ); + replace_one_pair( p ); + } + + xag.foreach_po( [&]( auto const& f, auto i ) { + if ( linear_equations[i].empty() ) + { + dest.create_po( dest.get_constant( xag.is_complemented( f ) ) ); + } + else + { + assert( linear_equations[i].size() == 1u ); + dest.create_po( signals[linear_equations[i].front()] ^ xag.is_complemented( f ) ); + } + } ); + + return dest; + } + +private: + void extract_linear_equations() + { + occurrence_to_pairs.resize( 1u ); + + linear_xag lxag{ xag }; + linear_equations = simulate>( lxag, linear_sum_simulator{} ); + + for ( auto o = 0u; o < linear_equations.size(); ++o ) + { + const auto& lin_eq = linear_equations[o]; + for ( auto j = 1u; j < lin_eq.size(); ++j ) + { + for ( auto i = 0u; i < j; ++i ) + { + const auto p = std::make_pair( lin_eq[i], lin_eq[j] ); + pairs_to_output[p].push_back( o ); + add_pair( p ); + } + } + } + } + + void add_pair( index_pair_t const& p ) + { + if ( auto it = pair_to_occurrence.find( p ); it != pair_to_occurrence.end() ) + { + // found another time + const auto occ = it->second; + occurrence_to_pairs[occ - 1u].erase( p ); + if ( occurrence_to_pairs.size() <= occ + 1u ) + { + occurrence_to_pairs.resize( occ + 1u ); + } + occurrence_to_pairs[occ].insert( p ); + it->second++; + } + else + { + // first time found + pair_to_occurrence[p] = 1u; + occurrence_to_pairs[0u].insert( p ); + } + } + + void remove_all_pairs( index_pair_t const& p ) + { + auto it = pair_to_occurrence.find( p ); + const auto occ = it->second; + pair_to_occurrence.erase( it ); + occurrence_to_pairs[occ - 1u].erase( p ); + while ( !occurrence_to_pairs.empty() && occurrence_to_pairs.back().empty() ) + { + occurrence_to_pairs.pop_back(); + } + pairs_to_output.erase( p ); + } + + void remove_one_pair( index_pair_t const& p, uint32_t output ) + { + auto it = pair_to_occurrence.find( p ); + const auto occ = it->second; + occurrence_to_pairs[occ - 1u].erase( p ); + if ( occ > 1u ) + { + occurrence_to_pairs[occ - 2u].insert( p ); + } + it->second--; + pairs_to_output[p].erase( std::remove( pairs_to_output[p].begin(), pairs_to_output[p].end(), output ), pairs_to_output[p].end() ); + } + + void replace_one_pair( index_pair_t const& p ) + { + const auto [a, b] = p; + auto c = static_cast( signals.size() ); + signals.push_back( dest.create_xor( signals[a], signals[b] ) ); + + /* update data structures */ + for ( auto o : pairs_to_output[p] ) + { + auto& leq = linear_equations[o]; + leq.erase( std::remove( leq.begin(), leq.end(), a ), leq.end() ); + leq.erase( std::remove( leq.begin(), leq.end(), b ), leq.end() ); + for ( auto i : leq ) + { + remove_one_pair( { std::min( i, a ), std::max( i, a ) }, o ); + remove_one_pair( { std::min( i, b ), std::max( i, b ) }, o ); + add_pair( { i, c } ); + pairs_to_output[{ i, c }].push_back( o ); + } + leq.push_back( c ); + } + remove_all_pairs( p ); + } + + void print_linear_matrix() + { + for ( auto const& le : linear_equations ) + { + auto it = le.begin(); + for ( auto i = 0u; i < signals.size(); ++i ) + { + if ( it != le.end() && *it == i ) + { + std::cout << " 1"; + it++; + } + else + { + std::cout << " 0"; + } + } + assert( it == le.end() ); + std::cout << "\n"; + } + } + +private: + Ntk const& xag; + Ntk dest; + std::vector> signals; + std::vector> linear_equations; + std::vector> occurrence_to_pairs; + std::unordered_map pair_to_occurrence; + std::unordered_map, pair_hash> pairs_to_output; +}; + +} // namespace detail + +/*! \brief Linear circuit resynthesis (Paar's algorithm) + * + * This algorithm works on an XAG that is only composed of XOR gates. It + * extracts a matrix representation of the linear output equations and + * resynthesizes them in a greedy manner by always substituting the most + * frequent pair of variables using the computed function of an XOR gate. + * + * Reference: [C. Paar, IEEE Int'l Symp. on Inf. Theo. (1997), page 250] + */ +template +Ntk linear_resynthesis_paar( Ntk const& xag ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + return detail::linear_resynthesis_paar_impl( xag ).run(); +} + +struct exact_linear_synthesis_params +{ + /*! \brief Upper bound on number of XOR gates. If used, best solution is found decreasing */ + std::optional upper_bound{}; + + /*! \brief Conflict limit for SAT solving (default 0 = no limit). */ + int conflict_limit{ 0 }; + + /*! \brief Solution must be cancellation-free. */ + bool cancellation_free{ false }; + + /*! \brief Ignore inputs in any step to compute this output. + * + * Either the vector is empty, if no inputs should be ignored, or it has as + * many entries as rows in the input matrix. Each entry is a vector of input + * indexes (starting from 0) to be ignored, an entry can be the empty vector, + * if no inputs should be ignored for some output. + */ + std::vector> ignore_inputs; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose (debug messages). */ + bool very_verbose{ false }; +}; + +struct exact_linear_synthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_solving{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + fmt::print( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] solving time = {:>5.2f} secs\n", to_seconds( time_solving ) ); + } +}; + +namespace detail +{ + +template +struct exact_linear_synthesis_problem_network +{ + using problem_network_t = cnf_view; + + exact_linear_synthesis_problem_network( uint32_t num_steps, std::vector> const& linear_matrix, std::vector> const& ignore_inputs, std::vector> const& trivial_pos, exact_linear_synthesis_params const& ps ) + : linear_matrix_( linear_matrix ), + k_( num_steps ), + n_( static_cast( linear_matrix.front().size() ) ), + m_( static_cast( linear_matrix.size() ) ), + bs_( k_ * n_ ), + cs_( ( ( k_ - 1 ) * k_ ) / 2 ), + fs_( k_ * m_ ), + psis_( k_ * n_ ), + phis_( k_ * n_ ), + ignore_inputs_( ignore_inputs ), + trivial_pos_( trivial_pos ), + ps_( ps ) + { + std::generate( bs_.begin(), bs_.end(), [&]() { return pntk_.create_pi(); } ); + std::generate( cs_.begin(), cs_.end(), [&]() { return pntk_.create_pi(); } ); + std::generate( fs_.begin(), fs_.end(), [&]() { return pntk_.create_pi(); } ); + + ensure_row_size2(); + ensure_connectivity(); + ensure_outputs(); + } + + std::optional solve() + { + return pntk_.solve( ps_.conflict_limit ); + } + + template + Ntk extract_solution() + { + Ntk ntk; + + std::vector> nodes( n_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return ntk.create_pi(); } ); + + for ( auto i = 0u; i < k_; ++i ) + { + std::array, 2> children; + auto it = children.begin(); + for ( auto j = 0u; j < n_ + i; ++j ) + { + if ( pntk_.model_value( b_or_c( i, j ) ) ) + { + *it++ = nodes[j]; + } + } + nodes.push_back( ntk.create_xor( children[0], children[1] ) ); + } + + auto it = trivial_pos_.begin(); + auto poctr = 0u; + for ( auto l = 0u; l < m_; ++l ) + { + while ( it != trivial_pos_.end() && it->first == poctr ) + { + ntk.create_po( it->second == n_ ? ntk.get_constant( false ) : nodes[it->second] ); + poctr++; + ++it; + } + + for ( auto i = 0u; i < k_; ++i ) + { + if ( pntk_.model_value( f( l, i ) ) ) + { + ntk.create_po( nodes[n_ + i] ); + poctr++; + break; + } + } + } + + /* maybe some trivial POs are still left. */ + while ( it != trivial_pos_.end() && it->first == poctr ) + { + ntk.create_po( it->second == n_ ? ntk.get_constant( false ) : nodes[it->second] ); + poctr++; + ++it; + } + + return ntk; + } + + void debug_solution() + { + for ( auto i = 0u; i < k_; ++i ) + { + fmt::print( i == 0 ? "B =" : " " ); + for ( auto j = 0u; j < n_; ++j ) + { + fmt::print( " {}", (int)pntk_.model_value( b( i, j ) ) ); + } + fmt::print( i == 0 ? " C =" : " " ); + for ( auto p = 0u; p < i; ++p ) + { + fmt::print( " {}", (int)pntk_.model_value( c( i, p ) ) ); + } + fmt::print( std::string( 2 * ( k_ - i ), ' ' ) ); + fmt::print( i == 0u ? " F =" : " " ); + for ( auto l = 0u; l < m_; ++l ) + { + fmt::print( " {}", (int)pntk_.model_value( f( l, i ) ) ); + } + fmt::print( "\n" ); + } + } + +private: + void ensure_row_size2() + { + for ( auto i = 0u; i < k_; ++i ) + { + /* at least 2 */ + for ( auto cpl = 0u; cpl <= n_ + i; ++cpl ) + { + std::vector> lits( n_ + i ); + for ( auto j = 0u; j < n_ + i; ++j ) + { + lits[j] = b_or_c( i, j ) ^ ( cpl == j ); + } + pntk_.add_clause( lits ); + } + + /* at most 2 */ + for ( auto j = 2u; j < n_ + i; ++j ) + { + for ( auto jj = 1u; jj < j; ++jj ) + { + for ( auto jjj = 0u; jjj < jj; ++jjj ) + { + pntk_.add_clause( !b_or_c( i, j ), !b_or_c( i, jj ), !b_or_c( i, jjj ) ); + } + } + } + } + } + + void ensure_connectivity() + { + // psi function + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + std::vector> xors( 1 + i ); + auto it = xors.begin(); + *it++ = b( i, j ); + for ( auto p = 0u; p < i; ++p ) + { + *it++ = pntk_.create_and( c( i, p ), psi( j, p ) ); + } + psi( j, i ) = pntk_.create_nary_xor( xors ); + } + } + + for ( auto l = 0u; l < m_; ++l ) + { + for ( auto i = 0u; i < k_; ++i ) + { + std::vector> ands( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + ands[j] = pntk_.create_xnor( psi( j, i ), pntk_.get_constant( linear_matrix_[l][j] ) ); + } + pntk_.add_clause( !f( l, i ), pntk_.create_nary_and( ands ) ); + } + } + + // No two steps are the same + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto p = 0u; p < i; ++p ) + { + std::vector> ors( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + ors[j] = pntk_.create_xor( psi( j, p ), psi( j, i ) ); + } + pntk_.add_clause( ors ); + } + } + + if ( !ignore_inputs_.empty() || ps_.cancellation_free ) + { + // phi function + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + std::vector> ors( 1 + i ); + auto it = ors.begin(); + *it++ = b( i, j ); + for ( auto p = 0u; p < i; ++p ) + { + *it++ = pntk_.create_and( c( i, p ), phi( j, p ) ); + } + phi( j, i ) = pntk_.create_nary_or( ors ); + } + } + + // cancellation-free + if ( ps_.cancellation_free ) + { + for ( auto i = 0u; i < k_; ++i ) + { + for ( auto j = 0u; j < n_; ++j ) + { + pntk_.add_clause( !psi( j, i ), phi( j, i ) ); + pntk_.add_clause( psi( j, i ), !phi( j, i ) ); + } + } + } + } + + // ignored inputs + if ( !ignore_inputs_.empty() ) + { + for ( auto l = 0u; l < m_; ++l ) + { + for ( auto j : ignore_inputs_[l] ) + { + for ( auto i = 0u; i < k_; ++i ) + { + pntk_.add_clause( !f( l, i ), !phi( j, i ) ); + } + } + } + } + + // at least 2 inputs in each compute form + for ( auto i = 0u; i < k_; ++i ) + { + /* at least 2 */ + for ( auto cpl = 0u; cpl <= n_; ++cpl ) + { + std::vector> lits( n_ ); + for ( auto j = 0u; j < n_; ++j ) + { + lits[j] = psi( j, i ) ^ ( cpl == j ); + } + pntk_.add_clause( lits ); + } + } + } + + void ensure_outputs() + { + // each output covers at least one row + for ( auto l = 0u; l < m_; ++l ) + { + std::vector> lits( k_ ); + for ( auto i = 0u; i < k_; ++i ) + { + lits[i] = f( l, i ); + for ( auto ii = i + 1; ii < k_; ++ii ) + { + pntk_.add_clause( !f( l, i ), !f( l, ii ) ); + } + } + pntk_.add_clause( lits ); + } + + // at most one output (if no duplicates) per row + // for ( auto i = 0u; i < k_; ++i ) + //{ + // for ( auto l = 1u; l < m_; ++l ) + // { + // for ( auto ll = 0u; ll < l; ++ll ) + // { + // pntk_.add_clause( !f( l, i ), !f( ll, i ) ); + // } + // } + //} + } + + // 0 <= i <= k - 1 + // 0 <= j <= n - 1 + const signal& b( uint32_t i, uint32_t j ) const + { + return bs_[i * n_ + j]; + } + + // 0 <= i <= k - 1 + // 0 <= p <= i - 1 + const signal& c( uint32_t i, uint32_t p ) const + { + return cs_[( ( ( i - 1 ) * i ) / 2 ) + p]; + } + + // 0 <= i <= k - 1 + // 0 <= j <= n + i - 1 + const signal& b_or_c( uint32_t i, uint32_t j ) const + { + return j < n_ ? b( i, j ) : c( i, j - n_ ); + } + + // 0 <= l <= m - 1 + // 0 <= i <= k - 1 + const signal& f( uint32_t l, uint32_t i ) const + { + return fs_[l * k_ + i]; + } + + // 0 <= j <= n - 1 + // 0 <= i <= k - 1 + signal& psi( uint32_t j, uint32_t i ) + { + return psis_[i * n_ + j]; + } + + // 0 <= j <= n - 1 + // 0 <= i <= k - 1 + signal& phi( uint32_t j, uint32_t i ) + { + return phis_[i * n_ + j]; + } + +private: + std::vector> const& linear_matrix_; + uint32_t k_; + uint32_t n_; + uint32_t m_; + + std::vector> bs_; + std::vector> cs_; + std::vector> fs_; + std::vector> psis_; + std::vector> phis_; + + problem_network_t pntk_; + std::vector> const& ignore_inputs_; + std::vector> const& trivial_pos_; + exact_linear_synthesis_params const& ps_; +}; + +template +struct exact_linear_synthesis_impl +{ + exact_linear_synthesis_impl( std::vector> const& linear_matrix, exact_linear_synthesis_params const& ps, exact_linear_synthesis_stats& st ) + : ps_( ps ), + st_( st ) + { + if ( ps_.very_verbose ) + { + fmt::print( "[i] input matrix =\n" ); + debug_matrix( linear_matrix ); + } + + /* check whether ignore inputs matches matrix size */ + if ( !ps_.ignore_inputs.empty() && ps_.ignore_inputs.size() != linear_matrix.size() ) + { + fmt::print( "[e] size of ignored inputs vector must match number of rows in linear matrix" ); + std::abort(); + } + + /* check matrix for trivial entries */ + for ( auto j = 0u; j < linear_matrix.size(); ++j ) + { + const auto& row = linear_matrix[j]; + n_ = static_cast( row.size() ); + + auto cnt = 0u; + auto idx = 0u; + for ( auto i = 0u; i < row.size(); ++i ) + { + if ( row[i] ) + { + idx = i; + if ( ++cnt == 2u ) + { + break; + } + } + } + if ( cnt == 0u ) + { + /* constant 0 is encoded as input n */ + trivial_pos_.emplace_back( j, n_ ); + } + else if ( cnt == 1u ) + { + trivial_pos_.emplace_back( j, idx ); + } + else + { + linear_matrix_.push_back( row ); + if ( !ps_.ignore_inputs.empty() ) + { + ignore_inputs_.push_back( ps_.ignore_inputs[j] ); + } + } + } + + m_ = static_cast( linear_matrix_.size() ); + + if ( ps_.very_verbose ) + { + fmt::print( "[i] problem matrix =\n" ); + debug_matrix( linear_matrix_ ); + fmt::print( "\n[i] trivial POs =\n" ); + for ( auto const& [j, i] : trivial_pos_ ) + { + if ( i == n_ ) + { + fmt::print( "f{} = 0\n", j ); + } + else + { + fmt::print( "f{} = x{}\n", j, i ); + } + } + if ( !ignore_inputs_.empty() ) + { + fmt::print( "\n[i] ignored inputs =\n" ); + for ( auto j = 0u; j < ignore_inputs_.size(); ++j ) + { + fmt::print( "for f{} ignore {{{}}}\n", j, fmt::join( ignore_inputs_[j], ", " ) ); + } + } + } + } + + std::optional run() + { + if ( m_ == 0u ) + { + Ntk ntk; + + std::vector> nodes( n_ ); + std::generate( nodes.begin(), nodes.end(), [&]() { return ntk.create_pi(); } ); + + for ( auto po : trivial_pos_ ) + { + ntk.create_po( po.second == n_ ? ntk.get_constant( false ) : nodes[po.second] ); + } + + return ntk; + } + + return ps_.upper_bound ? run_decreasing() : run_increasing(); + } + +private: + std::optional run_increasing() + { + auto k_ = m_; + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "[i] try to find a solution with {} steps, solving time so far = {:.2f} secs\n", k_, to_seconds( st_.time_solving ) ); + } + + exact_linear_synthesis_problem_network pntk( k_, linear_matrix_, ignore_inputs_, trivial_pos_, ps_ ); + const auto res = call_with_stopwatch( st_.time_solving, [&]() { return pntk.solve(); } ); + if ( res && *res ) + { + if ( ps_.very_verbose ) + { + pntk.debug_solution(); + } + return pntk.template extract_solution(); + } + ++k_; + } + } + + std::optional run_decreasing() + { + std::optional best{}; + auto k_ = *ps_.upper_bound; + while ( true ) + { + if ( ps_.verbose ) + { + fmt::print( "[i] try to find a solution with {} steps, solving time so far = {:.2f} secs\n", k_, to_seconds( st_.time_solving ) ); + } + exact_linear_synthesis_problem_network pntk( k_, linear_matrix_, ignore_inputs_, trivial_pos_, ps_ ); + const auto res = call_with_stopwatch( st_.time_solving, [&]() { return pntk.solve(); } ); + if ( res && *res ) + { + if ( ps_.very_verbose ) + { + pntk.debug_solution(); + } + best = pntk.template extract_solution(); + --k_; + } + else /* if unsat or timeout */ + { + return best; + } + } + } + +private: + void debug_matrix( std::vector> const& matrix ) const + { + for ( auto const& row : matrix ) + { + for ( auto b : row ) + { + fmt::print( "{}", b ? '1' : '0' ); + } + fmt::print( "\n" ); + } + } + +private: + uint32_t n_{}; + uint32_t m_{ 0u }; + std::vector> linear_matrix_; + std::vector> trivial_pos_; + std::vector> ignore_inputs_; + exact_linear_synthesis_params const& ps_; + exact_linear_synthesis_stats& st_; +}; + +} // namespace detail + +/*! \brief Extracts linear matrix from XOR-based XAG + * + * This algorithm can be used to extract the linear matrix represented by an + * XAG that only contains XOR gates and no inverters at the outputs. The matrix + * can be passed as an argument to `exact_linear_synthesis`. + */ +template +std::vector> get_linear_matrix( Ntk const& ntk ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + detail::linear_matrix_simulator sim( ntk.num_pis() ); + return simulate>( detail::linear_xag{ ntk }, sim ); +} + +/*! \brief Optimum linear circuit synthesis (based on SAT) + * + * This algorithm creates an XAG that is only composed of XOR gates. It is + * given as input a linear matrix, represented as vector of bool-vectors. The + * size of the outer vector corresponds to the number of outputs, the size of + * each inner vector must be the same and corresponds to the number of inputs. + * + * Reference: [C. Fuhs and P. Schneider-Kamp, SAT (2010), page 71-84] + */ +template +std::optional exact_linear_synthesis( std::vector> const& linear_matrix, exact_linear_synthesis_params const& ps = {}, exact_linear_synthesis_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + exact_linear_synthesis_stats st; + const auto xag = detail::exact_linear_synthesis_impl{ linear_matrix, ps, st }.run(); + + if ( ps.verbose ) + { + st.report(); + } + if ( pst ) + { + *pst = st; + } + return xag; +} + +/*! \brief Optimum linear circuit resynthesis (based on SAT) + * + * This algorithm extracts the linear matrix from an XAG that only contains of + * XOR gates and no inversions and returns a new XAG that has the optimum number + * of XOR gates to represent the same function. + * + * Reference: [C. Fuhs and P. Schneider-Kamp, SAT (2010), page 71-84] + */ +template +std::optional exact_linear_resynthesis( Ntk const& ntk, exact_linear_synthesis_params const& ps = {}, exact_linear_synthesis_stats* pst = nullptr ) +{ + static_assert( std::is_same_v, "Ntk is not XAG-like" ); + + const auto linear_matrix = get_linear_matrix( ntk ); + return exact_linear_synthesis( linear_matrix, ps, pst ); +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/lut_mapper.hpp b/include/mockturtle/algorithms/lut_mapper.hpp new file mode 100644 index 0000000..5765904 --- /dev/null +++ b/include/mockturtle/algorithms/lut_mapper.hpp @@ -0,0 +1,3015 @@ +/* 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 lut_mapper.hpp + \brief LUT mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../networks/klut.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/cuts.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/truth_table_cache.hpp" +#include "../views/choice_view.hpp" +#include "../views/mapping_view.hpp" +#include "../views/mffc_view.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "collapse_mapped.hpp" +#include "cut_enumeration.hpp" +#include "exorcism.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for map. + * + * The data structure `map_params` holds configurable parameters + * with default arguments for `map`. + */ +struct lut_map_params +{ + lut_map_params() + { + cut_enumeration_ps.cut_size = 6u; + cut_enumeration_ps.cut_limit = 8u; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 8. The maximum value + * is 16. The maxiumum cut size is 16. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Do area-oriented mapping. */ + bool area_oriented_mapping{ false }; + + /*! \brief Required depth for depth relaxation. */ + uint32_t required_delay{ 0u }; + + /*! \brief Required depth relaxation ratio (%). */ + uint32_t relax_required{ 0u }; + + /*! \brief Recompute cuts at each step. */ + bool recompute_cuts{ true }; + + /*! \brief Number of rounds for area sharing optimization. */ + uint32_t area_share_rounds{ 2u }; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 1u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Use edge count reduction. */ + bool edge_optimization{ true }; + + /*! \brief Try to expand the cuts. */ + bool cut_expansion{ true }; + + /*! \brief Remove the cuts that are contained in others */ + bool remove_dominated_cuts{ true }; + + /*! \brief Maps by collapsing MFFCs */ + bool collapse_mffcs{ false }; + + /*! \brief Depth optimization by balancing ISOPs */ + bool sop_balancing{ false }; + + /*! \brief Depth optimization by balancing ESOPs */ + bool esop_balancing{ false }; + + /*! \brief Maximum number variables for cost function caching */ + uint32_t cost_cache_vars{ 3u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for mapper. + * + * The data structure `map_stats` provides data collected by running + * `map`. + */ +struct lut_map_stats +{ + /*! \brief Area result. */ + uint32_t area{ 0 }; + /*! \brief Worst delay result. */ + uint32_t delay{ 0 }; + /*! \brief Edge result. */ + uint32_t edges{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Depth and size stats for each round. */ + std::vector round_stats{}; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +#pragma region cut set +/* cut data */ +struct cut_enumeration_lut_cut +{ + uint32_t delay{ 0 }; + uint32_t lut_area{ 0 }; + uint32_t lut_delay{ 0 }; + float area_flow{ 0 }; + float edge_flow{ 0 }; + bool ignore{ false }; +}; + +enum class lut_cut_sort_type +{ + DELAY, + DELAY2, + AREA, + AREA2, + NONE +}; + +template +class lut_cut_set +{ +public: + /*! \brief Standard constructor. + */ + lut_cut_set() + { + clear(); + } + + /*! \brief Assignment operator. + */ + lut_cut_set& operator=( lut_cut_set const& other ) + { + if ( this != &other ) + { + _pcend = _pend = _pcuts.begin(); + + auto it = other.begin(); + while ( it != other.end() ) + { + **_pend++ = **it++; + ++_pcend; + } + } + + return *this; + } + + /*! \brief Clears a cut set. + */ + void clear() + { + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } + } + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ) + { + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; + } + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const + { + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; + } + + static bool sort_delay( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + return c1->data.edge_flow < c2->data.edge_flow - eps; + } + + static bool sort_delay2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.edge_flow < c2->data.edge_flow - eps ) + return true; + if ( c1->data.edge_flow > c2->data.edge_flow + eps ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_area( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.delay < c2->data.delay ) + return true; + if ( c1->data.delay > c2->data.delay ) + return false; + return c1.size() < c2.size(); + } + + static bool sort_area2( CutType const& c1, CutType const& c2 ) + { + constexpr auto eps{ 0.005f }; + if ( !c1->data.ignore && c2->data.ignore ) + return true; + if ( c1->data.ignore && !c2->data.ignore ) + return false; + if ( c1->data.area_flow < c2->data.area_flow - eps ) + return true; + if ( c1->data.area_flow > c2->data.area_flow + eps ) + return false; + if ( c1->data.edge_flow < c2->data.edge_flow - eps ) + return true; + if ( c1->data.edge_flow > c2->data.edge_flow + eps ) + return false; + if ( c1.size() < c2.size() ) + return true; + if ( c1.size() > c2.size() ) + return false; + return c1->data.delay < c2->data.delay; + } + + /*! \brief Compare two cuts using sorting functions. + * + * This method compares two cuts using a sorting function. + * + * \param cut1 first cut. + * \param cut2 second cut. + * \param sort sorting function. + */ + static bool compare( CutType const& cut1, CutType const& cut2, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + if ( sort == lut_cut_sort_type::DELAY ) + { + return sort_delay( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + return sort_delay2( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::AREA ) + { + return sort_area( cut1, cut2 ); + } + else if ( sort == lut_cut_sort_type::AREA2 ) + { + return sort_area2( cut1, cut2 ); + } + else + { + return false; + } + } + + /*! \brief Inserts a cut into a set without checking dominance. + * + * This method will insert a cut into a set and maintain an order. This + * method doesn't remove the cuts that are dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param sort Cut prioritization function. + */ + void simple_insert( CutType const& cut, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + /* insert cut in a sorted way */ + typename std::array::iterator ipos = _pcuts.begin(); + + if ( sort == lut_cut_sort_type::DELAY ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_delay( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_delay2( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::AREA ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area( *a, *b ); } ); + } + else if ( sort == lut_cut_sort_type::AREA2 ) + { + ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return sort_area2( *a, *b ); } ); + } + else /* NONE */ + { + ipos == _pend; + } + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; + } + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. Variable `skip0` tell to skip the dominance check on + * cut zero. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + * \param skip0 Skip dominance check on cut zero. + * \param sort Cut prioritization function. + */ + void insert( CutType const& cut, bool skip0 = false, lut_cut_sort_type sort = lut_cut_sort_type::NONE ) + { + auto begin = _pcuts.begin(); + + if ( skip0 && _pend != _pcuts.begin() ) + ++begin; + + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( begin, _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + simple_insert( cut, sort ); + } + + /*! \brief Replaces a cut of the set. + * + * This method replaces the cut at position `index` in the set by `cut` + * and maintains the cuts order. The function does not check whether + * index is in the valid range. + * + * \param index Index of the cut to replace. + * \param cut Cut to insert. + */ + void replace( uint32_t index, CutType const& cut ) + { + *_pcuts[index] = cut; + } + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ) + { + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; + } + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ) + { + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } + } + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, lut_cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; +}; +#pragma endregion + +#pragma region LUT mapper +struct node_lut +{ + /* required time at node output */ + uint32_t required; + /* number of references in the cover */ + uint32_t map_refs; + /* references estimation */ + float est_refs; +}; + +template +class lut_map_impl +{ +private: + /* special map for output drivers to perform some optimizations */ + enum class driver_type : uint32_t + { + none = 0, + pos = 1, + neg = 2, + mixed = 3 + }; + +public: + static constexpr uint32_t max_cut_num = 32; + static constexpr uint32_t max_cut_size = 16; + static constexpr uint32_t max_cubes = 64; + static constexpr uint32_t max_sop_decomp_size = max_cut_size * ( max_cubes + 1 ); + using cut_t = cut>; + using cut_set_t = lut_cut_set; + using node = typename Ntk::node; + using cut_merge_t = typename std::array; + using TT = kitty::dynamic_truth_table; + using tt_cache = truth_table_cache; + using cost_cache = std::unordered_map>; + using sop_t = std::vector; + using isop_cache = std::vector; + using cubes_queue_t = std::priority_queue, std::greater>; + using lut_info = std::pair>>; + +public: + explicit lut_map_impl( Ntk& ntk, lut_map_params const& ps, lut_map_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + cuts( ntk.size() ) + { + assert( ps.cut_enumeration_ps.cut_limit < max_cut_num && "cut_limit exceeds the compile-time limit for the maximum number of cuts" ); + + if constexpr ( StoreFunction ) + { + TT zero( 0u ), proj( 1u ); + kitty::create_nth_var( proj, 0u ); + + tmp_visited.reserve( 100 ); + truth_tables.resize( 32768 ); + + truth_tables.insert( zero ); + truth_tables.insert( proj ); + + /* reserve cost cache */ + if constexpr ( !std::is_same::value ) + { + truth_tables_cost.reserve( 1000 ); + } + + /* reserve ISOP cache */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + isops.reserve( 32768 ); + isops.emplace_back(); /* empty ISOP for constant */ + isops.push_back( { kitty::cube{ 1, 1 } } ); /* ISOP for variable */ + } + } + } + + klut_network run() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + topo_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + + perform_mapping(); + return create_lut_network(); + } + + void run_inplace() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + topo_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + topo_order.push_back( n ); + } ); + + if ( ps.collapse_mffcs ) + { + compute_mffcs_mapping(); + return; + } + + perform_mapping(); + derive_mapping(); + } + +private: + void perform_mapping() + { + /* define area sorting function */ + lut_cut_sort_type area_sort = ( ps.area_oriented_mapping && !ps.edge_optimization ) ? lut_cut_sort_type::AREA : lut_cut_sort_type::AREA2; + + /* init the data structure */ + init_nodes(); + init_cuts(); + + /* compute mapping for depth or area */ + if ( !ps.area_oriented_mapping ) + { + compute_required_time(); + + if ( ps.recompute_cuts ) + { + compute_mapping( lut_cut_sort_type::DELAY, true, true ); + compute_required_time(); + compute_mapping( lut_cut_sort_type::DELAY2, true, true ); + compute_required_time(); + compute_mapping( area_sort, true, true ); + } + else + { + compute_mapping( lut_cut_sort_type::DELAY2, true, true ); + } + } + else + { + compute_required_time(); + compute_mapping( area_sort, false, true ); + } + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + + /* try backward area iterations */ + uint32_t i = 0; + while ( i < ps.area_share_rounds ) + { + compute_share_mapping( area_sort, i == 0 ); + + if ( ps.cut_expansion ) + { + expand_cuts(); + } + ++i; + } + + /* compute mapping using global area flow */ + i = 0; + while ( i < ps.area_flow_rounds ) + { + compute_required_time(); + compute_mapping( area_sort, false, ps.recompute_cuts ); + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + ++i; + } + + /* compute mapping using exact area/edge */ + i = 0; + while ( i < ps.ela_rounds ) + { + compute_required_time(); + compute_mapping( area_sort, false, ps.recompute_cuts ); + + if ( ps.cut_expansion ) + { + compute_required_time(); + expand_cuts(); + } + ++i; + } + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.map_refs = ntk.fanout_size( n ); + node_data.est_refs = static_cast( ntk.fanout_size( n ) ); + } ); + } + + void init_cuts() + { + /* init constant cut */ + add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ), false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + add_zero_cut( ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ), true ); + + /* init PIs cuts */ + ntk.foreach_ci( [&]( auto const& n ) { + add_unit_cut( ntk.node_to_index( n ) ); + } ); + } + + template + void compute_mapping( lut_cut_sort_type const sort, bool preprocess, bool recompute_cuts ) + { + cuts_total = 0; + for ( auto const& n : topo_order ) + { + if constexpr ( !ELA ) + { + auto const index = ntk.node_to_index( n ); + if ( !preprocess && iteration != 0 ) + { + node_match[index].est_refs = ( 2.0 * node_match[index].est_refs + 1.0 * node_match[index].map_refs ) / 3.0; + } + else + { + node_match[index].est_refs = static_cast( node_match[index].map_refs ); + } + } + + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + if ( recompute_cuts ) + { + if constexpr ( Ntk::min_fanin_size == 2 && Ntk::max_fanin_size == 2 ) + { + compute_best_cut2( n, sort, preprocess ); + } + else + { + compute_best_cut( n, sort, preprocess ); + } + } + else + { + /* update cost the function and move the best one first */ + update_cut_data( n, sort ); + } + } + + set_mapping_refs(); + + if constexpr ( DO_AREA ) + { + ++area_iteration; + } + + /* round stats */ + { + std::stringstream stats; + + if ( ( sort == lut_cut_sort_type::AREA || sort == lut_cut_sort_type::AREA2 ) && ELA ) + { + stats << fmt::format( "[i] Area : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else if ( sort == lut_cut_sort_type::AREA || sort == lut_cut_sort_type::AREA2 ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else if ( sort == lut_cut_sort_type::DELAY2 ) + { + stats << fmt::format( "[i] Delay2 : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + } + st.round_stats.push_back( stats.str() ); + } + } + + void compute_share_mapping( lut_cut_sort_type const sort, bool first ) + { + /* reset required times and references except for POs */ + compute_share_mapping_init( first ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + auto const index = ntk.node_to_index( *it ); + + /* skip not used nodes */ + if ( node_match[index].map_refs == 0 ) + continue; + + /* update cost the function and move the best one first */ + update_cut_data_share( *it, sort ); + } + + /* propagate correct arrival times and compute stats */ + propagate_arrival_times(); + + /* round stats */ + { + st.round_stats.push_back( fmt::format( "[i] AreaSh : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ) ); + } + } + + template + void expand_cuts() + { + /* cut expansion is not yet compatible with truth table computation */ + if constexpr ( StoreFunction ) + return; + + /* don't expand if cut recomputed cuts is off */ + if ( !ps.recompute_cuts ) + return; + + for ( auto const& n : topo_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + expand_cuts_node( n ); + } + + set_mapping_refs(); + + std::string stats = fmt::format( "[i] Reduce : Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + st.round_stats.push_back( stats ); + } + + template + void set_mapping_refs() + { + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs = 0u; + } + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + delay = std::max( delay, cuts[index][0]->data.delay ); + + if constexpr ( !ELA ) + { + ++node_match[index].map_refs; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0; + edges = 0; + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + { + continue; + } + + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs == 0u ) + continue; + + auto& best_cut = cuts[index][0]; + + if constexpr ( !ELA ) + { + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs++; + } + } + area += best_cut->data.lut_area; + edges += best_cut.size(); + } + + ++iteration; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required = UINT32_MAX >> 1; + } + + /* return in case of area_oriented_mapping */ + if ( iteration == 0 || ps.area_oriented_mapping ) + return; + + uint32_t required = delay; + + /* relax delay constraints */ + if ( ps.required_delay == 0.0f && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + if ( ps.required_delay != 0 ) + { + /* Global target time constraint */ + if ( ps.required_delay < delay ) + { + if ( !ps.area_oriented_mapping && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {}", ps.required_delay ) << std::endl; + } + else + { + required = ps.required_delay; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + node_match[index].required = required; + } ); + + /* propagate required time to the PIs */ + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + if ( ntk.is_ci( *it ) || ntk.is_constant( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + + if ( node_match[index].map_refs == 0 ) + continue; + + /* in case of decomposition cost */ + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + compute_balancing_cost_required( index ); + continue; + } + } + + for ( auto leaf : cuts[index][0] ) + { + node_match[leaf].required = std::min( node_match[leaf].required, node_match[index].required - cuts[index][0]->data.lut_delay ); + } + } + } + + void propagate_arrival_times() + { + area = 0; + edges = 0; + for ( auto const& n : topo_order ) + { + auto index = ntk.node_to_index( n ); + + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + { + continue; + } + + /* propagate arrival time */ + uint32_t node_delay = 0; + auto& best_cut = cuts[index].best(); + + for ( auto leaf : best_cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + node_delay = std::max( node_delay, best_leaf_cut->data.delay ); + } + + best_cut->data.delay = node_delay + best_cut->data.lut_delay; + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs == 0u ) + continue; + + /* update stats */ + area += best_cut->data.lut_area; + edges += best_cut.size(); + } + + /* update worst delay */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + delay = std::max( delay, cuts[index][0]->data.delay ); + } ); + } + + void compute_share_mapping_init( bool first ) + { + /* reset the mapping references and the required time */ + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required = UINT32_MAX >> 1; + if ( !first ) + node_match[i].est_refs = ( 2.0 * node_match[i].est_refs + 1.0 * node_match[i].map_refs ) / 3.0; + else + node_match[i].est_refs = std::max( 1.0, ( 1.0 * node_match[i].est_refs + 2.0 * node_match[i].map_refs ) / 3.0 ); + node_match[i].map_refs = 0; + + /* update flows if in area-oriented mapping */ + if ( ps.area_oriented_mapping ) + compute_cut_data( cuts[i].best(), ntk.index_to_node( i ), false ); + } + + uint32_t required = delay; + if ( ps.required_delay == 0.0f && ps.relax_required > 0.0f ) + { + required *= ( 100.0 + ps.relax_required ) / 100.0; + } + + if ( ps.required_delay != 0 ) + { + /* Global target time constraint */ + if ( ps.required_delay < delay ) + { + if ( !ps.area_oriented_mapping && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {}", ps.required_delay ) << std::endl; + } + else + { + required = ps.required_delay; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + node_match[index].required = required; + node_match[index].map_refs++; + } ); + } + + template + void compute_best_cut2( node const& n, lut_cut_sort_type const sort, bool preprocess ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut; + + /* compute cuts */ + const auto fanin = 2; + uint32_t pairs{ 1 }; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + pairs *= static_cast( lcuts[i]->size() ); + } ); + lcuts[2] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( rcuts[0] ); + } + } + + /* recompute the data of the best cut */ + if ( iteration != 0 ) + { + best_cut = rcuts[0]; + compute_cut_data( best_cut, n, true ); + } + + /* clear cuts */ + rcuts.clear(); + + /* insert the previous best cut */ + if ( iteration != 0 && !preprocess ) + { + rcuts.simple_insert( best_cut, sort ); + } + + cut_t new_cut; + std::vector vcuts( fanin ); + + for ( auto const& c1 : *lcuts[0] ) + { + for ( auto const& c2 : *lcuts[1] ) + { + if ( !c1->merge( *c2, new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + continue; + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + continue; + } + + if constexpr ( StoreFunction ) + { + vcuts[0] = c1; + vcuts[1] = c2; + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + compute_cut_data( new_cut, ntk.index_to_node( index ), true ); + + /* check required time */ + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + } + + cuts_total += rcuts.size(); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + + /* replace the new best cut with previous one */ + if ( preprocess && rcuts[0]->data.delay > node_data.required ) + rcuts.replace( 0, best_cut ); + + /* add trivial cut */ + if ( rcuts.size() > 1 || ( *rcuts.begin() )->size() > 1 ) + { + add_unit_cut( index ); + } + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( rcuts[0] ); + } + } + } + + template + void compute_best_cut( node const& n, lut_cut_sort_type const sort, bool preprocess ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut; + + /* compute cuts */ + uint32_t pairs{ 1 }; + std::vector cut_sizes; + ntk.foreach_fanin( ntk.index_to_node( index ), [this, &pairs, &cut_sizes]( auto child, auto i ) { + lcuts[i] = &cuts[ntk.node_to_index( ntk.get_node( child ) )]; + cut_sizes.push_back( static_cast( lcuts[i]->size() ) ); + pairs *= cut_sizes.back(); + } ); + const auto fanin = cut_sizes.size(); + lcuts[fanin] = &cuts[index]; + auto& rcuts = *lcuts[fanin]; + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( rcuts[0] ); + } + } + + /* recompute the data of the best cut */ + if ( iteration != 0 ) + { + best_cut = rcuts[0]; + compute_cut_data( best_cut, n, true ); + } + + /* clear cuts */ + rcuts.clear(); + + /* insert the previous best cut */ + if ( iteration != 0 && !preprocess ) + { + rcuts.simple_insert( best_cut, sort ); + } + + if ( fanin > 1 && fanin <= ps.cut_enumeration_ps.fanin_limit ) + { + cut_t new_cut, tmp_cut; + + std::vector vcuts( fanin ); + + foreach_mixed_radix_tuple( cut_sizes.begin(), cut_sizes.end(), [&]( auto begin, auto end ) { + auto it = vcuts.begin(); + auto i = 0u; + while ( begin != end ) + { + *it++ = &( ( *lcuts[i++] )[*begin++] ); + } + + if ( !vcuts[0]->merge( *vcuts[1], new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + return true; /* continue */ + } + + for ( i = 2; i < fanin; ++i ) + { + tmp_cut = new_cut; + if ( !vcuts[i]->merge( tmp_cut, new_cut, ps.cut_enumeration_ps.cut_size ) ) + { + return true; /* continue */ + } + } + + if ( ps.remove_dominated_cuts && rcuts.is_dominated( new_cut ) ) + { + return true; /* continue */ + } + + if constexpr ( StoreFunction ) + { + new_cut->func_id = compute_truth_table( index, vcuts, new_cut ); + } + + compute_cut_data( new_cut, index, true ); + + /* check required time */ + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + + return true; + } ); + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + else if ( fanin == 1 ) + { + for ( auto const& cut : *lcuts[0] ) + { + cut_t new_cut = *cut; + + if constexpr ( StoreFunction ) + { + new_cut->func_id = compute_truth_table( index, { cut }, new_cut ); + } + + compute_cut_data( new_cut, index, true ); + + if constexpr ( DO_AREA ) + { + if ( preprocess || new_cut->data.delay <= node_data.required ) + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + else + { + if ( ps.remove_dominated_cuts ) + rcuts.insert( new_cut, false, sort ); + else + rcuts.simple_insert( new_cut, sort ); + } + } + + /* limit the maximum number of cuts */ + rcuts.limit( ps.cut_enumeration_ps.cut_limit ); + } + + cuts_total += rcuts.size(); + + /* replace the new best cut with previous one */ + if ( preprocess && rcuts[0]->data.delay > node_data.required ) + rcuts.replace( 0, best_cut ); + + add_unit_cut( index ); + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( rcuts[0] ); + } + } + } + + template + void update_cut_data( node const& n, lut_cut_sort_type const sort ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& node_cut_set = cuts[index]; + uint32_t best_cut_index = 0; + uint32_t cut_index = 0; + + cut_t const* best_cut = &node_cut_set.best(); + + if constexpr ( DO_AREA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_deref( *best_cut ); + } + } + + /* recompute the data for all the cuts and pick the best */ + for ( cut_t* cut : node_cut_set ) + { + /* skip trivial cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ++cut_index; + continue; + } + + compute_cut_data( *cut, n, false ); + + /* update best */ + if constexpr ( DO_AREA ) + { + if ( ( *cut )->data.delay <= node_data.required ) + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + } + else + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + + ++cut_index; + } + + if constexpr ( DO_AREA || ELA ) + { + if ( iteration != 0 && node_data.map_refs > 0 ) + { + cut_ref( *best_cut ); + } + } + + /* update the best cut */ + node_cut_set.update_best( best_cut_index ); + } + + void update_cut_data_share( node const& n, lut_cut_sort_type const sort ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + auto& node_cut_set = cuts[index]; + uint32_t best_cut_index = 0; + uint32_t cut_index = 0; + + cut_t const* best_cut = &node_cut_set.best(); + + /* recompute the data for all the cuts and pick the best */ + for ( cut_t* cut : node_cut_set ) + { + /* skip trivial cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ++cut_index; + continue; + } + + compute_cut_data_share( *cut ); + + /* update best */ + if ( ( *cut )->data.delay <= node_data.required ) + { + if ( node_cut_set.compare( *cut, *best_cut, sort ) ) + { + best_cut = cut; + best_cut_index = cut_index; + } + } + + ++cut_index; + } + + /* propagate required times backward and reference the leaves */ + for ( auto leaf : *best_cut ) + { + node_match[leaf].required = std::min( node_match[leaf].required, node_data.required - ( *best_cut )->data.lut_delay ); + node_match[leaf].map_refs++; + } + + /* update the best cut */ + node_cut_set.update_best( best_cut_index ); + } + + void expand_cuts_node( node const& n ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + cut_t best_cut = cuts[index][0]; + + if ( node_data.map_refs == 0 ) + return; + + /* update delay */ + uint32_t delay_update = 0; + for ( auto const leaf : best_cut ) + { + delay_update = std::max( delay_update, cuts[leaf][0]->data.delay + best_cut->data.lut_delay ); + } + best_cut->data.delay = delay_update; + + auto const area_before = cut_deref( best_cut ); + + uint32_t cost_before = 0; + + std::vector leaves; + + /* mark volume */ + ntk.incr_trav_id(); + for ( auto const leaf : best_cut ) + { + ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); + leaves.push_back( leaf ); + + /* MFFC leaves */ + if ( node_match[leaf].map_refs == 0 ) + ++cost_before; + } + mark_cut_volume_rec( n ); + + /* improve cut */ + while ( improve_cut( leaves ) ) + ; + + /* measure improvement */ + uint32_t cost_after = 0; + for ( auto const leaf : leaves ) + { + /* MFFC leaves */ + if ( node_match[leaf].map_refs == 0 ) + ++cost_after; + } + + assert( cost_after <= cost_before ); + + /* create the new cut */ + cut_t new_cut; + new_cut.set_leaves( leaves.begin(), leaves.end() ); + new_cut->data = best_cut->data; + + uint32_t delay_after = 0; + for ( auto const leaf : leaves ) + { + delay_after = std::max( delay_after, cuts[leaf][0]->data.delay + new_cut->data.lut_delay ); + } + new_cut->data.delay = delay_after; + + auto const area_after = cut_ref( new_cut ); + + /* new cut is better */ + if ( area_after <= area_before && new_cut->data.delay <= node_data.required ) + { + cuts[index].replace( 0, new_cut ); + } + else + { + /* restore */ + cut_deref( new_cut ); + cut_ref( best_cut ); + } + } + + bool improve_cut( std::vector& leaves ) + { + if ( improve_cut_expand0( leaves ) ) + return true; + + if ( leaves.size() < ps.cut_enumeration_ps.cut_size && improve_cut_expand1( leaves ) ) + return true; + + assert( leaves.size() <= ps.cut_enumeration_ps.cut_size ); + return false; + } + + bool improve_cut_expand0( std::vector& leaves ) + { + for ( auto it = leaves.begin(); it != leaves.end(); ++it ) + { + if ( ntk.is_ci( *it ) ) + continue; + + /* test if expansion would increase the number of leaves */ + int marked = 0; + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + ++marked; + } ); + + if ( marked > 1 ) + continue; + + /* check that the cost does not increase */ + marked = 0; + if ( node_match[*it].map_refs == 0 ) + --marked; + + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return; + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && node_match[index].map_refs == 0 ) + ++marked; + } ); + + /* not referenced leaves don't increase from the transformation */ + if ( marked <= 0 ) + { + /* update leaves */ + uint32_t n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + { + leaves.push_back( index ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + return true; + } + } + + return false; + } + + bool improve_cut_expand1( std::vector& leaves ) + { + for ( auto it = leaves.begin(); it != leaves.end(); ++it ) + { + if ( ntk.is_ci( *it ) ) + continue; + + /* test if expansion would increase the number of leaves by more than 1*/ + int marked = 0; + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + ++marked; + } ); + + if ( marked > 2 ) + continue; + + /* check that the cost reduces */ + marked = 0; + if ( node_match[*it].map_refs == 0 ) + --marked; + + ntk.foreach_fanin( ntk.index_to_node( *it ), [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return; + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && node_match[index].map_refs == 0 ) + ++marked; + } ); + + /* not referenced leaves should be reduced by the transformation */ + if ( marked < 0 ) + { + /* update leaves */ + uint32_t n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const index = ntk.node_to_index( ntk.get_node( f ) ); + if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() ) + { + leaves.push_back( index ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + return true; + } + } + + return false; + } + + uint32_t cut_ref( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_ref( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_deref( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( --node_match[leaf].map_refs == 0u ) + { + count += cut_deref( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_measure_mffc( cut_t const& cut ) + { + tmp_visited.clear(); + + uint32_t count = cut_ref_visit( cut ); + + /* dereference visited */ + for ( auto const& s : tmp_visited ) + { + --node_match[s].map_refs; + } + + return count; + } + + uint32_t cut_ref_visit( cut_t const& cut ) + { + uint32_t count = cut->data.lut_area; + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* add to visited */ + tmp_visited.push_back( leaf ); + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_ref_visit( cuts[leaf][0] ); + } + } + + return count; + } + + uint32_t cut_edge_ref( cut_t const& cut ) + { + uint32_t count = cut.size(); + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs++ == 0u ) + { + count += cut_edge_ref( cuts[leaf][0] ); + } + } + return count; + } + + uint32_t cut_edge_deref( cut_t const& cut ) + { + uint32_t count = cut.size(); + + for ( auto leaf : cut ) + { + if ( ntk.is_ci( ntk.index_to_node( leaf ) ) || ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + + /* Recursive referencing if leaf was not referenced */ + if ( --node_match[leaf].map_refs == 0u ) + { + count += cut_edge_deref( cuts[leaf][0] ); + } + } + return count; + } + + void mark_cut_volume_rec( node const& n ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + mark_cut_volume_rec( ntk.get_node( f ) ); + } ); + } + + /* compute positions of leave indices in cut `sub` (subset) with respect to + * leaves in cut `sup` (super set). + * + * Example: + * compute_truth_table_support( {1, 3, 6}, {0, 1, 2, 3, 6, 7} ) = {1, 3, 4} + */ + void compute_truth_table_support( cut_t const& sub, cut_t const& sup, TT& tt ) + { + std::vector support( sub.size() ); + + size_t j = 0; + auto itp = sup.begin(); + for ( auto i : sub ) + { + itp = std::find( itp, sup.end(), i ); + support[j++] = static_cast( std::distance( sup.begin(), itp ) ); + } + + /* swap variables in the truth table */ + for ( int i = j - 1; i >= 0; --i ) + { + assert( i <= support[i] ); + kitty::swap_inplace( tt, i, support[i] ); + } + } + + template + void compute_cut_data( cut_t& cut, node const& n, bool recompute_cut_cost ) + { + uint32_t lut_area = 0; + uint32_t lut_delay = 0; + + if ( recompute_cut_cost ) + { + cut->data.ignore = false; + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + compute_isop( cut ); + } + else + { + if constexpr ( !std::is_same::value ) + { + if ( auto it = truth_tables_cost.find( cut->func_id ); it != truth_tables_cost.end() ) + { + std::tie( lut_area, lut_delay ) = it->second; + } + else + { + auto cost = lut_cost( truth_tables[cut->func_id] ); + if ( truth_tables[cut->func_id].num_vars() <= ps.cost_cache_vars ) + { + /* cache it */ + truth_tables_cost[cut->func_id] = cost; + } + lut_area = cost.first; + lut_delay = cost.second; + } + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( truth_tables[cut->func_id] ); + } + } + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( cut.size() ); + } + } + else + { + lut_area = cut->data.lut_area; + lut_delay = cut->data.lut_delay; + + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + /* reset fields to be recomputed */ + cut->data.lut_area = 0; + cut->data.lut_delay = 0; + } + } + } + + if constexpr ( ELA ) + { + uint32_t delay{ 0 }; + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + } + + cut->data.delay = lut_delay + delay; + cut->data.lut_area = lut_area; + cut->data.lut_delay = lut_delay; + if ( ps.edge_optimization ) + { + cut->data.area_flow = static_cast( cut_ref( cut ) ); + cut->data.edge_flow = static_cast( cut_edge_deref( cut ) ); + } + else + { + cut->data.area_flow = static_cast( cut_measure_mffc( cut ) ); + cut->data.edge_flow = 0; + } + } + else + { + uint32_t delay{ 0 }; + + float area_flow = static_cast( lut_area ); + float edge_flow = cut.size(); + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + if ( node_match[leaf].map_refs > 0 && leaf != 0 ) + { + area_flow += best_leaf_cut->data.area_flow / node_match[leaf].est_refs; + edge_flow += best_leaf_cut->data.edge_flow / node_match[leaf].est_refs; + } + else + { + area_flow += best_leaf_cut->data.area_flow; + edge_flow += best_leaf_cut->data.edge_flow; + } + } + + cut->data.delay = lut_delay + delay; + cut->data.lut_area = lut_area; + cut->data.lut_delay = lut_delay; + cut->data.area_flow = area_flow; + cut->data.edge_flow = edge_flow; + } + + if constexpr ( StoreFunction ) + { + if ( ps.sop_balancing || ps.esop_balancing ) + { + /* compute delay and area */ + compute_balancing_cost( cut ); + } + } + } + + void compute_cut_data_share( cut_t& cut ) + { + uint32_t delay{ 0 }; + float area_flow = static_cast( cut->data.lut_area ); + float edge_flow = cut.size(); + + for ( auto leaf : cut ) + { + const auto& best_leaf_cut = cuts[leaf][0]; + delay = std::max( delay, best_leaf_cut->data.delay ); + /* flow contribution is added only for not shared leaves */ + if ( node_match[leaf].map_refs == 0 && leaf != 0 ) + { + area_flow += best_leaf_cut->data.area_flow / node_match[leaf].est_refs; + edge_flow += best_leaf_cut->data.edge_flow / node_match[leaf].est_refs; + } + } + + cut->data.delay = cut->data.lut_delay + delay; + cut->data.area_flow = area_flow; + cut->data.edge_flow = edge_flow; + } + + void add_zero_cut( uint32_t index, bool phase ) + { + auto& cut = cuts[index].add_cut( &index, &index ); /* fake iterator for emptyness */ + + if constexpr ( StoreFunction ) + { + if ( phase ) + cut->func_id = 1; + else + cut->func_id = 0; + } + } + + void add_unit_cut( uint32_t index ) + { + auto& cut = cuts[index].add_cut( &index, &index + 1 ); + + if constexpr ( StoreFunction ) + { + cut->func_id = 2; + } + } + + inline bool fast_support_minimization( TT& tt, cut_t& res ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* has not minimized support? */ + if ( ( support & ( support + 1u ) ) != 0u ) + { + return false; + } + + /* variables not in the support are the most significative */ + if ( support_size != res.size() ) + { + std::vector leaves( res.begin(), res.begin() + support_size ); + res.set_leaves( leaves.begin(), leaves.end() ); + tt = kitty::shrink_to( tt, support_size ); + } + + return true; + } + + uint32_t compute_truth_table( uint32_t index, std::vector const& vcuts, cut_t& res ) + { + // stopwatch t( st.cut_enumeration_st.time_truth_table ); /* runtime optimized */ + + std::vector tt( vcuts.size() ); + auto i = 0; + for ( auto const& cut : vcuts ) + { + tt[i] = kitty::extend_to( truth_tables[( *cut )->func_id], res.size() ); + compute_truth_table_support( *cut, res, tt[i] ); + ++i; + } + + auto tt_res = ntk.compute( ntk.index_to_node( index ), tt.begin(), tt.end() ); + + if ( ps.cut_enumeration_ps.minimize_truth_table && !fast_support_minimization( tt_res, res ) ) + { + const auto support = kitty::min_base_inplace( tt_res ); + if ( support.size() != res.size() ) + { + auto tt_res_shrink = shrink_to( tt_res, static_cast( support.size() ) ); + std::vector leaves_before( res.begin(), res.end() ); + std::vector leaves_after( support.size() ); + + auto it_support = support.begin(); + auto it_leaves = leaves_after.begin(); + while ( it_support != support.end() ) + { + *it_leaves++ = leaves_before[*it_support++]; + } + res.set_leaves( leaves_after.begin(), leaves_after.end() ); + return truth_tables.insert( tt_res_shrink ); + } + } + + return truth_tables.insert( tt_res ); + } + + void compute_mffcs_mapping() + { + ntk.clear_mapping(); + + /* map POs */ + ntk.foreach_co( [&]( auto const& f ) { + node const& n = ntk.get_node( f ); + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + return; + + compute_mffc_mapping_node( n ); + } ); + + for ( auto it = topo_order.rbegin(); it != topo_order.rend(); ++it ) + { + node const& n = *it; + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + if ( ntk.fanout_size( n ) <= 1 ) /* it should be unnecessary */ + continue; + + /* create MFFC cut */ + compute_mffc_mapping_node( n ); + } + + st.area = area; + st.delay = delay; + st.edges = edges; + + { + std::stringstream stats; + stats << fmt::format( "[i] Area MFFC: Delay = {:8d} Area = {:8d} Edges = {:8d} Cuts = {:8d}\n", delay, area, edges, cuts_total ); + st.round_stats.push_back( stats.str() ); + } + } + + void compute_mffc_mapping_node( node const& n ) + { + uint32_t lut_area, lut_delay; + + /* create FC cut */ + std::vector inner, leaves; + ntk.incr_trav_id(); + get_fc_nodes_rec( n, inner ); + + /* extract leaves */ + for ( auto const& g : inner ) + { + ntk.foreach_fanin( g, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) != ntk.trav_id() && !ntk.is_constant( ntk.get_node( f ) ) ) + { + leaves.push_back( ntk.get_node( f ) ); + ntk.set_visited( ntk.get_node( f ), ntk.trav_id() ); + } + } ); + } + + /* sort leaves in topo order */ + std::stable_sort( leaves.begin(), leaves.end() ); + + ntk.add_to_mapping( n, leaves.begin(), leaves.end() ); + + delay = std::max( delay, static_cast( leaves.size() ) ); + + if constexpr ( StoreFunction ) + { + default_simulator sim( leaves.size() ); + unordered_node_map node_to_value( ntk ); + + /* populate simulation values for constants */ + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + /* populate simulation values for leaves */ + uint32_t i = 0u; + for ( auto const& g : leaves ) + { + node_to_value[g] = sim.compute_pi( i++ ); + } + + /* simulate recursively */ + simulate_fc_rec( n, node_to_value ); + + ntk.set_cell_function( n, node_to_value[n] ); + + std::tie( lut_area, lut_delay ) = lut_cost( node_to_value[n] ); + } + else + { + std::tie( lut_area, lut_delay ) = lut_cost( leaves.size() ); + } + + area += lut_area; + } + + void get_fc_nodes_rec( node const& n, std::vector& nodes ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + return; + + nodes.push_back( n ); + ntk.set_visited( n, ntk.trav_id() ); + + /* expand cut for single fanout nodes */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto g = ntk.get_node( f ); + if ( ntk.fanout_size( g ) == 1 ) + { + get_fc_nodes_rec( g, nodes ); + } + } ); + } + + void simulate_fc_rec( node const& n, unordered_node_map& node_to_value ) + { + std::vector fanin_values( ntk.fanin_size( n ) ); + + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fc_rec( ntk.get_node( f ), node_to_value ); + } + + fanin_values[i] = node_to_value[ntk.get_node( f )]; + } ); + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + +#pragma region Dump network + klut_network create_lut_network() + { + /* specialized method: does not support buffer/inverter sweeping */ + if ( StoreFunction && ps.cut_enumeration_ps.minimize_truth_table ) + { + return create_lut_network_mapped(); + } + + klut_network res; + node_map, Ntk> node_to_signal( ntk ); + + node_map node_driver_type( ntk, driver_type::none ); + + /* opposites are filled for nodes with mixed driver types, since they have + two nodes in the network. */ + std::unordered_map> opposites; + + /* initial driver types */ + ntk.foreach_co( [&]( auto const& f ) { + switch ( node_driver_type[f] ) + { + case driver_type::none: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::pos; + break; + case driver_type::pos: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::mixed : driver_type::pos; + break; + case driver_type::neg: + node_driver_type[f] = ntk.is_complemented( f ) ? driver_type::neg : driver_type::mixed; + break; + case driver_type::mixed: + default: + break; + } + } ); + + /* constants */ + auto add_constant_to_map = [&]( bool value ) { + const auto n = ntk.get_node( ntk.get_constant( value ) ); + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = res.get_constant( value ); + break; + + case driver_type::neg: + node_to_signal[n] = res.get_constant( !value ); + break; + + case driver_type::mixed: + node_to_signal[n] = res.get_constant( value ); + opposites[n] = res.get_constant( !value ); + break; + } + }; + + add_constant_to_map( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + add_constant_to_map( true ); + } + + /* primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + signal res_signal; + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + res_signal = res.create_pi(); + node_to_signal[n] = res_signal; + break; + + case driver_type::neg: + res_signal = res.create_pi(); + node_to_signal[n] = res.create_not( res_signal ); + break; + + case driver_type::mixed: + res_signal = res.create_pi(); + node_to_signal[n] = res_signal; + opposites[n] = res.create_not( node_to_signal[n] ); + break; + } + } ); + + /* TODO: add sequential compatibility */ + edges = 0; + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + auto const& best_cut = cuts[index][0]; + + kitty::dynamic_truth_table tt; + std::vector> children; + std::tie( tt, children ) = create_lut( n, node_to_signal, node_driver_type ); + edges += children.size(); + + switch ( node_driver_type[n] ) + { + default: + case driver_type::none: + case driver_type::pos: + node_to_signal[n] = res.create_node( children, tt ); + break; + + case driver_type::neg: + node_to_signal[n] = res.create_node( children, ~tt ); + break; + + case driver_type::mixed: + node_to_signal[n] = res.create_node( children, tt ); + opposites[n] = res.create_node( children, ~tt ); + edges += children.size(); + break; + } + } + + /* outputs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) && node_driver_type[f] == driver_type::mixed ) + res.create_po( opposites[ntk.get_node( f )] ); + else + res.create_po( node_to_signal[f] ); + } ); + + st.area = area; + st.delay = delay; + st.edges = edges; + + return res; + } + + klut_network create_lut_network_mapped() + { + klut_network res; + mapping_view mapping_ntk{ ntk }; + + /* load mapping info */ + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + std::vector nodes; + auto const& best_cut = cuts[index][0]; + + for ( auto const& l : best_cut ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + mapping_ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + mapping_ntk.set_cell_function( n, truth_tables[best_cut->func_id] ); + } + } + + /* generate mapped network */ + collapse_mapped_network( res, mapping_ntk ); + + st.area = area; + st.delay = delay; + st.edges = edges; + + return res; + } + + inline lut_info create_lut( node const& n, node_map, Ntk>& node_to_signal, node_map const& node_driver_type ) + { + auto const& best_cut = cuts[ntk.node_to_index( n )][0]; + + std::vector> children; + for ( auto const& l : best_cut ) + { + children.push_back( node_to_signal[ntk.index_to_node( l )] ); + } + + /* recursively compute the function for each choice until success */ + ntk.incr_trav_id(); + unordered_node_map node_to_value( ntk ); + + /* add constants */ + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = ~kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( true ) ), ntk.trav_id() ); + } + + /* add leaves */ + uint32_t ctr = 0; + for ( uint32_t leaf : best_cut ) + { + kitty::dynamic_truth_table tt_leaf( best_cut.size() ); + kitty::create_nth_var( tt_leaf, ctr++, node_driver_type[ntk.index_to_node( leaf )] == driver_type::neg ); + node_to_value[ntk.index_to_node( leaf )] = tt_leaf; + ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); + } + + /* recursively compute the function */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_function_rec( ntk.get_node( f ), node_to_value ); + } ); + + std::vector tts; + ntk.foreach_fanin( n, [&]( auto const& f ) { + tts.push_back( node_to_value[ntk.get_node( f )] ); + } ); + TT tt = ntk.compute( n, tts.begin(), tts.end() ); + + minimize_support( tt, children ); + + return { tt, children }; + } + + void compute_function_rec( node const& n, unordered_node_map& node_to_value ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + { + assert( node_to_value.has( n ) ); + return; + } + + assert( !ntk.is_ci( n ) ); + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_function_rec( ntk.get_node( f ), node_to_value ); + } ); + + /* compute the function */ + std::vector tts; + ntk.foreach_fanin( n, [&]( auto const& f ) { + tts.push_back( node_to_value[ntk.get_node( f )] ); + } ); + + node_to_value[n] = ntk.compute( n, tts.begin(), tts.end() ); + } + + void derive_mapping() + { + ntk.clear_mapping(); + + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + std::vector nodes; + auto const& best_cut = cuts[index][0]; + + for ( auto const& l : best_cut ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, truth_tables[best_cut->func_id] ); + } + } + + st.area = area; + st.delay = delay; + st.edges = edges; + } + + void minimize_support( TT& tt, std::vector>& children ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* variables not in the support are the most significative */ + if ( ( support & ( support + 1u ) ) == 0u ) + { + if ( support_size != children.size() ) + { + children.erase( children.begin() + support_size, children.end() ); + tt = kitty::shrink_to( tt, support_size ); + } + + return; + } + + /* vacuous variables */ + const auto support_vector = kitty::min_base_inplace( tt ); + assert( support_vector.size() != children.size() ); + + auto tt_shrink = shrink_to( tt, support_size ); + std::vector> children_support( support_size ); + + auto it_support = support_vector.begin(); + auto it_children = children_support.begin(); + while ( it_support != support_vector.end() ) + { + *it_children++ = children[*it_support++]; + } + + children = std::move( children_support ); + } +#pragma endregion + +#pragma region balancing + void compute_isop( cut_t& cut, bool both_phases = true ) + { + uint32_t func_id = cut->func_id >> 1; + + if ( func_id < isops.size() ) + { + auto const& sop = isops[func_id]; + if ( sop.size() > max_cubes ) + { + cut->data.ignore = true; + } + return; + } + + assert( func_id == isops.size() ); + + sop_t sop, sop_n; + if ( ps.sop_balancing ) + { + sop = kitty::isop( truth_tables[func_id << 1u] ); + } + else + { + sop = exorcism( truth_tables[func_id << 1u] ); + } + + if ( both_phases ) + { + sop_t n_sop; + if ( ps.sop_balancing ) + { + n_sop = kitty::isop( ~truth_tables[func_id << 1u] ); + } + else + { + n_sop = exorcism( ~truth_tables[func_id << 1u] ); + } + + if ( n_sop.size() < sop.size() ) + { + sop.swap( n_sop ); + } + else if ( n_sop.size() == sop.size() ) + { + /* compute literal cost */ + uint32_t lit = 0, n_lit = 0; + for ( auto const& c : sop ) + { + lit += c.num_literals(); + } + for ( auto const& c : n_sop ) + { + n_lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + sop.swap( n_sop ); + } + } + } + + /* check size of SOP < max_cubes */ + if ( sop.size() > max_cubes ) + { + cut->data.ignore = true; + } + + isops.push_back( sop ); + return; + } + + void compute_balancing_cost( cut_t& cut ) + { + uint32_t decomposition_size = 0; + uint32_t decomposition_delay = 0; + + auto const& sop = isops[cut->func_id >> 1]; + + if ( cut->data.ignore || sop.size() > max_cubes ) + return; + + /* specific case size = 0 or = 1 */ + if ( cut.size() < 2 ) + return; + + /* collect arrival times for fanin */ + std::array arrival_pin; + unsigned i = 0; + for ( auto l : cut ) + arrival_pin[i++] = cuts[l].best()->data.delay; + + cubes_queue_t terms; + + /* get terms delay */ + assert( sop.size() <= max_cubes ); + for ( kitty::cube const& c : sop ) + { + cubes_queue_t lits; + for ( i = 0; i < cut.size(); ++i ) + { + if ( c.get_mask( i ) ) + { + lits.push( arrival_pin[i] ); + } + } + + if ( lits.size() == 0 ) + continue; + + decomposition_size += lits.size() - 1; + terms.push( compute_balancing_cost_term( lits ) ); + } + + assert( terms.size() > 0 ); + + decomposition_size += terms.size() - 1; + decomposition_delay = compute_balancing_cost_term( terms ); + + cut->data.delay = decomposition_delay; + cut->data.lut_area = decomposition_size; + cut->data.lut_delay = 1; /* not used */ + cut->data.area_flow += decomposition_size; + /* edge flow not used */ + } + + inline uint32_t compute_balancing_cost_term( cubes_queue_t& terms ) + { + while ( terms.size() != 1 ) + { + uint32_t l0 = terms.top(); + terms.pop(); + uint32_t l1 = terms.top(); + terms.pop(); + terms.push( std::max( l0, l1 ) + 1 ); + } + + return terms.top(); + } + + void compute_balancing_cost_required( uint32_t index ) + { + cut_t const& cut = cuts[index][0]; + + if ( cut.size() == 0 ) + return; + + /* propagate unit delay back */ + if ( cut.size() == 1 ) + { + for ( auto l : cut ) + { + node_match[l].required = std::min( node_match[l].required, node_match[index].required ); + } + } + + /* collect arrival times for fanin */ + std::array, max_sop_decomp_size> connections; + std::array arrival_pin; + std::array required; + unsigned size = 0; + for ( auto l : cut ) + { + arrival_pin[size] = cuts[l].best()->data.delay; + connections[size] = std::make_pair( size, size ); + ++size; + } + + auto priority_cmp = []( std::pair const& a, std::pair const& b ) { return a.first > b.first; }; + using cubes_queue2_t = std::priority_queue, std::vector>, decltype( priority_cmp )>; + + cubes_queue2_t terms( priority_cmp ); + + /* get terms delay */ + auto const& sop = isops[cut->func_id >> 1]; + assert( sop.size() <= max_cubes ); + for ( kitty::cube const& c : sop ) + { + cubes_queue2_t lits( priority_cmp ); + for ( auto i = 0; i < cut.size(); ++i ) + { + if ( c.get_mask( i ) ) + { + lits.push( { arrival_pin[i], i } ); + } + } + + if ( lits.size() == 0 ) + continue; + + compute_balancing_cost_required_term( lits, connections, size ); + assert( size <= connections.size() ); + + terms.push( lits.top() ); + } + + assert( terms.size() > 0 ); + compute_balancing_cost_required_term( terms, connections, size ); + + uint32_t required_node = node_match[index].required; + assert( terms.top().first == cut->data.delay ); + assert( required_node >= terms.top().first ); + + /* init required times */ + for ( auto i = 0; i < size - 1; ++i ) + required[i] = UINT32_MAX; + required[size - 1] = required_node; + + compute_balancing_cost_propagate_required( connections, required, size, cut.size() ); + + /* assign required time */ + uint32_t ctr = 0; + for ( auto l : cut ) + { + node_match[l].required = std::min( node_match[l].required, required[ctr++] ); + assert( node_match[l].required >= cuts[l][0]->data.delay ); + } + } + + template + inline void compute_balancing_cost_required_term( Queue& terms, std::array, max_sop_decomp_size>& connections, uint32_t& size ) + { + while ( terms.size() != 1 ) + { + std::pair l0 = terms.top(); + terms.pop(); + std::pair l1 = terms.top(); + terms.pop(); + uint32_t arrival = std::max( l0.first, l1.first ) + 1; + terms.push( { arrival, size } ); + connections[size] = std::make_pair( l0.second, l1.second ); + ++size; + } + } + + inline void compute_balancing_cost_propagate_required( std::array, max_sop_decomp_size> const& connections, std::array& required, uint32_t size, uint32_t leaves ) + { + for ( auto i = size - 1; i >= leaves; --i ) + { + uint32_t time = required[i] - 1; + required[connections[i].first] = std::min( time, required[connections[i].first] ); + required[connections[i].second] = std::min( time, required[connections[i].second] ); + } + } +#pragma endregion + +private: + Ntk& ntk; + lut_map_params const& ps; + lut_map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + uint32_t area_iteration{ 0 }; /* current area iteration */ + uint32_t delay{ 0 }; /* current delay of the mapping */ + uint32_t area{ 0 }; /* current area of the mapping */ + uint32_t edges{ 0 }; /* current edges of the mapping */ + uint32_t cuts_total{ 0 }; /* current computed cuts */ + const float epsilon{ 0.005f }; /* epsilon */ + LUTCostFn lut_cost{}; + + std::vector topo_order; + std::vector tmp_visited; + std::vector node_match; + + std::vector cuts; /* compressed representation of cuts */ + cut_merge_t lcuts; /* cut merger container */ + tt_cache truth_tables; /* cut truth tables */ + cost_cache truth_tables_cost; /* truth tables cost */ + isop_cache isops; /* cache for isops */ +}; +#pragma endregion + +} /* namespace detail */ + +/*! \brief LUT mapper. + * + * This function implements a LUT mapping algorithm. It is controlled by one + * template argument `ComputeTruth` (defaulted to `false`) which controls + * whether the LUT function is computed or the mapping is structural. In the + * former case, truth tables are computed during cut enumeration, + * which requires more runtime. + * + * This function returns a k-LUT network. + * + * The template `LUTCostFn` sets the cost function to evaluate depth and + * size of a truth table given its support size if `ComputeTruth` is set + * to false, or its function if `ComputeTruth` is set to true. + * + * This implementation offers more options such as delay oriented mapping + * and edges minimization compared to the command `lut_mapping`. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + */ +template +klut_network lut_map( Ntk& ntk, lut_map_params ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + lut_map_params tps = ps; + lut_map_stats st; + klut_network klut; + + /* adjust params for balancing */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + tps.area_oriented_mapping = false; + tps.recompute_cuts = false; + tps.area_share_rounds = 0; + tps.edge_optimization = false; + tps.cut_expansion = false; + } + + detail::lut_map_impl p( ntk, tps, st ); + klut = p.run(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst != nullptr ) + { + *pst = st; + } + + return klut; +} + +/*! \brief LUT mapper inplace. + * + * This function implements a LUT mapping algorithm. It is controlled by one + * template argument `StoreFunction` (defaulted to `false`) which controls + * whether the LUT function is stored in the mapping. In that case + * truth tables are computed during cut enumeration, which requires more + * runtime. + * + * The input network must be wrapped in a `mapping_view`. The computed mapping + * is stored in the view. In this version, some features of the mapper are + * disabled, such as on-the-fly decompositions, due to incompatibility. + * + * The template `LUTCostFn` sets the cost function to evaluate depth and + * size of a truth table given its support size, if `StoreFunction` is set + * to false, or its function, if `StoreFunction` is set to true. + * + * This implementation offers more options such as delay oriented mapping + * and edges minimization compared to the command `lut_mapping`. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_lut_function` (if `StoreFunction` is true) + * + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was inspired by the LUT + mapping command ``&if`` in ABC. + \endverbatim + */ +template +void lut_map_inplace( Ntk& ntk, lut_map_params const& ps = {}, lut_map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + + lut_map_params tps = ps; + lut_map_stats st; + + /* adjust params for balancing */ + if ( ps.sop_balancing || ps.esop_balancing ) + { + tps.area_oriented_mapping = false; + tps.recompute_cuts = false; + tps.area_share_rounds = 0; + tps.edge_optimization = false; + tps.cut_expansion = false; + } + + detail::lut_map_impl p( ntk, tps, st ); + p.run_inplace(); + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst != nullptr ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/lut_mapping.hpp b/include/mockturtle/algorithms/lut_mapping.hpp new file mode 100644 index 0000000..8874873 --- /dev/null +++ b/include/mockturtle/algorithms/lut_mapping.hpp @@ -0,0 +1,546 @@ +/* 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 lut_mapping.hpp + \brief LUT mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include + +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/mf_cut.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for lut_mapping. + * + * The data structure `lut_mapping_params` holds configurable parameters + * with default arguments for `lut_mapping`. + */ +struct lut_mapping_params +{ + lut_mapping_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 8; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut size is 6, the default cut limit is 8. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Number of rounds for area flow optimization. + * + * The first round is used for delay optimization. + */ + uint32_t rounds{ 2u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t rounds_ela{ 1u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for lut_mapping. + * + * The data structure `lut_mapping_stats` provides data collected by running + * `lut_mapping`. + */ +struct lut_mapping_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +/* function to update all cuts after cut enumeration */ +template +struct lut_mapping_update_cuts +{ + template + static void apply( NetworkCuts const& cuts, Ntk const& ntk ) + { + (void)cuts; + (void)ntk; + } +}; + +namespace detail +{ + +template +class lut_mapping_impl +{ +public: + using network_cuts_t = network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + lut_mapping_impl( Ntk& ntk, lut_mapping_params const& ps, lut_mapping_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + flow_refs( ntk.size() ), + map_refs( ntk.size(), 0 ), + flows( ntk.size() ), + delays( ntk.size() ), + cuts( cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + lut_mapping_update_cuts().apply( cuts, ntk ); + } + + void run() + { + stopwatch t( st.time_total ); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + init_nodes(); + // print_state(); + + set_mapping_refs(); + // print_state(); + + while ( iteration < ps.rounds ) + { + compute_mapping(); + } + + while ( iteration < ps.rounds + ps.rounds_ela ) + { + compute_mapping(); + } + + derive_mapping(); + } + +private: + uint32_t cut_area( cut_t const& cut ) const + { + return static_cast( cut->data.cost ); + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto n, auto ) { + const auto index = ntk.node_to_index( n ); + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + flow_refs[index] = 1.0f; + } + else + { + flow_refs[index] = static_cast( ntk.fanout_size( n ) ); + } + + flows[index] = cuts.cuts( index )[0]->data.flow; + delays[index] = cuts.cuts( index )[0]->data.delay; + } ); + } + + template + void compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + compute_best_cut( ntk.node_to_index( n ) ); + } + set_mapping_refs(); + // print_state(); + } + + template + void set_mapping_refs() + { + const auto coef = 1.0f / ( 1.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + /* compute current delay and update mapping refs */ + delay = 0; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + delay = std::max( delay, delays[index] ); + + if constexpr ( !ELA ) + { + map_refs[index]++; + } + } ); + + /* compute current area and update mapping refs */ + area = 0; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + /* skip constants and PIs (TODO: stop earlier) */ + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + continue; + + const auto index = ntk.node_to_index( *it ); + if ( map_refs[index] == 0 ) + continue; + + if constexpr ( !ELA ) + { + for ( auto leaf : cuts.cuts( index )[0] ) + { + map_refs[leaf]++; + } + } + area++; + } + + /* blend flow references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + flow_refs[i] = coef * flow_refs[i] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( map_refs[i] ) ); + } + + ++iteration; + } + + std::pair cut_flow( cut_t const& cut ) + { + uint32_t time{ 0u }; + float flow{ 0.0f }; + + for ( auto leaf : cut ) + { + time = std::max( time, delays[leaf] ); + flow += flows[leaf]; + } + + return { flow + cut_area( cut ), time + 1u }; + } + + /* reference cut: + * adds cut to current mapping and recursively adds best cuts of leaf + * nodes, if they are not part of the current mapping. + */ + uint32_t cut_ref( cut_t const& cut ) + { + uint32_t count = cut_area( cut ); + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + if ( map_refs[leaf]++ == 0 ) + { + count += cut_ref( cuts.cuts( leaf )[0] ); + } + } + return count; + } + + /* dereference cut: + * removes cut from current mapping and recursively removes best cuts of + * leaf nodes, if they are part of the current mapping. + * (this is the inverse operation to cut_ref) + */ + uint32_t cut_deref( cut_t const& cut ) + { + uint32_t count = cut_area( cut ); + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + if ( --map_refs[leaf] == 0 ) + { + count += cut_deref( cuts.cuts( leaf ).best() ); + } + } + return count; + } + + /* reference cut (special version): + * this special version of cut_ref does two additional things: + * 1. it stops recursing if it has found `limit` cuts + * 2. it remembers all cuts for which the reference count increases in the + * vector `tmp_area`. + */ + uint32_t cut_ref_limit_save( cut_t const& cut, uint32_t limit ) + { + uint32_t count = cut_area( cut ); + if ( limit == 0 ) + return count; + + for ( auto leaf : cut ) + { + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) || ntk.is_ci( ntk.index_to_node( leaf ) ) ) + continue; + + tmp_area.push_back( leaf ); + if ( map_refs[leaf]++ == 0 ) + { + count += cut_ref_limit_save( cuts.cuts( leaf ).best(), limit - 1 ); + } + } + return count; + } + + /* estimates the cost of adding this cut to the mapping: + * This algorithm references cuts recursively to estimate how many cuts + * would be needed to add to the mapping if `cut` were to be added. It + * temporarily modifies the reference counters but reverts them eventually. + */ + uint32_t cut_area_estimation( cut_t const& cut ) + { + tmp_area.clear(); + const auto count = cut_ref_limit_save( cut, 8 ); + for ( auto const& n : tmp_area ) + { + map_refs[n]--; + } + return count; + } + + template + void compute_best_cut( uint32_t index ) + { + constexpr auto mf_eps{ 0.005f }; + + float flow; + uint32_t time{ 0 }; + int32_t best_cut{ -1 }; + float best_flow{ std::numeric_limits::max() }; + uint32_t best_time{ std::numeric_limits::max() }; + int32_t cut_index{ -1 }; + + if constexpr ( ELA ) + { + if ( map_refs[index] > 0 ) + { + cut_deref( cuts.cuts( index )[0] ); + } + } + + for ( auto* cut : cuts.cuts( index ) ) + { + ++cut_index; + if ( cut->size() == 1 ) + continue; + + if constexpr ( ELA ) + { + flow = static_cast( cut_area_estimation( *cut ) ); + } + else + { + std::tie( flow, time ) = cut_flow( *cut ); + } + + if ( best_cut == -1 || best_flow > flow + mf_eps || ( best_flow > flow - mf_eps && best_time > time ) ) + { + best_cut = cut_index; + best_flow = flow; + best_time = time; + } + } + + if ( best_cut == -1 ) + return; + + if constexpr ( ELA ) + { + if ( map_refs[index] > 0 ) + { + cut_ref( cuts.cuts( index )[best_cut] ); + } + } + else + { + map_refs[index] = 0; + } + if constexpr ( ELA ) + { + best_time = cut_flow( cuts.cuts( index )[best_cut] ).second; + } + delays[index] = best_time; + flows[index] = best_flow / flow_refs[index]; + + if ( best_cut != 0 ) + { + cuts.cuts( index ).update_best( best_cut ); + } + } + + void derive_mapping() + { + ntk.clear_mapping(); + + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( map_refs[index] == 0 ) + continue; + + std::vector> nodes; + for ( auto const& l : cuts.cuts( index ).best() ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, cuts.truth_table( cuts.cuts( index ).best() ) ); + } + } + } + + void print_state() + { + for ( auto i = 0u; i < ntk.size(); ++i ) + { + std::cout << fmt::format( "*** Obj = {:>3} (node = {:>3}) FlowRefs = {:5.2f} MapRefs = {:>2} Flow = {:5.2f} Delay = {:>3}\n", i, ntk.index_to_node( i ), flow_refs[i], map_refs[i], flows[i], delays[i] ); + // std::cout << cuts.cuts( i ); + } + std::cout << fmt::format( "Level = {} Area = {}\n", delay, area ); + } + +private: + Ntk& ntk; + lut_mapping_params const& ps; + lut_mapping_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + uint32_t delay{ 0 }; /* current delay of the mapping */ + uint32_t area{ 0 }; /* current area of the mapping */ + // bool ela{false}; /* compute exact area */ + + std::vector> top_order; + std::vector flow_refs; + std::vector map_refs; + std::vector flows; + std::vector delays; + network_cuts_t cuts; + + std::vector tmp_area; /* temporary vector to compute exact area */ +}; + +}; /* namespace detail */ + +/*! \brief LUT mapping. + * + * This function implements a LUT mapping algorithm. It is controlled by two + * template arguments `StoreFunction` (defaulted to `true`) and `CutData` + * (defaulted to `cut_enumeration_mf_cut`). The first argument `StoreFunction` + * controls whether the LUT function is stored in the mapping. In that case + * truth tables are computed during cut enumeration, which requires more + * runtime. The second argument is simuilar to the `CutData` argument in + * `cut_enumeration`, which can specialize the cost function to select priority + * cuts and store additional data. For LUT mapping using this function the + * type passed as `CutData` must implement the following three fields: + * + * - `uint32_t delay` + * - `float flow` + * - `float costs` + * + * See `include/mockturtle/algorithms/cut_enumeration/mf_cut.hpp` for one + * example of a CutData type that implements the cost function that is used in + * the LUT mapper `&mf` in ABC. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_lut_function` (if `StoreFunction` is true) + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired but the LUT + mapping command ``&mf`` in ABC. + \endverbatim + */ +template +void lut_mapping( Ntk& ntk, lut_mapping_params const& ps = {}, lut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + lut_mapping_stats st; + detail::lut_mapping_impl p( ntk, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/mapper.hpp b/include/mockturtle/algorithms/mapper.hpp new file mode 100644 index 0000000..441f981 --- /dev/null +++ b/include/mockturtle/algorithms/mapper.hpp @@ -0,0 +1,3483 @@ +/* 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 mapper.hpp + \brief Mapper + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include + +#include "../networks/aig.hpp" +#include "../networks/klut.hpp" +#include "../networks/mig.hpp" +#include "../networks/sequential.hpp" +#include "../networks/xag.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/tech_library.hpp" +#include "../views/binding_view.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/exact_map_cut.hpp" +#include "cut_enumeration/tech_map_cut.hpp" +#include "detail/mffc_utils.hpp" +#include "detail/switching_activity.hpp" +#include "reconv_cut.hpp" +#include "resyn_engines/mig_resyn.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for map. + * + * The data structure `map_params` holds configurable parameters + * with default arguments for `map`. + */ +struct map_params +{ + map_params() + { + cut_enumeration_ps.cut_limit = 49; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut limit is 49. By default, + * truth table minimization is performed. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Required time for delay optimization. */ + double required_time{ 0.0f }; + + /*! \brief Skip delay round for area optimization. */ + bool skip_delay_round{ false }; + + /*! \brief Number of rounds for area flow optimization. */ + uint32_t area_flow_rounds{ 1u }; + + /*! \brief Number of rounds for exact area optimization. */ + uint32_t ela_rounds{ 2u }; + + /*! \brief Number of rounds for exact switching power optimization. */ + uint32_t eswp_rounds{ 0u }; + + /*! \brief Number of patterns for switching activity computation. */ + uint32_t switching_activity_patterns{ 2048u }; + + /*! \brief Exploit logic sharing in exact area optimization of graph mapping. */ + bool enable_logic_sharing{ false }; + + /*! \brief Maximum number of cuts evaluated for logic sharing. */ + uint32_t logic_sharing_cut_limit{ 8u }; + + /*! \brief Use satisfiability don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 12u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for mapper. + * + * The data structure `map_stats` provides data collected by running + * `map`. + */ +struct map_stats +{ + /*! \brief Area result. */ + double area{ 0 }; + /*! \brief Worst delay result. */ + double delay{ 0 }; + /*! \brief Power result. */ + double power{ 0 }; + + /*! \brief Runtime for covering. */ + stopwatch<>::duration time_mapping{ 0 }; + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Cut enumeration stats. */ + cut_enumeration_stats cut_enumeration_st{}; + + /*! \brief Delay and area stats for each round. */ + std::vector round_stats{}; + + /*! \brief Mapping error. */ + bool mapping_error{ false }; + + void report() const + { + for ( auto const& stat : round_stats ) + { + std::cout << stat; + } + std::cout << fmt::format( "[i] Area = {:>5.2f}; Delay = {:>5.2f};", area, delay ); + if ( power != 0 ) + std::cout << fmt::format( " Power = {:>5.2f};\n", power ); + else + std::cout << "\n"; + std::cout << fmt::format( "[i] Mapping runtime = {:>5.2f} secs\n", to_seconds( time_mapping ) ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +struct cut_match_tech +{ + /* list of supergates matching the cut for positive and negative output phases */ + std::array> const*, 2> supergates = { nullptr, nullptr }; + /* input negations, 0: pos, 1: neg */ + std::array negations{ 0, 0 }; +}; + +template +struct node_match_tech +{ + /* best gate match for positive and negative output phases */ + supergate const* best_supergate[2] = { nullptr, nullptr }; + /* fanin pin phases for both output phases */ + uint8_t phase[2]; + /* best cut index for both phases */ + uint32_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match{ false }; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg, 2: pos+neg */ + uint32_t map_refs[3]; + /* references estimation */ + float est_refs[3]; + /* area flow */ + float flows[3]; +}; + +template +class tech_map_impl +{ +public: + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + using match_map = std::unordered_map>>; + using klut_map = std::unordered_map, 2>>; + using map_ntk_t = binding_view; + using seq_map_ntk_t = binding_view>; + +public: + explicit tech_map_impl( Ntk const& ntk, tech_library const& library, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + matches(), + switch_activity( ps.eswp_rounds ? switching_activity( ntk, ps.switching_activity_patterns ) : std::vector( 0 ) ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps, &st.cut_enumeration_st ) ) + { + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + } + + explicit tech_map_impl( Ntk const& ntk, tech_library const& library, std::vector const& switch_activity, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + node_match( ntk.size() ), + matches(), + switch_activity( switch_activity ), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps, &st.cut_enumeration_st ) ) + { + std::tie( lib_inv_area, lib_inv_delay, lib_inv_id ) = library.get_inverter_info(); + std::tie( lib_buf_area, lib_buf_delay, lib_buf_id ) = library.get_buffer_info(); + } + + map_ntk_t run() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_map_network(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + compute_matches(); + + /* init the data structure */ + init_nodes(); + + /* execute mapping */ + if ( !execute_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + + return res; + } + + seq_map_ntk_t run_seq() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_map_seq_network(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + compute_matches(); + + /* init the data structure */ + init_nodes(); + + /* execute mapping */ + if ( !execute_mapping() ) + return res; + + /* insert buffers for POs driven by PIs */ + insert_buffers(); + + /* generate the output network */ + finalize_cover( res, old2new ); + + return res; + } + +private: + bool execute_mapping() + { + /* compute mapping for delay */ + if ( !ps.skip_delay_round ) + { + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using global area flow */ + while ( iteration < ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping() ) + { + return false; + } + } + + /* compute mapping using exact area */ + while ( iteration < ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping_exact() ) + { + return false; + } + } + + /* compute mapping using exact switching activity estimation */ + while ( iteration < ps.eswp_rounds + ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping_exact() ) + { + return false; + } + } + + return true; + } + + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n, auto ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = node_data.est_refs[2] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + match_constants( index ); + } + else if ( ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.arrival[1] = lib_inv_delay; + } + } ); + } + + void compute_matches() + { + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + + std::vector> node_matches; + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::extend_to<6>( tt ); + auto fe_canon = fe; + + uint8_t negations_pos = 0; + uint8_t negations_neg = 0; + + /* match positive polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization( fe ); + fe_canon = std::get<0>( canon ); + negations_pos = std::get<1>( canon ); + } + auto const supergates_pos = library.get_supergates( fe_canon ); + + /* match negative polarity */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization( ~fe ); + fe_canon = std::get<0>( canon ); + negations_neg = std::get<1>( canon ); + } + else + { + fe_canon = ~fe; + } + auto const supergates_neg = library.get_supergates( fe_canon ); + + if ( supergates_pos != nullptr || supergates_neg != nullptr ) + { + cut_match_tech match{ { supergates_pos, supergates_neg }, { negations_pos, negations_neg } }; + + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + template + bool compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + continue; + } + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0 ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool compute_mapping_exact() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0 ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + if constexpr ( SwitchActivity ) + stats << fmt::format( "[i] Switching: Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + else + stats << fmt::format( "[i] Area : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + template + bool set_mapping_refs() + { + const auto coef = 1.0f / ( 2.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs[0] = node_match[i].map_refs[1] = node_match[i].map_refs[2] = 0u; + } + } + + /* compute the current worst delay and update the mapping refs */ + delay = 0.0f; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + node_match[index].map_refs[2]++; + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + auto& node_data = node_match[index]; + + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + if ( node_match[index].map_refs[2] > 0u ) + { + /* if used and not available in the library launch a mapping error */ + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + { + std::cerr << "[i] MAP ERROR: technology library does not contain constant gates, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + } + continue; + } + else if ( ntk.is_ci( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter area over the negated fanins */ + area += lib_inv_area; + } + continue; + } + + /* continue if not referenced in the cover */ + if ( node_match[index].map_refs[2] == 0u ) + continue; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_supergate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[i] MAP ERROR: technology library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto ctr = 0u; + + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> ctr++ ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + /* blend estimated references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[2] = coef * node_match[i].est_refs[2] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[2] ) ); + node_match[i].est_refs[1] = coef * node_match[i].est_refs[1] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[1] ) ); + node_match[i].est_refs[0] = coef * node_match[i].est_refs[0] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[0] ) ); + } + + ++iteration; + return true; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return in case of `skip_delay_round` */ + if ( iteration == 0 ) + return; + + auto required = delay; + + if ( ps.required_time != 0.0f ) + { + /* Global target time constraint */ + if ( ps.required_time < delay - epsilon ) + { + if ( !ps.skip_delay_round && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + /* propagate required time to the PIs */ + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + if ( ntk.is_ci( *it ) || ntk.is_constant( *it ) ) + break; + + const auto index = ntk.node_to_index( *it ); + + if ( node_match[index].map_refs[2] == 0 ) + continue; + + auto& node_data = node_match[index]; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_supergate[0] != nullptr || node_data.best_supergate[1] != nullptr ); + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over the output inverter if present */ + if ( node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& supergate = node_data.best_supergate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( index )[node_data.best_cut[other_phase]]; + auto const& supergate = node_data.best_supergate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> ctr ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[ctr] ); + ++ctr; + } + } + } + } + + template + void match_phase( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + double best_area_flow = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area_flow = best_supergate->area + cut_leaves_flow( cut, n, phase ); + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_supergate->tdelay[ctr]; + best_arrival = std::max( best_arrival, arrival_pin ); + ++ctr; + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index].supergates; + auto const negation = cut_matches[( *cut )->data.match_index].negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint8_t gate_polarity = gate.polarity ^ negation; + node_data.phase[phase] = gate_polarity; + double area_local = gate.area + cut_leaves_flow( *cut, n, phase ); + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if constexpr ( DO_AREA ) + { + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + } + + if ( compare_map( worst_arrival, best_arrival, area_local, best_area_flow, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_area_flow = area_local; + best_size = cut->size(); + best_cut = cut_index; + best_area = gate.area; + best_phase = gate_polarity; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_area_flow; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + } + + template + void match_phase_exact( node const& n, uint8_t phase ) + { + double best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + + auto ctr = 0u; + for ( auto l : cut ) + { + double arrival_pin = node_match[l].arrival[( best_phase >> ctr ) & 1] + best_supergate->tdelay[ctr]; + best_arrival = std::max( best_arrival, arrival_pin ); + ++ctr; + } + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index].supergates; + auto const negation = cut_matches[( *cut )->data.match_index].negations[phase]; + + if ( supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates[phase] ) + { + uint8_t gate_polarity = gate.polarity ^ negation; + node_data.phase[phase] = gate_polarity; + node_data.area[phase] = gate.area; + float area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + double worst_arrival = 0.0f; + + auto ctr = 0u; + for ( auto l : *cut ) + { + double arrival_pin = node_match[l].arrival[( gate_polarity >> ctr ) & 1] + gate.tdelay[ctr]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + ++ctr; + } + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = gate_polarity; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + template + void match_drop_phase( node const& n, float required_margin_factor ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + double worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + double worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival improves matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < ( node_data.required[1] + epsilon - required_margin_factor * lib_inv_delay ); + use_one = worst_arrival_npos < ( node_data.required[0] + epsilon - required_margin_factor * lib_inv_delay ); + } + + /* condition on not used phases, evaluate a substitution during exact area recovery */ + if constexpr ( ELA ) + { + if ( iteration != 0 ) + { + if ( node_data.map_refs[0] == 0 || node_data.map_refs[1] == 0 ) + { + /* select the used match */ + auto phase = 0; + auto nphase = 0; + if ( node_data.map_refs[0] == 0 ) + { + phase = 1; + use_one = true; + use_zero = false; + } + else + { + nphase = 1; + use_one = false; + use_zero = true; + } + /* select the not used match instead if it leads to area improvement and doesn't violate the required time */ + if ( node_data.arrival[nphase] + lib_inv_delay < node_data.required[phase] + epsilon ) + { + auto size_phase = cuts.cuts( index )[node_data.best_cut[phase]].size(); + auto size_nphase = cuts.cuts( index )[node_data.best_cut[nphase]].size(); + + if ( compare_map( node_data.arrival[nphase] + lib_inv_delay, node_data.arrival[phase], node_data.flows[nphase] + lib_inv_area, node_data.flows[phase], size_nphase, size_phase ) ) + { + /* invert the choice */ + use_zero = !use_zero; + use_one = !use_one; + } + } + } + } + } + + if ( !use_zero && !use_one ) + { + /* use both phases */ + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + node_data.flows[2] = node_data.flows[0] + node_data.flows[1]; + node_data.same_match = false; + return; + } + + /* use area flow as a tiebreaker */ + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the negative phase cut if in use */ + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + /* reference the positive cut if not in use before */ + if ( node_data.map_refs[0] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + /* set cut references */ + if ( !node_data.same_match ) + { + /* dereference the positive phase cut if in use */ + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + /* reference the negative cut if not in use before */ + if ( node_data.map_refs[1] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, double worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_supergate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase]; + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[2]; + node_data.flows[phase_n] = node_data.flows[phase]; + node_data.flows[2] = node_data.flows[phase]; + } + + void match_constants( uint32_t index ) + { + auto& node_data = node_match[index]; + + kitty::static_truth_table<6> zero_tt; + auto const supergates_zero = library.get_supergates( zero_tt ); + auto const supergates_one = library.get_supergates( ~zero_tt ); + + /* Not available in the library */ + if ( supergates_zero == nullptr && supergates_one == nullptr ) + { + return; + } + /* if only one is available, the other is obtained using an inverter */ + if ( supergates_zero != nullptr ) + { + node_data.best_supergate[0] = &( ( *supergates_zero )[0] ); + node_data.arrival[0] = node_data.best_supergate[0]->tdelay[0]; + node_data.area[0] = node_data.best_supergate[0]->area; + node_data.phase[0] = 0; + } + if ( supergates_one != nullptr ) + { + node_data.best_supergate[1] = &( ( *supergates_one )[0] ); + node_data.arrival[1] = node_data.best_supergate[1]->tdelay[0]; + node_data.area[1] = node_data.best_supergate[1]->area; + node_data.phase[1] = 0; + } + else + { + node_data.same_match = true; + node_data.arrival[1] = node_data.arrival[0] + lib_inv_delay; + node_data.area[1] = node_data.area[0] + lib_inv_area; + node_data.phase[1] = 1; + } + if ( supergates_zero == nullptr ) + { + node_data.same_match = true; + node_data.arrival[0] = node_data.arrival[1] + lib_inv_delay; + node_data.area[0] = node_data.area[1] + lib_inv_area; + node_data.phase[0] = 1; + } + } + + inline double cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + double flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + template + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + ++node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs[2]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + ++node_match[leaf].map_refs[2]; + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + template + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + float count; + + if constexpr ( SwitchActivity ) + count = switch_activity[ntk.node_to_index( n )]; + else + count = node_data.area[phase]; + + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> ctr++ ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + } + else + { + --node_match[leaf].map_refs[0]; + } + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + { + if constexpr ( SwitchActivity ) + count += switch_activity[leaf]; + else + count += lib_inv_area; + } + /* Recursive dereferencing */ + if ( --node_match[leaf].map_refs[2] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + --node_match[leaf].map_refs[2]; + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + } + return count; + } + + void insert_buffers() + { + if ( lib_buf_id != UINT32_MAX ) + { + double area_old = area; + bool buffers = false; + + ntk.foreach_co( [&]( auto const& f ) { + auto const& n = ntk.get_node( f ); + if ( !ntk.is_constant( n ) && ntk.is_ci( n ) && !ntk.is_complemented( f ) ) + { + area += lib_buf_area; + delay = std::max( delay, node_match[ntk.node_to_index( n )].arrival[0] + lib_inv_delay ); + buffers = true; + } + } ); + + /* round stats */ + if ( ps.verbose && buffers ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + area_gain = float( ( area_old - area ) / area_old * 100 ); + + stats << fmt::format( "[i] Buffering: Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + } + } + + std::pair initialize_map_network() + { + map_ntk_t dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + + return { dest, old2new }; + } + + std::pair initialize_map_seq_network() + { + seq_map_ntk_t dest( library.get_gates() ); + klut_map old2new; + + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][0] = dest.get_constant( false ); + old2new[ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) )][1] = dest.get_constant( true ); + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_pi(); + } ); + ntk.foreach_ro( [&]( auto const& n ) { + old2new[ntk.node_to_index( n )][0] = dest.create_ro(); + } ); + + return { dest, old2new }; + } + + template + void finalize_cover( NtkDest& res, klut_map& old2new ) + { + for ( auto const& n : top_order ) + { + auto index = ntk.node_to_index( n ); + auto const& node_data = node_match[index]; + + /* add inverter at PI if needed */ + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + continue; + } + else if ( ntk.is_ci( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + { + old2new[index][1] = res.create_not( old2new[n][0] ); + res.add_binding( res.get_node( old2new[index][1] ), lib_inv_id ); + } + continue; + } + + /* continue if cut is not in the cover */ + if ( node_data.map_refs[2] == 0u ) + continue; + + unsigned phase = ( node_data.best_supergate[0] != nullptr ) ? 0 : 1; + + /* add used cut */ + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + + /* add inverted version if used */ + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + { + old2new[index][phase ^ 1] = res.create_not( old2new[index][phase] ); + res.add_binding( res.get_node( old2new[index][phase ^ 1] ), lib_inv_id ); + } + } + + phase = phase ^ 1; + /* add the optional other match if used */ + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + create_lut_for_gate( res, old2new, index, phase ); + } + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_ci( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for POs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_po( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_po( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + res.create_ri( old2new[ntk.node_to_index( ntk.get_node( f ) )][1] ); + } + else if ( !ntk.is_constant( ntk.get_node( f ) ) && ntk.is_ci( ntk.get_node( f ) ) && lib_buf_id != UINT32_MAX ) + { + /* create buffers for RIs */ + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + const auto buf = res.create_node( { old2new[ntk.node_to_index( ntk.get_node( f ) )][0] }, tt_buf ); + res.create_ri( buf ); + res.add_binding( res.get_node( buf ), lib_buf_id ); + } + else + { + res.create_ri( old2new[ntk.node_to_index( ntk.get_node( f ) )][0] ); + } + } ); + } + + /* write final results */ + st.area = area; + st.delay = delay; + if ( ps.eswp_rounds ) + st.power = compute_switching_power(); + } + + template + void create_lut_for_gate( NtkDest& res, klut_map& old2new, uint32_t index, unsigned phase ) + { + auto const& node_data = node_match[index]; + auto& best_cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& gate = node_data.best_supergate[phase]->root; + + /* permutate and negate to obtain the matched gate truth table */ + std::vector> children( gate->num_vars ); + + auto ctr = 0u; + for ( auto l : best_cut ) + { + if ( ctr >= gate->num_vars ) + break; + children[node_data.best_supergate[phase]->permutation[ctr]] = old2new[l][( node_data.phase[phase] >> ctr ) & 1]; + ++ctr; + } + + if ( !gate->is_super ) + { + /* create the node */ + auto f = res.create_node( children, gate->function ); + res.add_binding( res.get_node( f ), gate->root->id ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + else + { + /* supergate, create sub-gates */ + auto f = create_lut_for_gate_rec( res, *gate, children ); + + /* add the node in the data structure */ + old2new[index][phase] = f; + } + } + + template + signal create_lut_for_gate_rec( NtkDest& res, composed_gate const& gate, std::vector> const& children ) + { + std::vector> children_local( gate.fanin.size() ); + + auto i = 0u; + for ( auto const fanin : gate.fanin ) + { + if ( fanin->root == nullptr ) + { + /* terminal condition */ + children_local[i] = children[fanin->id]; + } + else + { + children_local[i] = create_lut_for_gate_rec( res, *fanin, children ); + } + ++i; + } + + auto f = res.create_node( children_local, gate.root->function ); + res.add_binding( res.get_node( f ), gate.root->id ); + return f; + } + + template + inline bool compare_map( double arrival, double best_arrival, double area_flow, double best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + } + if ( size < best_size ) + { + return true; + } + return false; + } + + double compute_switching_power() + { + double power = 0.0f; + + for ( auto const& n : top_order ) + { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + if ( ntk.is_constant( n ) ) + { + if ( node_data.best_supergate[0] == nullptr && node_data.best_supergate[1] == nullptr ) + continue; + } + else if ( ntk.is_ci( n ) ) + { + if ( node_data.map_refs[1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + continue; + } + + /* continue if cut is not in the cover */ + if ( node_match[index].map_refs[2] == 0u ) + continue; + + unsigned phase = ( node_data.best_supergate[0] != nullptr ) ? 0 : 1; + + if ( node_data.same_match || node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + + if ( node_data.same_match && node_data.map_refs[phase ^ 1] > 0 ) + power += switch_activity[ntk.node_to_index( n )]; + } + + phase = phase ^ 1; + if ( !node_data.same_match && node_data.map_refs[phase] > 0 ) + { + power += switch_activity[ntk.node_to_index( n )]; + } + } + + return power; + } + +private: + Ntk const& ntk; + tech_library const& library; + map_params const& ps; + map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + const float epsilon{ 0.005f }; /* epsilon */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + uint32_t lib_inv_id; + + /* lib buffer info */ + float lib_buf_area; + float lib_buf_delay; + uint32_t lib_buf_id; + + std::vector> top_order; + std::vector> node_match; + match_map matches; + std::vector switch_activity; + network_cuts_t cuts; +}; + +} /* namespace detail */ + +/*! \brief Technology mapping. + * + * This function implements a technology mapping algorithm. It is controlled by a + * template argument `CutData` (defaulted to `cut_enumeration_tech_map_cut`). + * The argument is similar to the `CutData` argument in `cut_enumeration`, which can + * specialize the cost function to select priority cuts and store additional data. + * The default argument gives priority firstly to the cut size, then delay, and lastly + * to area flow. Thus, it is more suited for delay-oriented mapping. + * The type passed as `CutData` must implement the following four fields: + * + * - `uint32_t delay` + * - `float flow` + * - `uint8_t match_index` + * - `bool ignore` + * + * See `include/mockturtle/algorithms/cut_enumeration/cut_enumeration_tech_map_cut.hpp` + * for one example of a CutData type that implements the cost function that is used in + * the technology mapper. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a k-LUT network. Each LUT abstracts a gate of the technology library. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_co` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + * The implementation of this algorithm was inspired by the + * mapping command ``map`` in ABC. + */ +template +binding_view map( Ntk const& ntk, tech_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + map_stats st; + detail::tech_map_impl p( ntk, library, ps, st ); + auto res = p.run(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +/*! \brief Technology mapping for sequential networks. + * + * Version of `map` for technology mapping of sequential networks. + * + * The function returns a sequential k-LUT network. Each LUT abstracts a gate of the technology library. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_ro` + * - `foreach_co` + * - `foreach_ri` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Sequential network + * \param library Technology library + * \param ps Mapping params + * \param pst Mapping statistics + * + */ +template +binding_view> seq_map( Ntk const& ntk, tech_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co method" ); + static_assert( has_foreach_ri_v, "Ntk does not implement the has_foreach_ri method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the has_foreach_po method" ); + static_assert( has_foreach_ro_v, "Ntk does not implement the has_foreach_ro method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + map_stats st; + detail::tech_map_impl p( ntk, library, ps, st ); + auto res = p.run_seq(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose && !st.mapping_error ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return res; +} + +namespace detail +{ + +template +struct cut_match_t +{ + /* list of supergates matching the cut for positive and negative output phases */ + std::vector> const* supergates[2] = { nullptr, nullptr }; + /* input permutations, at index i, it contains the permutated position of i */ + std::array permutation{}; + /* permutated input negations */ + uint8_t negation{ 0 }; +}; + +template +struct node_match_t +{ + /* best supergate match for positive and negative output phases */ + exact_supergate const* best_supergate[2] = { nullptr, nullptr }; + /* fanin pin phases for both output phases */ + uint8_t phase[2]; + /* best cut index for both phases */ + uint32_t best_cut[2]; + /* node is mapped using only one phase */ + bool same_match{ false }; + + /* arrival time at node output */ + double arrival[2]; + /* required time at node output */ + double required[2]; + /* area of the best matches */ + float area[2]; + + /* number of references in the cover 0: pos, 1: neg, 2: pos+neg */ + uint32_t map_refs[3]; + /* references estimation */ + float est_refs[3]; + /* area flow */ + float flows[3]; +}; + +template +class exact_map_impl +{ +public: + static constexpr uint32_t max_window_size = 12; + using network_cuts_t = fast_network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + explicit exact_map_impl( Ntk& ntk, exact_library const& library, map_params const& ps, map_stats& st ) + : ntk( ntk ), + library( library ), + ps( ps ), + st( st ), + lib_database( library.get_database() ), + node_match( ntk.size() ), + matches(), + cuts( fast_cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + std::tie( lib_inv_area, lib_inv_delay ) = library.get_inverter_info(); + } + + NtkDest run() + { + stopwatch t( st.time_mapping ); + + auto [res, old2new] = initialize_dest(); + + /* compute and save topological order */ + top_order.reserve( ntk.size() ); + topo_view( ntk ).foreach_node( [this]( auto n ) { + top_order.push_back( n ); + } ); + + /* match cuts with gates */ + if ( ps.use_dont_cares ) + { + compute_matches_dc(); + } + else + { + compute_matches(); + } + + /* init the data structure */ + init_nodes(); + + /* compute mapping delay */ + if ( !ps.skip_delay_round ) + { + if ( !compute_mapping() ) + { + return res; + } + } + + /* compute mapping using global area flow */ + while ( iteration < ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( !compute_mapping() ) + { + return res; + } + } + + /* compute mapping using exact area */ + while ( iteration < ps.ela_rounds + ps.area_flow_rounds + 1 ) + { + compute_required_time(); + if ( ps.enable_logic_sharing && iteration == ps.ela_rounds + ps.area_flow_rounds ) + { + if ( !compute_exact_area_aggressive( res, old2new ) ) + { + return res; + } + } + else + { + if ( !compute_exact_area() ) + { + return res; + } + } + } + + /* generate the output network using the computed mapping */ + finalize_cover( res, old2new ); + + if ( ps.enable_logic_sharing ) + return cleanup_dangling( res ); + else + return res; + } + +private: + void init_nodes() + { + ntk.foreach_node( [this]( auto const& n, auto ) { + const auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + node_data.est_refs[0] = node_data.est_refs[1] = node_data.est_refs[2] = static_cast( ntk.fanout_size( n ) ); + + if ( ntk.is_constant( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = node_data.arrival[1] = 0.0f; + } + else if ( ntk.is_ci( n ) ) + { + /* all terminals have flow 1.0 */ + node_data.flows[0] = node_data.flows[1] = node_data.flows[2] = 0.0f; + node_data.arrival[0] = 0.0f; + /* PIs have the negative phase implemented with an inverter */ + node_data.arrival[1] = lib_inv_delay; + } + } ); + } + + void compute_matches() + { + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + + std::vector> node_matches; + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + + /* match the cut using canonization and get the gates */ + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::extend_to( tt ); + const auto config = kitty::exact_npn_canonization( fe ); + auto const supergates_npn = library.get_supergates( std::get<0>( config ) ); + auto const supergates_npn_neg = library.get_supergates( ~std::get<0>( config ) ); + + if ( supergates_npn != nullptr || supergates_npn_neg != nullptr ) + { + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + uint8_t phase = ( neg >> NInputs ) & 1; + cut_match_t match; + + match.supergates[phase] = supergates_npn; + match.supergates[phase ^ 1] = supergates_npn_neg; + + /* store permutations and negations */ + match.negation = 0; + for ( auto j = 0u; j < perm.size() && j < NInputs; ++j ) + { + match.permutation[perm[j]] = j; + match.negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + void compute_matches_dc() + { + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.window_size; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl reconv_cuts( ntk, rps, rst ); + + color_view color_ntk{ ntk }; + std::array divisors; + for ( uint32_t i = 0; i < NInputs; ++i ) + { + divisors[i] = i; + } + + /* match gates */ + ntk.foreach_gate( [&]( auto const& n ) { + const auto index = ntk.node_to_index( n ); + std::vector> node_matches; + + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, roots ) }; + window_view window_ntk{ color_ntk, extended_leaves, roots, gates }; + + default_simulator> sim; + const auto tts = simulate_nodes>( window_ntk, sim ); + + auto i = 0u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* ignore unit cut */ + if ( cut->size() == 1 && *cut->begin() == index ) + { + ( *cut )->data.ignore = true; + continue; + } + + if ( cut->size() > NInputs ) + { + /* Ignore cuts too big to be mapped using the library */ + ( *cut )->data.ignore = true; + continue; + } + + /* match the cut using canonization and get the gates */ + const auto tt = cuts.truth_table( *cut ); + const auto fe = kitty::shrink_to( tt ); + + auto [tt_npn, neg, perm] = kitty::exact_npn_canonization( fe ); + auto perm_neg = perm; + auto neg_neg = neg; + + /* dont cares computation */ + kitty::static_truth_table care; + + bool containment = true; + bool filter = false; + for ( auto const& l : *cut ) + { + if ( color_ntk.color( ntk.index_to_node( l ) ) != color_ntk.current_color() ) + { + containment = false; + break; + } + } + + if ( containment ) + { + /* compute care set */ + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + auto j = 0u; + for ( auto const& l : *cut ) + { + entry |= kitty::get_bit( tts[l], i ) << j; + ++j; + } + kitty::set_bit( care, entry ); + } + } + else + { + /* completely specified */ + care = ~care; + } + + auto const dc_npn = apply_npn_transformation( ~care, neg & ~( 1 << NInputs ), perm ); + const std::vector>* supergates_npn = library.get_supergates( tt_npn, dc_npn, neg, perm ); + const std::vector>* supergates_npn_neg = library.get_supergates( ~tt_npn, dc_npn, neg_neg, perm_neg ); + + if ( supergates_npn != nullptr || supergates_npn_neg != nullptr ) + { + cut_match_t match; + + if ( supergates_npn == nullptr ) + { + perm = perm_neg; + neg = neg_neg; + } + + uint8_t phase = ( neg >> NInputs ) & 1; + + match.supergates[phase] = supergates_npn; + match.supergates[phase ^ 1] = supergates_npn_neg; + + /* store permutations and negations */ + match.negation = 0; + for ( auto j = 0u; j < perm.size() && j < NInputs; ++j ) + { + match.permutation[perm[j]] = j; + match.negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + node_matches.push_back( match ); + ( *cut )->data.match_index = i++; + } + else + { + /* Ignore not matched cuts */ + ( *cut )->data.ignore = true; + } + } + + matches[index] = node_matches; + } ); + } + + template + bool compute_mapping() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + /* match positive phase */ + match_phase( n, 0u ); + + /* match negative phase */ + match_phase( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0u ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + std::stringstream stats{}; + float area_gain = 0.0f; + + if ( iteration != 1 ) + area_gain = float( ( area_old - area ) / area_old * 100 ); + + if constexpr ( DO_AREA ) + { + stats << fmt::format( "[i] AreaFlow : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + else + { + stats << fmt::format( "[i] Delay : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + } + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + bool compute_exact_area() + { + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + match_phase_exact( n, 0u ); + + /* match negative phase */ + match_phase_exact( n, 1u ); + + /* try to drop one phase */ + match_drop_phase( n, 0u ); + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + stats << fmt::format( "[i] Area : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + std::pair, Ntk>> initialize_dest() + { + node_map, Ntk> old2new( ntk ); + NtkDest dest; + + old2new[ntk.get_constant( false )] = dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + old2new[ntk.get_constant( true )] = dest.get_constant( true ); + } + + ntk.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + + if constexpr ( has_foreach_ro_v ) + { + ntk.foreach_ro( [&]( auto const& n ) { + old2new[n] = dest.create_ro(); + } ); + } + + return { dest, old2new }; + } + + void finalize_cover( NtkDest& res, node_map, Ntk>& old2new ) + { + if ( !ps.enable_logic_sharing || iteration == ps.area_flow_rounds + 1 ) + { + auto const& db = library.get_database(); + + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return true; + auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs[2] == 0u ) + return true; + + /* get the implemented phase and map the best cut */ + unsigned phase = ( node_match[index].best_supergate[0] != nullptr ) ? 0 : 1; + auto& best_cut = cuts.cuts( index )[node_match[index].best_cut[phase]]; + + std::vector> children( NInputs, res.get_constant( false ) ); + auto const& match = matches[index][best_cut->data.match_index]; + auto const& supergate = node_match[index].best_supergate[phase]; + auto ctr = 0u; + for ( auto l : best_cut ) + { + children[match.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + for ( auto i = 0u; i < NInputs; ++i ) + { + if ( ( match.negation >> i ) & 1 ) + { + children[i] = !children[i]; + } + } + topo_view topo{ db, supergate->root }; + auto f = cleanup_dangling( topo, res, children.begin(), children.end() ).front(); + + if ( phase == 1 ) + f = !f; + + old2new[n] = f; + return true; + } ); + } + + /* create POs */ + ntk.foreach_po( [&]( auto const& f ) { + res.create_po( ntk.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f ) { + res.create_ri( ntk.is_complemented( f ) ? res.create_not( old2new[f] ) : old2new[f] ); + } ); + } + + /* write final results */ + st.area = area; + st.delay = delay; + } + + template + bool set_mapping_refs() + { + const auto coef = 1.0f / ( 2.0f + ( iteration + 1 ) * ( iteration + 1 ) ); + + if constexpr ( !ELA ) + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].map_refs[0] = node_match[i].map_refs[1] = node_match[i].map_refs[2] = 0u; + } + } + + /* compute current delay and update mapping refs */ + delay = 0.0f; + ntk.foreach_co( [this]( auto s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + delay = std::max( delay, node_match[index].arrival[1] ); + else + delay = std::max( delay, node_match[index].arrival[0] ); + + if constexpr ( !ELA ) + { + node_match[index].map_refs[2]++; + if ( ntk.is_complemented( s ) ) + node_match[index].map_refs[1]++; + else + node_match[index].map_refs[0]++; + } + } ); + + /* compute current area and update mapping refs in top-down order */ + area = 0.0f; + for ( auto it = top_order.rbegin(); it != top_order.rend(); ++it ) + { + const auto index = ntk.node_to_index( *it ); + /* skip constants and PIs */ + if ( ntk.is_constant( *it ) ) + { + continue; + } + else if ( ntk.is_ci( *it ) ) + { + if ( node_match[index].map_refs[1] > 0u ) + { + /* Add inverter over the negated fanins */ + area += lib_inv_area; + } + continue; + } + + if ( node_match[index].map_refs[2] == 0u ) + continue; + + auto& node_data = node_match[index]; + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + + if ( node_data.best_supergate[use_phase] == nullptr ) + { + /* Library is not complete, mapping is not possible */ + std::cerr << "[i] MAP ERROR: library is not complete, impossible to perform mapping" << std::endl; + st.mapping_error = true; + return false; + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& match = matches[index][best_cut->data.match_index]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> match.permutation[ctr++] ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + if ( node_data.same_match && node_data.map_refs[use_phase ^ 1] > 0 ) + { + area += lib_inv_area; + } + } + + /* invert the phase */ + use_phase = use_phase ^ 1; + + /* if both phases are implemented and used */ + if ( !node_data.same_match && node_data.map_refs[use_phase] > 0 ) + { + if constexpr ( !ELA ) + { + auto const& best_cut = cuts.cuts( index )[node_data.best_cut[use_phase]]; + auto const& match = matches[index][best_cut->data.match_index]; + auto ctr = 0u; + for ( auto const leaf : best_cut ) + { + node_match[leaf].map_refs[2]++; + if ( ( node_data.phase[use_phase] >> match.permutation[ctr++] ) & 1 ) + node_match[leaf].map_refs[1]++; + else + node_match[leaf].map_refs[0]++; + } + } + area += node_data.area[use_phase]; + } + } + + /* blend flow references */ + for ( auto i = 0u; i < ntk.size(); ++i ) + { + node_match[i].est_refs[2] = coef * node_match[i].est_refs[2] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[2] ) ); + node_match[i].est_refs[1] = coef * node_match[i].est_refs[1] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[1] ) ); + node_match[i].est_refs[0] = coef * node_match[i].est_refs[0] + ( 1.0f - coef ) * std::max( 1.0f, static_cast( node_match[i].map_refs[0] ) ); + } + + ++iteration; + return true; + } + + void compute_required_time() + { + for ( auto i = 0u; i < node_match.size(); ++i ) + { + node_match[i].required[0] = node_match[i].required[1] = std::numeric_limits::max(); + } + + /* return in case of `skip_delay_round` */ + if ( iteration == 0 ) + return; + + auto required = delay; + + if ( ps.required_time != 0.0f ) + { + /* Global target time constraint */ + if ( ps.required_time < delay - epsilon ) + { + if ( !ps.skip_delay_round && iteration == 1 ) + std::cerr << fmt::format( "[i] MAP WARNING: cannot meet the target required time of {:.2f}", ps.required_time ) << std::endl; + } + else + { + required = ps.required_time; + } + } + + /* set the required time at POs */ + ntk.foreach_co( [&]( auto const& s ) { + const auto index = ntk.node_to_index( ntk.get_node( s ) ); + if ( ntk.is_complemented( s ) ) + node_match[index].required[1] = required; + else + node_match[index].required[0] = required; + } ); + + /* propagate required time to the PIs */ + auto i = ntk.size(); + while ( i-- > 0u ) + { + const auto n = ntk.index_to_node( i ); + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + break; + + if ( node_match[i].map_refs[2] == 0 ) + continue; + + auto& node_data = node_match[i]; + + unsigned use_phase = node_data.best_supergate[0] == nullptr ? 1u : 0u; + unsigned other_phase = use_phase ^ 1; + + assert( node_data.best_supergate[0] != nullptr || node_data.best_supergate[1] != nullptr ); + assert( node_data.map_refs[0] || node_data.map_refs[1] ); + + /* propagate required time over output inverter if present */ + if ( node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + node_data.required[use_phase] = std::min( node_data.required[use_phase], node_data.required[other_phase] - lib_inv_delay ); + } + + if ( node_data.same_match || node_data.map_refs[use_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( i )[node_data.best_cut[use_phase]]; + auto const& match = matches[i][best_cut->data.match_index]; + auto const& supergate = node_data.best_supergate[use_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[use_phase] >> match.permutation[ctr] ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[use_phase] - supergate->tdelay[match.permutation[ctr]] ); + ctr++; + } + } + + if ( !node_data.same_match && node_data.map_refs[other_phase] > 0 ) + { + auto ctr = 0u; + auto best_cut = cuts.cuts( i )[node_data.best_cut[other_phase]]; + auto const& match = matches[i][best_cut->data.match_index]; + auto const& supergate = node_data.best_supergate[other_phase]; + for ( auto leaf : best_cut ) + { + auto phase = ( node_data.phase[other_phase] >> match.permutation[ctr] ) & 1; + node_match[leaf].required[phase] = std::min( node_match[leaf].required[phase], node_data.required[other_phase] - supergate->tdelay[match.permutation[ctr]] ); + ctr++; + } + } + } + } + + template + void match_phase( node const& n, uint8_t phase ) + { + float best_arrival = std::numeric_limits::max(); + float best_area_flow = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area_flow = best_supergate->area + cut_leaves_flow( cut, n, phase ); + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( best_phase >> pin ) & 1] + best_supergate->tdelay[pin]; + best_arrival = std::max( best_arrival, arrival_pin ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation ^ gate.polarity; + node_data.phase[phase] = complement; + float area_local = gate.area + cut_leaves_flow( *cut, n, phase ); + float worst_arrival = 0.0f; + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( complement >> pin ) & 1] + gate.tdelay[pin]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + } + + if constexpr ( DO_AREA ) + { + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + } + + if ( compare_map( worst_arrival, best_arrival, area_local, best_area_flow, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_area_flow = area_local; + best_size = cut->size(); + best_cut = cut_index; + best_area = gate.area; + best_phase = complement; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_area_flow; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + } + + void match_phase_exact( node const& n, uint8_t phase ) + { + float best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* recompute best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + best_phase = node_data.phase[phase]; + best_arrival = 0.0f; + best_area = best_supergate->area; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( best_phase >> pin ) & 1] + best_supergate->tdelay[pin]; + best_arrival = std::max( best_arrival, arrival_pin ); + } + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts or not matched cuts */ + if ( ( *cut )->data.ignore ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + /* permutate the children to the NPN-represenentative configuration */ + std::vector children( NInputs, 0u ); + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = l; + } + + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation ^ gate.polarity; + node_data.phase[phase] = complement; + node_data.area[phase] = gate.area; + auto area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + float worst_arrival = 0.0f; + for ( auto pin = 0u; pin < NInputs; pin++ ) + { + float arrival_pin = node_match[children[pin]].arrival[( complement >> pin ) & 1] + gate.tdelay[pin]; + worst_arrival = std::max( worst_arrival, arrival_pin ); + } + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = gate.area; + best_size = cut->size(); + best_cut = cut_index; + best_phase = complement; + best_supergate = &gate; + } + } + + ++cut_index; + } + + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + bool compute_exact_area_aggressive( NtkDest& res, node_map, Ntk>& old2new ) + { + depth_view res_d{ res }; + + for ( auto const& n : top_order ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + continue; + + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* recursively deselect the best cut shared between + * the two phases if in use in the cover */ + if ( node_data.same_match && node_data.map_refs[2] != 0 ) + { + if ( node_data.best_supergate[0] != nullptr ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0u ); + else + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1u ); + } + + /* match positive phase */ + auto sig0 = match_phase_exact_aggressive( res_d, old2new, n, 0u ); + + /* match negative phase */ + auto sig1 = match_phase_exact_aggressive( res_d, old2new, n, 1u ); + + /* try to drop one phase */ + float worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + float worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + recursive_ref( res, res.get_node( sig1 ) ); + } + old2new[n] = sig1; + continue; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + recursive_ref( res, res.get_node( sig0 ) ); + } + old2new[n] = sig0; + continue; + } + use_zero = worst_arrival_nneg < node_data.required[1] + epsilon; + use_one = worst_arrival_npos < node_data.required[0] + epsilon; + + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + recursive_ref( res, res.get_node( sig0 ) ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + old2new[n] = sig0; + } + else + { + if ( node_data.map_refs[2] ) + { + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + recursive_ref( res, res.get_node( sig1 ) ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + old2new[n] = sig1; + } + } + + double area_old = area; + bool success = set_mapping_refs(); + + /* round stats */ + if ( ps.verbose ) + { + float area_gain = float( ( area_old - area ) / area_old * 100 ); + std::stringstream stats{}; + stats << fmt::format( "[i] Area RW : Delay = {:>12.2f} Area = {:>12.2f} {:>5.2f} %\n", delay, area, area_gain ); + st.round_stats.push_back( stats.str() ); + } + + return success; + } + + signal match_phase_exact_aggressive( depth_view& res, node_map, Ntk>& old2new, node const& n, uint8_t phase ) + { + signal best_signal = res.get_constant( false ); + + float best_arrival = std::numeric_limits::max(); + float best_exact_area = std::numeric_limits::max(); + float best_area = std::numeric_limits::max(); + uint32_t best_size = UINT32_MAX; + uint8_t best_cut = 0u; + uint8_t best_phase = 0u; + uint8_t cut_index = 0u; + auto index = ntk.node_to_index( n ); + + auto& node_data = node_match[index]; + auto& cut_matches = matches[index]; + exact_supergate const* best_supergate = node_data.best_supergate[phase]; + + /* create best match info */ + if ( best_supergate != nullptr ) + { + auto const& cut = cuts.cuts( index )[node_data.best_cut[phase]]; + auto const& supergates = cut_matches[( cut )->data.match_index]; + + /* permutate the children to the NPN-represenentative configuration */ + std::vector> children( NInputs, res.get_constant( false ) ); + auto ctr = 0u; + + for ( auto l : cut ) + { + children[supergates.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + + best_phase = supergates.negation; + best_cut = node_data.best_cut[phase]; + best_size = cut.size(); + for ( auto i = 0u; i < NInputs; ++i ) + { + if ( ( best_phase >> i ) & 1 ) + { + children[i] = !children[i]; + } + } + topo_view topo{ lib_database, best_supergate->root }; + auto f = cleanup_dangling( topo, res, children.begin(), children.end() ).front(); + + if ( phase == 1 ) + f = !f; + + best_signal = f; + + best_arrival = res.level( res.get_node( f ) ); + + /* if cut is implemented, remove it from the cover */ + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + best_area = recursive_ref( res, res.get_node( f ) ); + recursive_deref( res, res.get_node( f ) ); + best_exact_area = cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + else + { + best_area = recursive_ref( res, res.get_node( f ) ); + recursive_deref( res, res.get_node( f ) ); + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + cut_deref( cuts.cuts( index )[best_cut], n, phase ); + } + } + + /* foreach cut */ + unsigned int rewrite_count = 1u; + for ( auto& cut : cuts.cuts( index ) ) + { + /* trivial cuts, not matched cuts, or rewriting limit reached */ + if ( ( *cut )->data.ignore || ( rewrite_count > ps.logic_sharing_cut_limit && cut_index != best_cut ) ) + { + ++cut_index; + continue; + } + + auto const& supergates = cut_matches[( *cut )->data.match_index]; + + if ( supergates.supergates[phase] == nullptr ) + { + ++cut_index; + continue; + } + + ++rewrite_count; + + std::vector> children( NInputs, res.get_constant( false ) ); + + auto ctr = 0u; + for ( auto l : *cut ) + { + children[supergates.permutation[ctr++]] = old2new[ntk.index_to_node( l )]; + } + + /* match each gate and take the best one */ + for ( auto const& gate : *supergates.supergates[phase] ) + { + uint8_t complement = supergates.negation; + node_data.phase[phase] = complement; + + /* rewrite each structure and measure the logic sharing */ + std::vector> children_loc( NInputs ); + + for ( auto ctr = 0u; ctr < NInputs; ++ctr ) + { + children_loc[ctr] = children[ctr] ^ ( ( ( complement >> ctr ) & 1 ) == 1 ); + } + topo_view topo{ lib_database, gate.root }; + auto f = cleanup_dangling( topo, res, children_loc.begin(), children_loc.end() ).front(); + + if ( phase == 1 ) + f = !f; + + float worst_arrival = res.level( res.get_node( f ) ); + + float area_hashed = recursive_ref( res, res.get_node( f ) ); + node_data.area[phase] = area_hashed; + recursive_deref( res, res.get_node( f ) ); + auto area_exact = cut_ref( *cut, n, phase ); + cut_deref( *cut, n, phase ); + + if ( worst_arrival > node_data.required[phase] + epsilon ) + continue; + + if ( compare_map( worst_arrival, best_arrival, area_exact, best_exact_area, cut->size(), best_size ) ) + { + best_arrival = worst_arrival; + best_exact_area = area_exact; + best_area = area_hashed; + best_size = cut->size(); + best_cut = cut_index; + best_phase = complement; + best_supergate = &gate; + best_signal = f; + } + } + + ++cut_index; + } + old2new[n] = best_signal; + node_data.flows[phase] = best_exact_area; + node_data.arrival[phase] = best_arrival; + node_data.area[phase] = best_area; + node_data.best_cut[phase] = best_cut; + node_data.phase[phase] = best_phase; + node_data.best_supergate[phase] = best_supergate; + + if ( !node_data.same_match && node_data.map_refs[phase] ) + { + recursive_ref( res, res.get_node( best_signal ) ); + best_exact_area = cut_ref( cuts.cuts( index )[best_cut], n, phase ); + } + return best_signal; + } + + template + void match_drop_phase( node const& n, unsigned area_margin_factor ) + { + auto index = ntk.node_to_index( n ); + auto& node_data = node_match[index]; + + /* compute arrival adding an inverter to the other match phase */ + float worst_arrival_npos = node_data.arrival[1] + lib_inv_delay; + float worst_arrival_nneg = node_data.arrival[0] + lib_inv_delay; + bool use_zero = false; + bool use_one = false; + + /* only one phase is matched */ + if ( node_data.best_supergate[0] == nullptr ) + { + set_match_complemented_phase( index, 1, worst_arrival_npos ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + return; + } + else if ( node_data.best_supergate[1] == nullptr ) + { + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + if constexpr ( ELA ) + { + if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + return; + } + + /* try to use only one match to cover both phases */ + if constexpr ( !DO_AREA ) + { + /* if arrival is less matching the other phase and inserting an inverter */ + if ( worst_arrival_npos < node_data.arrival[0] + epsilon ) + { + use_one = true; + } + if ( worst_arrival_nneg < node_data.arrival[1] + epsilon ) + { + use_zero = true; + } + if ( !use_zero && !use_one ) + { + /* use both phases to improve delay */ + node_data.flows[2] = ( node_data.flows[0] + node_data.flows[1] ) / node_data.est_refs[2]; + node_data.flows[0] = node_data.flows[0] / node_data.est_refs[0]; + node_data.flows[1] = node_data.flows[1] / node_data.est_refs[1]; + return; + } + } + else + { + /* check if both phases + inverter meet the required time */ + use_zero = worst_arrival_nneg < node_data.required[1] + epsilon - area_margin_factor * lib_inv_delay; + use_one = worst_arrival_npos < node_data.required[0] + epsilon - area_margin_factor * lib_inv_delay; + } + + /* use area flow as a tiebreaker. Unfortunately cannot keep + * the both phases since `node_map` does not support that */ + if ( use_zero && use_one ) + { + auto size_zero = cuts.cuts( index )[node_data.best_cut[0]].size(); + auto size_one = cuts.cuts( index )[node_data.best_cut[1]].size(); + if ( compare_map( worst_arrival_nneg, worst_arrival_npos, node_data.flows[0], node_data.flows[1], size_zero, size_one ) ) + use_one = false; + else + use_zero = false; + } + + if ( use_zero ) + { + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + if ( node_data.map_refs[1] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + if ( node_data.map_refs[0] == 0 ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + } + set_match_complemented_phase( index, 0, worst_arrival_nneg ); + } + else + { + if constexpr ( ELA ) + { + if ( !node_data.same_match ) + { + if ( node_data.map_refs[0] > 0 ) + cut_deref( cuts.cuts( index )[node_data.best_cut[0]], n, 0 ); + if ( node_data.map_refs[1] == 0 && node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + else if ( node_data.map_refs[2] ) + cut_ref( cuts.cuts( index )[node_data.best_cut[1]], n, 1 ); + } + set_match_complemented_phase( index, 1, worst_arrival_npos ); + } + } + + inline void set_match_complemented_phase( uint32_t index, uint8_t phase, float worst_arrival_n ) + { + auto& node_data = node_match[index]; + auto phase_n = phase ^ 1; + node_data.same_match = true; + node_data.best_supergate[phase_n] = nullptr; + node_data.best_cut[phase_n] = node_data.best_cut[phase]; + node_data.phase[phase_n] = node_data.phase[phase] ^ ( 1 << NInputs ); + node_data.arrival[phase_n] = worst_arrival_n; + node_data.area[phase_n] = node_data.area[phase]; + node_data.flows[phase] = node_data.flows[phase] / node_data.est_refs[2]; + node_data.flows[phase_n] = node_data.flows[phase]; + node_data.flows[2] = node_data.flows[phase]; + } + + inline float cut_leaves_flow( cut_t const& cut, node const& n, uint8_t phase ) + { + float flow{ 0.0f }; + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + + uint8_t ctr = 0u; + for ( auto leaf : cut ) + { + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr++] ) & 1; + flow += node_match[leaf].flows[leaf_phase]; + } + + return flow; + } + + float cut_ref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + float count = node_data.area[phase]; + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr] ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + ++ctr; + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* reference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( node_match[leaf].map_refs[1]++ == 0u ) + count += lib_inv_area; + } + else + { + ++node_match[leaf].map_refs[0]; + } + ++ctr; + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if not present yet and leaf node is implemented in the opposite phase */ + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + count += lib_inv_area; + /* Recursive referencing if leaf was not referenced */ + if ( node_match[leaf].map_refs[2]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + ++node_match[leaf].map_refs[2]; + if ( node_match[leaf].map_refs[leaf_phase]++ == 0u ) + { + count += cut_ref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + ++ctr; + } + return count; + } + + float cut_deref( cut_t const& cut, node const& n, uint8_t phase ) + { + auto const& node_data = node_match[ntk.node_to_index( n )]; + auto const& match = matches[ntk.node_to_index( n )][cut->data.match_index]; + float count = node_data.area[phase]; + uint8_t ctr = 0; + for ( auto leaf : cut ) + { + /* compute leaf phase using the current gate */ + uint8_t leaf_phase = ( node_data.phase[phase] >> match.permutation[ctr] ) & 1; + + if ( ntk.is_constant( ntk.index_to_node( leaf ) ) ) + { + ++ctr; + continue; + } + else if ( ntk.is_ci( ntk.index_to_node( leaf ) ) ) + { + /* dereference PIs, add inverter cost for negative phase */ + if ( leaf_phase == 1u ) + { + if ( --node_match[leaf].map_refs[1] == 0u ) + count += lib_inv_area; + } + else + { + --node_match[leaf].map_refs[0]; + } + ++ctr; + continue; + } + + if ( node_match[leaf].same_match ) + { + /* Add inverter area if it is used only by the current gate and leaf node is implemented in the opposite phase */ + if ( --node_match[leaf].map_refs[leaf_phase] == 0u && node_match[leaf].best_supergate[leaf_phase] == nullptr ) + count += lib_inv_area; + /* Recursive dereferencing */ + if ( --node_match[leaf].map_refs[2] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + else + { + --node_match[leaf].map_refs[2]; + if ( --node_match[leaf].map_refs[leaf_phase] == 0u ) + { + count += cut_deref( cuts.cuts( leaf )[node_match[leaf].best_cut[leaf_phase]], ntk.index_to_node( leaf ), leaf_phase ); + } + } + ++ctr; + } + return count; + } + + template + inline bool compare_map( float arrival, float best_arrival, float area_flow, float best_area_flow, uint32_t size, uint32_t best_size ) + { + if constexpr ( DO_AREA ) + { + if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + else if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + } + else + { + if ( arrival < best_arrival - epsilon ) + { + return true; + } + else if ( arrival > best_arrival + epsilon ) + { + return false; + } + else if ( area_flow < best_area_flow - epsilon ) + { + return true; + } + else if ( area_flow > best_area_flow + epsilon ) + { + return false; + } + } + if ( size < best_size ) + { + return true; + } + return false; + } + +private: + Ntk& ntk; + exact_library const& library; + map_params const& ps; + map_stats& st; + + uint32_t iteration{ 0 }; /* current mapping iteration */ + double delay{ 0.0f }; /* current delay of the mapping */ + double area{ 0.0f }; /* current area of the mapping */ + const float epsilon{ 0.005f }; /* epsilon */ + + /* lib inverter info */ + float lib_inv_area; + float lib_inv_delay; + + NtkDest const& lib_database; + + std::vector> top_order; + std::vector> node_match; + std::unordered_map>> matches; + network_cuts_t cuts; +}; + +} /* namespace detail */ + +/*! \brief Exact mapping. + * + * This function implements a mapping algorithm using a database of structures. + * It is controlled by a template argument `CutData` (defaulted to + * `cut_enumeration_exact_map_cut`). The argument is similar to the `CutData` argument + * in `cut_enumeration`, which can specialize the cost function to select priority + * cuts and store additional data. The default argument gives priority firstly to + * area flow, then delay, and lastly to the cut size. + * The type passed as `CutData` must implement the following four fields: + * + * - `uint32_t delay` + * - `float flow` + * - `uint8_t match_index` + * - `bool ignore` + * + * See `include/mockturtle/algorithms/cut_enumeration/cut_enumeration_exact_map_cut.hpp` + * for one example of a CutData type that implements the cost function that is used in + * the technology mapper. + * + * The function takes the size of the cuts in the template parameter `CutSize`. + * + * The function returns a mapped network representation generated using the exact + * synthesis entries in the `exact_library`. This function supports also sequential networks. + * + * **Required network functions:** + * - `size` + * - `is_ci` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * + * \param ntk Network + * \param library Exact library + * \param ps Mapping params + * \param pst Mapping statistics + */ +template +NtkDest map( Ntk& ntk, exact_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_foreach_ri_v == has_create_ri_v, "Ntk and NtkDest networks are not both sequential" ); + static_assert( has_foreach_ro_v == has_create_ro_v, "Ntk and NtkDest networks are not both sequential" ); + + map_stats st; + detail::exact_map_impl p( ntk, library, ps, st ); + auto res = p.run(); + + st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + return res; +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp b/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp new file mode 100644 index 0000000..9aa2fce --- /dev/null +++ b/include/mockturtle/algorithms/mig_algebraic_rewriting.hpp @@ -0,0 +1,376 @@ +/* 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_algebraic_rewriting.hpp + \brief MIG algebraric rewriting + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for mig_algebraic_depth_rewriting. + * + * The data structure `mig_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `mig_algebraic_depth_rewriting`. + */ +struct mig_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; +}; + +/*! \brief Statistics for mig_algebraic_depth_rewriting. */ +struct mig_algebraic_depth_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; +}; + +namespace detail +{ + +template +class mig_algebraic_depth_rewriting_impl +{ +public: + mig_algebraic_depth_rewriting_impl( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps, mig_algebraic_depth_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + switch ( ps.strategy ) + { + case mig_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case mig_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case mig_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + reduce_depth( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( z, assoc ? u : x, ntk.create_maj( x, y, u ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto opt = ntk.create_maj( ocs2[2], + ntk.create_maj( ocs[0], ocs[1], ocs2[0] ), + ntk.create_maj( ocs[0], ocs[1], ocs2[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + mig_algebraic_depth_rewriting_params const& ps; + mig_algebraic_depth_rewriting_stats& st; +}; + +} // namespace detail + +/*! \brief Majority algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than MIGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void mig_algebraic_depth_rewriting( Ntk& ntk, mig_algebraic_depth_rewriting_params const& ps = {}, mig_algebraic_depth_rewriting_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_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + mig_algebraic_depth_rewriting_stats st; + detail::mig_algebraic_depth_rewriting_impl p( ntk, ps, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/mig_inv_optimization.hpp b/include/mockturtle/algorithms/mig_inv_optimization.hpp new file mode 100644 index 0000000..7e0473c --- /dev/null +++ b/include/mockturtle/algorithms/mig_inv_optimization.hpp @@ -0,0 +1,394 @@ +/* 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 + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include +#include +#include +#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. */ + int32_t num_inverted{ 0 }; + + /*! \brief Number of two level inverted nodes. */ + int32_t num_two_level_inverted{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + int32_t 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( [this, &changed]( auto const& f ) { + int32_t gain = calculate_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, [this, &f, &nodes_to_invert]( auto const& parent ) { + // convert each fanout if inverting it makes sense + int32_t sub_gain = 0; + sub_gain += calculate_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 + sub_gain -= 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 + sub_gain += 2; + } + if ( sub_gain > 0 ) + { + st.total_gain += sub_gain; + 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 */ + int32_t two_level_gain( node n ) + { + int32_t gain = 0; + gain += calculate_gain( n ); + + ntk.foreach_fanout( n, [this, &gain, &n]( auto const& f ) { + int32_t sub_gain = 0; + sub_gain += calculate_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 + sub_gain -= 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 + sub_gain += 2; + } + + // convert each fanout if inverting it makes sense + if ( sub_gain > 0 ) + { + gain += sub_gain; + } + } ); + + return gain; + } + + /*! \brief calculates the decrease in the number of inverters if this node is inverted */ + int32_t calculate_gain( node n ) + { + if ( ntk.is_dead( n ) ) + { + std::cerr << "node" << n << " is dead\n"; + return 0; + } + int32_t gain = 0; + + // count the inverted and non-inverted fanins + ntk.foreach_fanin( n, [this, &gain]( 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, [this, &n, &gain]( auto const& parent ) { + if ( is_complemented_parent( parent, n ) ) + { + gain++; + } + else + { + gain--; + } + } ); + + // count the inverted and non-inverted POs + ntk.foreach_po( [this, &n, &gain]( 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, int32_t& 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, [this, &child, &ret, &changed]( 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; + } + 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 */ + if ( auto const it = ntk._storage->hash.find( nd ); it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + auto const 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: + fanout_view 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( std::is_same_v, "Ntk is not an MIG network" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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 0000000..ce9163b --- /dev/null +++ b/include/mockturtle/algorithms/mig_inv_propagation.hpp @@ -0,0 +1,374 @@ +/* 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 + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "./cleanup.hpp" + +#include +#include +#include +#include +#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. */ + uint32_t node_increase{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + uint32_t 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 ); + + auto const initial_size = number_of_nodes( ntk ); + auto const initial_inverters = number_of_inverters( ntk ); + + propagate(); + + st.node_increase = number_of_nodes( ntk ) - initial_size; + st.total_gain = initial_inverters - number_of_inverters( ntk ); + } + +private: + /*! \brief implements the inverter propagation algorithm */ + void propagate() + { + // starting from primary outputs, propagate the inversions + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + // if it is complemented, invert the node + auto const old_node = ntk.get_node( f ); + auto const 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( const node n ) + { + std::vector> complement_list{}; + complement_list.reserve( Ntk::max_fanin_size ); + + // for each fanin, check if it is complemented + ntk.foreach_fanin( n, [this, &complement_list]( auto const& f ) { + // 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 const new_node = invert_node( f ); + // replace the fanin with the inverted node + if ( auto const 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( const node n ) + { + signal a, b, c; + + ntk.foreach_fanin( n, [&a, &b, &c]( auto const& f, auto idx ) { + if ( idx == 0 ) + { + a = f; + } + else if ( idx == 1 ) + { + b = f; + } + else if ( idx == 2 ) + { + c = f; + } + } ); + + return !create_maj_directly( !a, !b, !c ); + } + + /** + * \brief The 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; + } + 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 */ + auto const it = ntk._storage->hash.find( nd ); + if ( it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + auto const 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 }; + } + + uint32_t number_of_inverters( Ntk const& ntk ) const + { + uint32_t num_inverters{ 0 }; + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_dead( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_constant( ntk.get_node( f ) ) || ntk.is_pi( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_complemented( f ) ) + { + ++num_inverters; + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + ++num_inverters; + } + } ); + + return num_inverters; + } + /** + * \brief Determines the number of nodes in the given network that are actually alive. + */ + uint64_t number_of_nodes( Ntk const& ntk ) const + { + uint64_t nodes{}; + + ntk.foreach_node( [&nodes]( auto const ) { + ++nodes; + } ); + + return nodes; + } + +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( std::is_same_v, "Ntk is not an MIG network" ); + 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/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp new file mode 100644 index 0000000..1998a83 --- /dev/null +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -0,0 +1,875 @@ +/* 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_resub.hpp + \brief Majority-specific resustitution rules + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../utils/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/mig_resyn.hpp" + +#include + +namespace mockturtle +{ + +struct mig_enumerative_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for relevance resub */ + stopwatch<>::duration time_resubR{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted relevance resubsitutions */ + uint32_t num_divR_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: mig_enumerative_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] R-resub {:6d} ({:>5.2f} secs)\n", + num_divR_accepts, to_seconds( time_resubR ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} = {:6d} MAJ ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} = {:6d} 2MAJ ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_divR_accepts + num_div1_accepts + num_div2_accepts ) ); + } +}; /* mig_enumerative_resub_stats */ + +template +struct mig_enumerative_resub_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_enumerative_resub_stats; + + struct unate_divisors + { + std::vector u0; + std::vector u1; + std::vector next_candidates; + + void clear() + { + u0.clear(); + u1.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + std::vector b0; + std::vector b1; + std::vector b2; + + void clear() + { + b0.clear(); + b1.clear(); + b2.clear(); + } + }; + +public: + explicit mig_enumerative_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider relevance optimization */ + g = call_with_stopwatch( st.time_resubR, [&]() { + return resub_divR( root, required ); + } ); + if ( g ) + { + ++st.num_divR_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_mffc == 2 ) + return std::nullopt; + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, required ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_mffc - 2; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( tt == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( tt != sim.get_tt( ntk.make_signal( d ) ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + std::optional resub_divR( node const& root, uint32_t required ) + { + (void)required; + + std::vector fs; + ntk.foreach_fanin( root, [&]( const auto& f ) { + fs.emplace_back( f ); + } ); + + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const& d0 = divs.at( i ); + auto const& s = ntk.make_signal( d0 ); + auto const& tt = sim.get_tt( s ); + + if ( d0 == root ) + break; + + auto const tt0 = sim.get_tt( fs[0] ); + auto const tt1 = sim.get_tt( fs[1] ); + auto const tt2 = sim.get_tt( fs[2] ); + + if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? !s : s, a, b ); + } + else if ( ntk.get_node( fs[0] ) != d0 && ntk.fanout_size( ntk.get_node( fs[0] ) ) == 1 && can_replace_majority_fanin( ~tt0, tt1, tt2, tt ) ) + { + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, b, c ); + } + else if ( ntk.get_node( fs[1] ) != d0 && ntk.fanout_size( ntk.get_node( fs[1] ) ) == 1 && can_replace_majority_fanin( ~tt1, tt0, tt2, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const c = sim.get_phase( ntk.get_node( fs[2] ) ) ? !fs[2] : fs[2]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, c ); + } + else if ( ntk.get_node( fs[2] ) != d0 && ntk.fanout_size( ntk.get_node( fs[2] ) ) == 1 && can_replace_majority_fanin( ~tt2, tt0, tt1, tt ) ) + { + auto const a = sim.get_phase( ntk.get_node( fs[0] ) ) ? !fs[0] : fs[0]; + auto const b = sim.get_phase( ntk.get_node( fs[1] ) ) ? !fs[1] : fs[1]; + + return sim.get_phase( root ) ? !ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ) : ntk.create_maj( sim.get_phase( d0 ) ? s : !s, a, b ); + } + } + + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + auto const& one = sim.get_tt( ntk.get_constant( true ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d0 = divs.at( i ); + if ( ntk.level( d0 ) > required - 1 ) + continue; + auto const& tt_s0 = sim.get_tt( ntk.make_signal( d0 ) ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const d1 = divs.at( j ); + if ( ntk.level( d1 ) > required - 1 ) + continue; + auto const& tt_s1 = sim.get_tt( ntk.make_signal( d1 ) ); + + /* Boolean filtering rule for MAJ-3 */ + if ( kitty::ternary_majority( tt_s0, tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( !ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.make_signal( d1 ) ); + continue; + } + + if ( kitty::ternary_majority( tt_s0, ~tt_s1, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( !ntk.make_signal( d1 ) ); + continue; + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d1 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d1 ) ); + } + + if constexpr ( use_constant ) /* allowing "not real" MAJ gates (one fanin is constant) */ + { + if ( kitty::ternary_majority( tt_s0, one, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( true ) ); + continue; + } + + if ( kitty::ternary_majority( ~tt_s0, one, tt ) == tt ) + { + udivs.u0.emplace_back( !ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( true ) ); + continue; + } + + if ( kitty::ternary_majority( tt_s0, ~one, tt ) == tt ) + { + udivs.u0.emplace_back( ntk.make_signal( d0 ) ); + udivs.u1.emplace_back( ntk.get_constant( false ) ); + continue; + } + } + + if ( std::find( udivs.next_candidates.begin(), udivs.next_candidates.end(), ntk.make_signal( d0 ) ) == udivs.next_candidates.end() ) + udivs.next_candidates.emplace_back( ntk.make_signal( d0 ) ); + } + + if constexpr ( use_constant ) + { + udivs.next_candidates.emplace_back( ntk.get_constant( true ) ); + } + } + + std::optional resub_div1( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + for ( auto i = 0u; i < udivs.u0.size(); ++i ) + { + auto const s0 = udivs.u0.at( i ); + auto const s1 = udivs.u1.at( i ); + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + for ( auto j = i + 1; j < udivs.u0.size(); ++j ) + { + auto s2 = udivs.u0.at( j ); + auto tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + + s2 = udivs.u1.at( j ); + tt_s2 = sim.get_tt( s2 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ) == tt ) + { + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t required ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > required - 2 ) + continue; + + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > required - 2 ) + continue; + + auto const& tt_s1 = sim.get_tt( s1 ); + + for ( auto k = j + 1; k < udivs.next_candidates.size(); ++k ) + { + auto const& s2 = udivs.next_candidates.at( k ); + if ( ntk.level( ntk.get_node( s2 ) ) > required - 2 ) + continue; + + auto const& tt_s2 = sim.get_tt( s2 ); + + /* Note: the implication relation is actually not necessary for majority; this is an over-filtering */ + if ( kitty::implies( kitty::ternary_majority( tt_s0, tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, ~tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, ~tt_s1, tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( tt_s0, ~tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + + if ( kitty::implies( kitty::ternary_majority( ~tt_s0, ~tt_s1, ~tt_s2 ), tt ) ) + { + bdivs.b0.emplace_back( !s0 ); + bdivs.b1.emplace_back( !s1 ); + bdivs.b2.emplace_back( !s2 ); + continue; + } + } + } + } + } + + std::optional resub_div2( node const& root, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + for ( auto i = 0u; i < udivs.u0.size(); ++i ) + { + auto const& s0 = udivs.u0.at( i ); + auto const& s1 = udivs.u1.at( i ); + + for ( auto j = 0u; j < bdivs.b0.size(); ++j ) + { + auto const& s2 = bdivs.b0.at( j ); + auto const& s3 = bdivs.b1.at( j ); + auto const& s4 = bdivs.b2.at( j ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + auto const d = sim.get_phase( ntk.get_node( s3 ) ) ? !s3 : s3; + auto const e = sim.get_phase( ntk.get_node( s4 ) ) ? !s4 : s4; + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + auto const& tt_s3 = sim.get_tt( s3 ); + auto const& tt_s4 = sim.get_tt( s4 ); + + if ( kitty::ternary_majority( tt_s0, tt_s1, kitty::ternary_majority( tt_s2, tt_s3, tt_s4 ) ) == tt ) + { + return sim.get_phase( root ) ? !ntk.create_maj( a, b, ntk.create_maj( c, d, e ) ) : ntk.create_maj( a, b, ntk.create_maj( c, d, e ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* mig_enumerative_resub_functor */ + +struct mig_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* mig_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with MIG resynthesis engines for `window_based_resub_engine`. + */ +template> +struct mig_resyn_functor +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit mig_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + 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() ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, divs.begin(), divs.end(), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + 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 + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* mig_resyn_functor */ + +/*! \brief MIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from mig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void mig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8u>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_enumerative_resub_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_enumerative_resub_functor, truthtable_dc_t>; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +/*! \brief MIG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from mig_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void mig_resubstitution2( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not mig_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = mig_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/miter.hpp b/include/mockturtle/algorithms/miter.hpp new file mode 100644 index 0000000..1eb8263 --- /dev/null +++ b/include/mockturtle/algorithms/miter.hpp @@ -0,0 +1,113 @@ +/* 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 miter.hpp + \brief Generate miter from two networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" +#include "cleanup.hpp" + +namespace mockturtle +{ + +/*! \brief Creates a combinational miter from two networks. + * + * This method combines two networks that have the same number of primary + * inputs and the same number of primary outputs into a miter. The miter + * has the same number of inputs and one primary output. This output is the + * OR of XORs of all primary output pairs. In other words, the miter outputs + * 1 for all input assignments in which the two input networks differ. + * + * All networks may have different types. The method returns an optional, which + * is `nullopt`, whenever the two input networks don't match in their number of + * primary inputs and primary outputs. + */ +template +std::optional miter( NtkSource1 const& ntk1, NtkSource2 const& ntk2 ) +{ + static_assert( is_network_type_v, "NtkSource1 is not a network type" ); + static_assert( is_network_type_v, "NtkSource2 is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_num_pis_v, "NtkSource1 does not implement the num_pis method" ); + static_assert( has_num_pos_v, "NtkSource1 does not implement the num_pos method" ); + static_assert( has_num_pis_v, "NtkSource2 does not implement the num_pis method" ); + static_assert( has_num_pos_v, "NtkSource2 does not implement the num_pos method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + static_assert( has_create_xor_v, "NtkDest does not implement the create_xor method" ); + static_assert( has_create_nary_or_v, "NtkDest does not implement the create_nary_or method" ); + + /* both networks must have same number of inputs and outputs */ + if ( ( ntk1.num_pis() != ntk2.num_pis() ) || ( ntk1.num_pos() != ntk2.num_pos() ) ) + { + return std::nullopt; + } + + /* create primary inputs */ + NtkDest dest; + std::vector> pis; + for ( auto i = 0u; i < ntk1.num_pis(); ++i ) + { + pis.push_back( dest.create_pi() ); + } + + /* copy networks */ + const auto pos1 = cleanup_dangling( ntk1, dest, pis.begin(), pis.end() ); + const auto pos2 = cleanup_dangling( ntk2, dest, pis.begin(), pis.end() ); + + if constexpr ( has_EXODC_interface_v ) + { + ntk1.build_oe_miter( dest, pos1, pos2 ); + return dest; + } + if constexpr ( has_EXODC_interface_v ) + { + ntk2.build_oe_miter( dest, pos1, pos2 ); + return dest; + } + + /* create XOR of output pairs */ + std::vector> xor_outputs; + std::transform( pos1.begin(), pos1.end(), pos2.begin(), std::back_inserter( xor_outputs ), + [&]( auto const& o1, auto const& o2 ) { return dest.create_xor( o1, o2 ); } ); + + /* create big OR of XOR gates */ + dest.create_po( dest.create_nary_or( xor_outputs ) ); + + return dest; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/network_fuzz_tester.hpp b/include/mockturtle/algorithms/network_fuzz_tester.hpp new file mode 100644 index 0000000..48ae5c9 --- /dev/null +++ b/include/mockturtle/algorithms/network_fuzz_tester.hpp @@ -0,0 +1,260 @@ +/* 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 network_fuzz_tester.hpp + \brief Network fuzz tester + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#include "../io/aiger_reader.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_aiger.hpp" +#include "../io/write_verilog.hpp" +#include "../utils/stopwatch.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for testcase_minimizer. */ +struct fuzz_tester_params +{ + /*! \brief File format to be generated. */ + enum + { + verilog, + aiger + } file_format = verilog; + + /*! \brief Name of the generated testcase file. */ + std::string filename{ "fuzz_test.v" }; + + /*! \brief Filename written out by the command (to do CEC with the input testcase). */ + std::optional outputfile{ std::nullopt }; + + /*! \brief Max number of networks to test: nullopt means infinity. */ + std::optional num_iterations{ std::nullopt }; + + /*! \brief Timeout in seconds: nullopt means infinity. */ + std::optional timeout{ std::nullopt }; +}; /* fuzz_tester_params */ + +/*! \brief Network fuzz tester + * + * Runs an algorithm on many small random logic networks. Fuzz + * testing is often useful to detect potential segmentation faults in + * new implementations. The generated benchmarks are saved first in a + * file. If a segmentation fault or unexpected behavior occurs, the + * file can be used to reproduce and debug the problem. + * + * The entry function `run` generates different networks with the same + * number of PIs and gates. The function `run_incremental`, on the other + * hand, generates networks of increasing sizes. These functions return + * true if it was terminated by an unexpected behavior, or return false + * if it terminates normally after the specified number of iterations + * without observing any defect. + * + * The script of algorithm(s) to be tested can be provided as (1) a + * lambda function taking a network as input and returning a Boolean, + * which is true if the algorithm behaves as expected; or (2) a lambda + * function making a command string to be called, taking a filename string + * as input (not supported on Windows platform). If the command exits + * normally (with return value 0), CEC will be performed on the output + * file; otherwise (segfault, assertion fail, or return value is not 0), + * the fuzzer is terminated. + * + \verbatim embed:rst + + Usage + + .. code-block:: c++ + + #include + #include + #include + #include + #include + + auto opt = [&]( aig_network aig ) -> bool { + resubstitution_params ps; + resubstitution_stats st; + aig_resubstitution( aig, ps, &st ); + aig = cleanup_dangling( aig ); + return true; + }; + + fuzz_tester_params ps; + ps.num_iterations = 100; + auto gen = default_random_aig_generator(); + network_fuzz_tester fuzzer( gen, ps ); + fuzzer.run( opt ); + \endverbatim +*/ +template +class network_fuzz_tester +{ +public: + explicit network_fuzz_tester( NetworkGenerator& gen, fuzz_tester_params const ps = {} ) + : gen( gen ), ps( ps ) + {} + +#ifndef _MSC_VER + uint64_t run( std::function&& make_command ) + { + return run( make_callback( make_command ) ); + } +#endif + + uint64_t run( std::function&& fn ) + { + uint64_t counter{ 0 }; + stopwatch<>::duration time{ 0 }; + while ( ( !ps.num_iterations || counter < ps.num_iterations ) && + ( !ps.timeout || to_seconds( time ) < ps.timeout ) ) + { + stopwatch t( time ); + auto ntk = gen.generate(); + fmt::print( "[i] create network #{}: I/O = {}/{} gates = {} nodes = {}, write into `{}`\n", + ++counter, ntk.num_pis(), ntk.num_pos(), ntk.num_gates(), ntk.size(), ps.filename ); + + switch ( ps.file_format ) + { + case fuzz_tester_params::verilog: + write_verilog( ntk, ps.filename ); + break; + case fuzz_tester_params::aiger: + write_aiger( ntk, ps.filename ); + break; + default: + fmt::print( "[w] unsupported format\n" ); + return 0; + } + + /* run optimization algorithm */ + if ( !fn( ntk ) ) + { + return counter; + } + + if ( ps.outputfile ) + { + if ( !abc_cec() ) + return counter; + } + } + return 0; + } + +private: +#ifndef _MSC_VER + inline std::function make_callback( std::function& make_command ) + { + std::function fn = [&]( Ntk ntk ) -> bool { + (void)ntk; + int status = std::system( make_command( ps.filename ).c_str() ); + if ( status < 0 ) + { + std::cout << "[e] Unexpected error when calling command: " << strerror( errno ) << '\n'; + return false; + } + else + { + if ( WIFEXITED( status ) ) + { + if ( WEXITSTATUS( status ) == 0 ) // normal + { + if ( ps.outputfile ) + return abc_cec(); + return true; + } + else if ( WEXITSTATUS( status ) == 1 || WEXITSTATUS( status ) == 134 ) // buggy or assertion fail + { + return false; + } + else + { + std::cout << "[e] Unexpected return value: " << WEXITSTATUS( status ) << '\n'; + return false; + } + } + else // segfault + { + return false; + } + } + }; + return fn; + } +#endif + + inline bool abc_cec() + { + std::string command = fmt::format( "abc -q \"cec -n {} {}\"", ps.filename, *ps.outputfile ); + + std::array buffer; + std::string result; +#ifdef _MSC_VER + std::unique_ptr pipe( _popen( command.c_str(), "r" ), _pclose ); +#else + std::unique_ptr pipe( popen( command.c_str(), "r" ), pclose ); +#endif + if ( !pipe ) + { + throw std::runtime_error( "popen() failed" ); + } + while ( fgets( buffer.data(), buffer.size(), pipe.get() ) != nullptr ) + { + result += buffer.data(); + } + + /* search for one line which says "Networks are equivalent" and ignore all other debug output from ABC */ + std::stringstream ss( result ); + std::string line; + while ( std::getline( ss, line, '\n' ) ) + { + if ( line.size() >= 23u && line.substr( 0u, 23u ) == "Networks are equivalent" ) + { + return true; + } + } + + fmt::print( "[e] Files are not equivalent\n" ); + return false; + } + +private: + NetworkGenerator& gen; + fuzz_tester_params const ps; +}; /* network_fuzz_tester */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis.hpp b/include/mockturtle/algorithms/node_resynthesis.hpp new file mode 100644 index 0000000..743d192 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis.hpp @@ -0,0 +1,357 @@ +/* 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 node_resynthesis.hpp + \brief Node resynthesis + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for node_resynthesis. + * + * The data structure `node_resynthesis_params` holds configurable parameters + * with default arguments for `node_resynthesis`. + */ +struct node_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for node_resynthesis. + * + * The data structure `node_resynthesis_stats` provides data collected by + * running `node_resynthesis`. + */ +struct node_resynthesis_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class node_resynthesis_impl +{ +public: + node_resynthesis_impl( NtkDest& ntk_dest, NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps, node_resynthesis_stats& st ) + : ntk_dest( ntk_dest ), + ntk( ntk ), + resynthesis_fn( resynthesis_fn ), + ps( ps ), + st( st ) + { + } + + NtkDest run() + { + stopwatch t( st.time_total ); + + node_map, NtkSource> node2new( ntk ); + + /* map constants */ + node2new[ntk.get_node( ntk.get_constant( false ) )] = ntk_dest.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( true ) ) != ntk.get_node( ntk.get_constant( false ) ) ) + { + node2new[ntk.get_node( ntk.get_constant( true ) )] = ntk_dest.get_constant( true ); + } + + /* map primary inputs */ + ntk.foreach_pi( [&]( auto n ) { + node2new[n] = ntk_dest.create_pi(); + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + + if constexpr ( has_foreach_ro_v && has_create_ro_v ) + { + ntk.foreach_ro( [&]( auto n, auto i ) { + node2new[n] = ntk_dest.create_ro(); + ntk_dest.set_register( i, ntk.register_at( i ) ); + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( node2new[n], ntk.get_name( ntk.make_signal( n ) ) ); + } + } ); + } + + /* map nodes */ + topo_view ntk_topo{ ntk }; + ntk_topo.foreach_node( [&]( auto n ) { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + return; + + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f] ); + } ); + + bool performed_resyn = false; + resynthesis_fn( ntk_dest, ntk.node_function( n ), children.begin(), children.end(), [&]( auto const& f ) { + node2new[n] = f; + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + if ( ntk.has_name( ntk.make_signal( n ) ) ) + ntk_dest.set_name( f, ntk.get_name( ntk.make_signal( n ) ) ); + } + + performed_resyn = true; + return false; + } ); + + if ( !performed_resyn ) + { + fmt::print( "[e] could not perform resynthesis for node {} in node_resynthesis\n", ntk.node_to_index( n ) ); + std::abort(); + } + } ); + + /* map primary outputs */ + ntk.foreach_po( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_po( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + ntk_dest.set_output_name( index, ntk.get_output_name( index ) ); + } + } + } ); + + if constexpr ( has_foreach_ri_v && has_create_ri_v ) + { + ntk.foreach_ri( [&]( auto const& f, auto index ) { + (void)index; + + auto const o = ntk.is_complemented( f ) ? ntk_dest.create_not( node2new[f] ) : node2new[f]; + ntk_dest.create_ri( o ); + + if constexpr ( has_has_output_name_v && has_get_output_name_v && has_set_output_name_v ) + { + if ( ntk.has_output_name( index ) ) + { + ntk_dest.set_output_name( index + ntk.num_pos(), ntk.get_output_name( index + ntk.num_pos() ) ); + } + } + } ); + } + + return ntk_dest; + } + +private: + NtkDest& ntk_dest; + NtkSource const& ntk; + ResynthesisFn&& resynthesis_fn; + node_resynthesis_params const& ps; + node_resynthesis_stats& st; +}; + +} /* namespace detail */ + +/*! \brief Node resynthesis algorithm. + * + * This algorithm takes as input a network (of type `NtkSource`) and creates a + * new network (of type `NtkDest`), by translating each node of the input + * network into a subnetwork for the output network. To find a new subnetwork, + * the algorithm uses a resynthesis function that takes as input the input + * node's truth table. This algorithm can for example be used to translate + * k-LUT networks into AIGs or MIGs. + * + * The resynthesis function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `foreach_fanin` + * - `node_function` + * - `foreach_po` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_not` + * - `create_po` + * + * \param ntk Input network of type `NtkSource` + * \param resynthesis_fn Resynthesis function + * \return An equivalent network of type `NtkDest` + */ +template +NtkDest node_resynthesis( NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps = {}, node_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + node_resynthesis_stats st; + NtkDest ntk_dest; + detail::node_resynthesis_impl p( ntk_dest, ntk, resynthesis_fn, ps, st ); + const auto ret = p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + return ret; +} + +/*! \brief Node resynthesis algorithm. + * + * This algorithm takes as input a network (of type `NtkSource`) and creates a + * new network (of type `NtkDest`), by translating each node of the input + * network into a subnetwork for the output network. To find a new subnetwork, + * the algorithm uses a resynthesis function that takes as input the input + * node's truth table. This algorithm can for example be used to translate + * k-LUT networks into AIGs or MIGs. + * + * The resynthesis function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. + * + * **Required network functions for parameter ntk (type NtkSource):** + * - `get_node` + * - `get_constant` + * - `foreach_pi` + * - `foreach_node` + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `foreach_fanin` + * - `node_function` + * - `foreach_po` + * + * **Required network functions for return value (type NtkDest):** + * - `get_constant` + * - `create_pi` + * - `create_not` + * - `create_po` + * + * \param ntk_dest Output network of type `NtkDest` + * \param ntk Input network of type `NtkSource` + * \param resynthesis_fn Resynthesis function + */ +template +void node_resynthesis( NtkDest& ntk_dest, NtkSource const& ntk, ResynthesisFn&& resynthesis_fn, node_resynthesis_params const& ps = {}, node_resynthesis_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "NtkSource is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + static_assert( has_get_node_v, "NtkSource does not implement the get_node method" ); + static_assert( has_get_constant_v, "NtkSource does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "NtkSource does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "NtkSource does not implement the foreach_node method" ); + static_assert( has_is_constant_v, "NtkSource does not implement the is_constant method" ); + static_assert( has_is_pi_v, "NtkSource does not implement the is_pi method" ); + static_assert( has_is_complemented_v, "NtkSource does not implement the is_complemented method" ); + static_assert( has_foreach_fanin_v, "NtkSource does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "NtkSource does not implement the node_function method" ); + static_assert( has_foreach_po_v, "NtkSource does not implement the foreach_po method" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_create_not_v, "NtkDest does not implement the create_not method" ); + static_assert( has_create_po_v, "NtkDest does not implement the create_po method" ); + + node_resynthesis_stats st; + detail::node_resynthesis_impl p( ntk_dest, ntk, resynthesis_fn, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/akers.hpp b/include/mockturtle/algorithms/node_resynthesis/akers.hpp new file mode 100644 index 0000000..8f20f1b --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/akers.hpp @@ -0,0 +1,76 @@ +/* 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 akers.hpp + \brief Resynthesis with Akers synthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/akers_synthesis.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Akers synthesis. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + akers_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +template +class akers_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + fn( akers_synthesis( ntk, function, ~function.construct(), begin, end ) ); + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp b/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp new file mode 100644 index 0000000..270c412 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/bidecomposition.hpp @@ -0,0 +1,82 @@ +/* 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 bidecomposition.hpp + \brief Resynthesis with bi_decomposition + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/bi_decomposition.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on bi-decomposition + * + * This resynthesis function can be passed to ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const xag_network xag = ...; + bidecomposition_resynthesis resyn; + const auto xag = refactoring( xag, resyn ); + \endverbatim + */ + +template +class bidecomposition_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + fn( bi_decomposition( ntk, function, ~dc, { begin, end } ) ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } +}; +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/cached.hpp b/include/mockturtle/algorithms/node_resynthesis/cached.hpp new file mode 100644 index 0000000..738dc53 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/cached.hpp @@ -0,0 +1,317 @@ +/* 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 cached.hpp + \brief Generic resynthesis with a cache + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#if !__clang__ || __clang_major__ > 10 + +#include +#if __GNUC__ == 7 +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../traits.hpp" +#include "../../utils/json_utils.hpp" +#include "../../utils/network_cache.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +struct no_blacklist_cache_info +{ + bool retry( no_blacklist_cache_info const& old_info ) const + { + (void)old_info; + return false; + } +}; + +inline void to_json( nlohmann::json& j, no_blacklist_cache_info const& info ) +{ + (void)info; + j = nullptr; +} + +inline void from_json( nlohmann::json const& j, no_blacklist_cache_info& info ) +{ + (void)j; + (void)info; +} + +template +class cached_resynthesis +{ +public: + explicit cached_resynthesis( ResynthesisFn const& resyn_fn, uint32_t max_pis, std::string const& cache_filename = {}, BlacklistCacheInfo const& blacklist_cache_info = {} ) + : _resyn_fn( resyn_fn ), + _cache( max_pis ), + _cache_filename( cache_filename ), + _blacklist_cache_info( blacklist_cache_info ), + _initial_size( max_pis ) + { + if ( !_cache_filename.empty() ) + { + load(); + } + } + + ~cached_resynthesis() + { + if ( !_cache_filename.empty() ) + { + save(); + } + } + +private: + using cache_key_t = std::pair>; + + struct cache_hash + { + std::size_t operator()( const cache_key_t& p ) const + { + auto seed = _h( p.first ); + std::for_each( p.second.begin(), p.second.end(), [&]( auto const& tt ) { kitty::hash_combine( seed, _h( tt ) ); } ); + return seed; + } + + private: + kitty::hash _h; + }; + + using blacklist_cache_key_t = std::pair; + + struct blacklist_cache_hash + { + std::size_t operator()( const blacklist_cache_key_t& p ) const + { + return _h( p.first ); + } + + private: + kitty::hash _h; + }; + + struct blacklist_cache_equal + { + bool operator()( const blacklist_cache_key_t& lhs, const blacklist_cache_key_t& rhs ) const + { + return lhs.first == rhs.first; + } + }; + + bool is_blacklisted( kitty::dynamic_truth_table const& tt ) const + { + auto it = _blacklist_cache.find( { tt, _blacklist_cache_info } ); + + /* function cannot be found in black list cache */ + if ( it == _blacklist_cache.end() ) + { + return false; + } + /* newer black list info, erase old entry from cache */ + else if ( _blacklist_cache_info.retry( it->second ) ) + { + _blacklist_cache.erase( it ); + return false; + } + /* function is black listed */ + else + { + return true; + } + } + +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( auto const key = std::make_pair( function, _existing_functions ); + _cache.has( key ) ) + { + ++_cache_hits; + std::vector> signals( _cache.pis().size(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + std::copy( _existing_signals.begin(), _existing_signals.end(), signals.begin() + _initial_size ); + fn( cleanup_dangling( _cache.get_view( key ), ntk, signals.begin(), signals.end() ).front() ); + } + else if ( is_blacklisted( function ) ) + { + ++_cache_hits; + return; /* do nothing */ + } + else + { + bool found_one = false; + auto on_signal = [&]( signal const& f ) -> bool { + if ( !found_one ) + { + ++_cache_misses; + _cache.insert_signal( key, f ); + found_one = true; + + std::vector> signals( _cache.pis().size(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + std::copy( _existing_signals.begin(), _existing_signals.end(), signals.begin() + _initial_size ); + fn( cleanup_dangling( _cache.get_view( key ), ntk, signals.begin(), signals.end() ).front() ); + } + return false; + }; + + _resyn_fn( _cache.network(), function, _cache.pis().begin(), _cache.pis().begin() + function.num_vars(), on_signal ); + + if ( !found_one ) + { + _blacklist_cache.insert( { function, _blacklist_cache_info } ); + } + } + } + + void set_bounds( std::optional const& lower_bound, std::optional const& upper_bound ) + { + if constexpr ( has_set_bounds_v ) + { + _resyn_fn.set_bounds( lower_bound, upper_bound ); + } + } + + void clear_functions() + { + if constexpr ( has_clear_functions_v ) + { + _existing_signals.clear(); + _existing_functions.clear(); + _resyn_fn.clear_functions(); + } + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + if constexpr ( has_add_function_v ) + { + // index of cache PI to forward + const auto pi_index = _initial_size + _existing_signals.size(); + + _existing_signals.push_back( s ); + _existing_functions.push_back( tt ); + _cache.ensure_pis( _initial_size + _existing_functions.size() ); + + _resyn_fn.add_function( _cache.pis()[pi_index], tt ); + } + else + { + // TODO assert or warn? + } + } + + void report() const + { + fmt::print( "[i] cache hits = {}\n", _cache_hits ); + fmt::print( "[i] cache misses = {}\n", _cache_misses ); + fmt::print( "[i] size of cache = {}\n", _cache.size() ); + fmt::print( "[i] size of blacklist cache = {}\n", _blacklist_cache.size() ); + } + +private: + void load() + { + std::ifstream is( _cache_filename.c_str(), std::ifstream::in ); + if ( !is.good() ) + return; + nlohmann::json data; + is >> data; + + _cache.insert_json( data["cache"] ); + data["blacklist_cache"].get_to( _blacklist_cache ); + data["initial_size"].get_to( _initial_size ); + } + + void save() + { +#if __GNUC__ == 7 + namespace fs = std::experimental::filesystem::v1; +#else + namespace fs = std::filesystem; +#endif + + nlohmann::json data{ + { "cache", _cache.to_json() }, + { "blacklist_cache", _blacklist_cache }, + { "initial_size", _initial_size } }; + + // make a backup of existing cache file, if it exists + std::string _backup_filename = fmt::format( "{}.bak", _cache_filename ); + if ( fs::exists( _cache_filename ) ) + { + fs::copy( _cache_filename, _backup_filename ); + } + + std::ofstream os( _cache_filename.c_str(), std::ofstream::out ); + os << data.dump() << "\n"; + os.close(); + + if ( fs::exists( _backup_filename ) ) + { + fs::remove( _backup_filename ); + } + } + +private: + ResynthesisFn _resyn_fn; + mutable network_cache _cache; + mutable std::unordered_set _blacklist_cache; + std::string _cache_filename; + BlacklistCacheInfo _blacklist_cache_info; + uint32_t _initial_size{}; + + std::vector _existing_functions; + std::vector> _existing_signals; + + /* statistics */ + mutable uint32_t _cache_hits{}; + mutable uint32_t _cache_misses{}; +}; +} /* namespace mockturtle */ + +#endif diff --git a/include/mockturtle/algorithms/node_resynthesis/composed.hpp b/include/mockturtle/algorithms/node_resynthesis/composed.hpp new file mode 100644 index 0000000..7219c3c --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/composed.hpp @@ -0,0 +1,83 @@ +/* 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 composed.hpp + \brief Traits for additional node_resynthesis methods + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#if !__clang__ || __clang_major__ > 10 + +#include +#include + +#include "../../networks/aig.hpp" +#include "../../networks/xag.hpp" +#include "cached.hpp" +#include "exact.hpp" + +namespace mockturtle +{ + +struct exact_blacklist_cache_info +{ + bool retry( exact_blacklist_cache_info const& old_info ) const + { + return conflict_limit > old_info.conflict_limit; + } + + int conflict_limit; +}; + +inline void to_json( nlohmann::json& j, exact_blacklist_cache_info const& info ) +{ + j = info.conflict_limit; +} + +inline void from_json( nlohmann::json const& j, exact_blacklist_cache_info& info ) +{ + j.get_to( info.conflict_limit ); +} + +template +auto cached_exact_xag_resynthesis( std::string const& cache_filename, uint32_t input_limit = 12u, int conflict_limit = 10e5 ) +{ + exact_resynthesis_params exact_ps; + exact_ps.conflict_limit = conflict_limit; + exact_aig_resynthesis exact_resyn( std::is_same_v, exact_ps ); + exact_blacklist_cache_info info; + info.conflict_limit = conflict_limit; + return cached_resynthesis( exact_resyn, input_limit, cache_filename, info ); +} + +} // namespace mockturtle + +#endif \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/davio.hpp b/include/mockturtle/algorithms/node_resynthesis/davio.hpp new file mode 100644 index 0000000..064f5ad --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/davio.hpp @@ -0,0 +1,150 @@ +/* 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 davio.hpp + \brief Use Davio decomposition for resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../decomposition.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Davio decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + positive_davio_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class positive_davio_resynthesis +{ +public: + positive_davio_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = positive_davio_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = positive_davio_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +/*! \brief Resynthesis function based on Davio decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + negative_davio_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class negative_davio_resynthesis +{ +public: + negative_davio_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = negative_davio_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = negative_davio_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/direct.hpp b/include/mockturtle/algorithms/node_resynthesis/direct.hpp new file mode 100644 index 0000000..0fdd83a --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/direct.hpp @@ -0,0 +1,264 @@ +/* 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 direct.hpp + \brief Resynthesis by trying to directly add gates + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../../algorithms/akers_synthesis.hpp" +#include "../../networks/mig.hpp" + +namespace mockturtle +{ + +struct direct_resynthesis_params +{ + bool warn_on_unsupported{ false }; +}; + +/*! \brief Resynthesis function that creates a gate for each node. + * + * If it detects a function that can be constructed as a gate, it does so. + * Otherwise, it does not create a gate. In that case, a warning can be + * printed, if configured in the parameter struct. + * + * The function works with all 0-, 1-, and 2-input node functions and with + * some 3-input node functions, e.g., 3-input majority for MIGs and XMGs, or + * 3-input XOR for XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + direct_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +template +class direct_resynthesis +{ +public: + direct_resynthesis( direct_resynthesis_params const& ps = {} ) + : ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + (void)end; + switch ( function.num_vars() ) + { + case 0u: + synthesize0( ntk, function, fn ); + break; + + case 1u: + synthesize1( ntk, function, *begin, fn ); + break; + + case 2u: + synthesize2( ntk, function, *begin, *( begin + 1 ), fn ); + break; + + case 3u: + synthesize3( ntk, function, *begin, *( begin + 1 ), *( begin + 2 ), fn ); + break; + } + } + +private: + template + void synthesize0( Ntk& ntk, kitty::dynamic_truth_table const& function, Fn&& fn ) const + { + fn( ntk.get_constant( kitty::is_const0( function ) ) ); + } + + template + void synthesize1( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, Fn&& fn ) const + { + switch ( *function.begin() ) + { + case 0b00: + fn( ntk.get_constant( false ) ); + break; + case 0b01: + fn( ntk.create_not( f ) ); + break; + case 0b10: + fn( f ); + break; + case 0b11: + fn( ntk.get_constant( true ) ); + break; + } + } + + template + void synthesize2( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, signal const& g, Fn&& fn ) const + { + switch ( *function.begin() ) + { + case 0b0000: + fn( ntk.get_constant( false ) ); + break; + case 0b0001: /* NOR */ + fn( ntk.create_not( ntk.create_or( f, g ) ) ); + break; + case 0b0010: /* AND(f, !g) */ + fn( ntk.create_and( f, ntk.create_not( g ) ) ); + break; + case 0b0011: /* !g */ + fn( ntk.create_not( g ) ); + break; + case 0b0100: /* AND(!f, g) */ + fn( ntk.create_and( ntk.create_not( f ), g ) ); + break; + case 0b0101: /* !f */ + fn( ntk.create_not( f ) ); + break; + case 0b0110: /* XOR */ + fn( ntk.create_xor( f, g ) ); + break; + case 0b0111: /* NAND */ + fn( ntk.create_not( ntk.create_and( f, g ) ) ); + break; + case 0b1000: /* AND */ + fn( ntk.create_and( f, g ) ); + break; + case 0b1001: /* XNOR */ + fn( ntk.create_not( ntk.create_xor( f, g ) ) ); + break; + case 0b1010: /* f */ + fn( f ); + break; + case 0b1011: /* OR(f, !g) */ + fn( ntk.create_or( f, ntk.create_not( g ) ) ); + break; + case 0b1100: /* g */ + fn( g ); + break; + case 0b1101: /* OR(!f, g) */ + fn( ntk.create_or( ntk.create_not( f ), g ) ); + break; + case 0b1110: /* OR(f, g) */ + fn( ntk.create_or( f, g ) ); + break; + case 0b1111: + fn( ntk.get_constant( true ) ); + break; + } + } + + template + void synthesize3( Ntk& ntk, kitty::dynamic_truth_table const& function, signal const& f, signal const& g, signal const& h, Fn&& fn ) const + { + // TODO? all contained extended 1-input and 2-input functions + // TODO create_ite + + const auto word = *function.begin(); + switch ( word ) + { + case 0x00: + fn( ntk.get_constant( false ) ); + break; + case 0xff: + fn( ntk.get_constant( true ) ); + break; + case 0xe8: /* */ + case 0xd4: /* */ + case 0xb2: /* */ + case 0x8e: /* */ + case 0x71: /* */ + case 0x4d: /* */ + case 0x2b: /* */ + case 0x17: /* */ + if constexpr ( has_create_maj_v ) + { + const auto _f = ( ( word == 0xd4 ) || ( word == 0x71 ) || ( word == 0x4d ) || ( word == 0x17 ) ) ? ntk.create_not( f ) : f; + const auto _g = ( ( word == 0xb2 ) || ( word == 0x71 ) || ( word == 0x2b ) || ( word == 0x17 ) ) ? ntk.create_not( g ) : g; + const auto _h = ( ( word == 0x8e ) || ( word == 0x4d ) || ( word == 0x2b ) || ( word == 0x17 ) ) ? ntk.create_not( h ) : h; + fn( ntk.create_maj( _f, _g, _h ) ); + } + else + { + if ( ps.warn_on_unsupported ) + { + std::cout << "[w] function " << kitty::to_hex( function ) << " cannot be synthesized as gate\n"; + } + } + break; + case 0x96: /* [abc] */ + case 0x69: /* ![abc] */ + if constexpr ( has_create_xor3_v ) + { + const auto o = ntk.create_xor3( f, g, h ); + fn( word == 0x69 ? ntk.create_not( o ) : o ); + } + else + { + if ( ps.warn_on_unsupported ) + { + std::cout << "[w] function " << kitty::to_hex( function ) << " cannot be synthesized as gate\n"; + } + } + break; + default: + std::cout << "[w] failed to synthesize function " << kitty::to_hex( function ) << "\n"; + } + } + +private: + direct_resynthesis_params ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/dsd.hpp b/include/mockturtle/algorithms/node_resynthesis/dsd.hpp new file mode 100644 index 0000000..2fc1a0c --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/dsd.hpp @@ -0,0 +1,147 @@ +/* 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 dsd.hpp + \brief Use DSD as pre-process to resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include + +#include "../dsd_decomposition.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for dsd_resynthesis function. */ +struct dsd_resynthesis_params +{ + /*! \brief Skip resynthesis on prime nodes, if it exceeds this limit. */ + std::optional prime_input_limit; + + /*! \brief DSD decomposition parameters */ + dsd_decomposition_params dsd_ps; +}; + +/*! \brief Resynthesis function based on DSD decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on DSD decomposition. Since DSD decomposition may not be + * able to decompose the whole truth table, a different fall-back resynthesis + * function must be passed to this function. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + + exact_aig_resynthesis fallback; // fallback + dsd_resynthesis resyn( fallback ); + cut_rewriting( aig, resyn ); + aig = cleanup_dangling( aig ); + \endverbatim + * + */ +template +class dsd_resynthesis +{ +public: + explicit dsd_resynthesis( ResynthesisFn& resyn_fn, dsd_resynthesis_params const& ps = {} ) + : _resyn_fn( resyn_fn ), + _ps( ps ) + { + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + bool success{ true }; + const auto on_prime = [&]( kitty::dynamic_truth_table const& remainder, std::vector> const& leaves ) { + success = false; + signal f = ntk.get_constant( false ); + if ( _ps.prime_input_limit && leaves.size() > *_ps.prime_input_limit ) + { + return f; + } + + const auto on_signal = [&]( signal const& _f ) { + if ( !success ) + { + f = _f; + success = true; + } + return true; + }; + auto _leaves = leaves; + + if constexpr ( has_set_bounds_v ) + { + _resyn_fn.set_bounds( static_cast( _leaves.size() ), std::nullopt ); + } + _resyn_fn( ntk, remainder, _leaves.begin(), _leaves.end(), on_signal ); + return f; + }; + + const auto f = dsd_decomposition( ntk, function, std::vector>( begin, end ), on_prime, _ps.dsd_ps ); + if ( success ) + { + fn( f ); + } + } + + void clear_functions() + { + if constexpr ( has_clear_functions_v ) + { + _resyn_fn.clear_functions(); + } + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + if constexpr ( has_add_function_v ) + { + _resyn_fn.add_function( s, tt ); + } + } + +private: + ResynthesisFn& _resyn_fn; + dsd_resynthesis_params _ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/exact.hpp b/include/mockturtle/algorithms/node_resynthesis/exact.hpp new file mode 100644 index 0000000..98920b4 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/exact.hpp @@ -0,0 +1,659 @@ +/* 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 exact.hpp + \brief Replace with exact synthesis result + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../networks/aig.hpp" +#include "../../networks/klut.hpp" +#include "../../networks/xmg.hpp" +#include "../../utils/include/percy.hpp" + +namespace mockturtle +{ + +struct exact_resynthesis_params +{ + using cache_map_t = std::unordered_map>; + using cache_t = std::shared_ptr; + + using blacklist_cache_map_t = std::unordered_map>; + using blacklist_cache_t = std::shared_ptr; + + cache_t cache; + blacklist_cache_t blacklist_cache; + + bool add_alonce_clauses{ true }; + bool add_colex_clauses{ true }; + bool add_lex_clauses{ false }; + bool add_lex_func_clauses{ true }; + bool add_nontriv_clauses{ true }; + bool add_noreapply_clauses{ true }; + bool add_symvar_clauses{ true }; + int conflict_limit{ 0 }; + + percy::SolverType solver_type = percy::SLV_BSAT2; + + percy::EncoderType encoder_type = percy::ENC_SSV; + + percy::SynthMethod synthesis_method = percy::SYNTH_STD; +}; + +/*! \brief Resynthesis function based on exact synthesis. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized in terms of an optimum size `k`-LUT network, where `k` is + * specified as input to the constructor. In order to guarantee a reasonable + * runtime, `k` should be 3 or 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + exact_resynthesis resyn( 3 ); + klut = cut_rewriting( klut, resyn ); + \endverbatim + * + * A cache can be passed as second parameter to the constructor, which will + * store optimum networks for all functions for which resynthesis is invoked + * for. The cache can be used to retrieve the computed network, which reduces + * runtime. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + exact_resynthesis_params ps; + ps.cache = std::make_shared(); + exact_resynthesis resyn( 3, ps ); + klut = cut_rewriting( klut, resyn ); + + The underlying engine for this resynthesis function is percy_. + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_resynthesis +{ +public: + explicit exact_resynthesis( uint32_t fanin_size = 3u, exact_resynthesis_params const& ps = {} ) + : _fanin_size( fanin_size ), + _ps( ps ) + { + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( static_cast( function.num_vars() ) <= _fanin_size ) + { + fn( ntk.create_node( std::vector>( begin, end ), function ) ); + return; + } + + percy::spec spec; + spec.fanin = _fanin_size; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + spec[0] = function; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( dont_cares ) ) + { + spec.set_dont_care( 0, dont_cares ); + with_dont_cares = true; + } + + auto c = [&]() -> std::optional { + if ( !with_dont_cares && _ps.cache ) + { + const auto it = _ps.cache->find( function ); + if ( it != _ps.cache->end() ) + { + return it->second; + } + } + if ( !with_dont_cares && _ps.blacklist_cache ) + { + const auto it = _ps.blacklist_cache->find( function ); + if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + { + return std::nullopt; + } + } + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + if ( !with_dont_cares && _ps.blacklist_cache ) + { + ( *_ps.blacklist_cache )[function] = result == percy::timeout ? _ps.conflict_limit : 0; + } + return std::nullopt; + } + c.denormalize(); + if ( !with_dont_cares && _ps.cache ) + { + ( *_ps.cache )[function] = c; + } + return c; + }(); + + if ( !c ) + { + return; + } + + std::vector> signals( begin, end ); + + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + std::vector> fanin; + for ( const auto& child : c->get_step( i ) ) + { + fanin.emplace_back( signals[child] ); + } + signals.emplace_back( ntk.create_node( fanin, c->get_operator( i ) ) ); + } + + fn( signals.back() ); + } + +private: + uint32_t _fanin_size{ 3u }; + exact_resynthesis_params _ps; +}; + +/*! \brief Resynthesis function based on exact synthesis for AIGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthesized in terms of an optimum size AIG network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + exact_aig_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + \endverbatim + * + * A cache can be passed as second parameter to the constructor, which will + * store optimum networks for all functions for which resynthesis is invoked + * for. The cache can be used to retrieve the computed network, which reduces + * runtime. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + exact_resynthesis_params ps; + ps.cache = std::make_shared(); + exact_aig_resynthesis resyn( false, ps ); + aig = cut_rewriting( aig, resyn ); + + The underlying engine for this resynthesis function is percy_. + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_aig_resynthesis +{ +public: + using signal = typename Ntk::signal; + +public: + explicit exact_aig_resynthesis( bool _allow_xor = false, exact_resynthesis_params const& ps = {} ) + : _allow_xor( _allow_xor ), + _ps( ps ) + { + } + + void clear_functions() + { + existing_functions.clear(); + } + + void add_function( signal const& s, kitty::dynamic_truth_table const& tt ) + { + existing_functions.emplace_back( s, tt ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + operator()( ntk, function, function.construct(), begin, end, fn ); + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + // TODO: special case for small functions (up to 2 variables)? + percy::spec spec; + if ( !_allow_xor ) + { + spec.set_primitive( percy::AIG ); + } + spec.fanin = 2; + spec.verbosity = 0; + spec.add_alonce_clauses = _ps.add_alonce_clauses; + spec.add_colex_clauses = _ps.add_colex_clauses; + spec.add_lex_clauses = _ps.add_lex_clauses; + spec.add_lex_func_clauses = _ps.add_lex_func_clauses; + spec.add_nontriv_clauses = _ps.add_nontriv_clauses; + spec.add_noreapply_clauses = _ps.add_noreapply_clauses; + spec.add_symvar_clauses = _ps.add_symvar_clauses; + spec.conflict_limit = _ps.conflict_limit; + if ( _lower_bound ) + { + spec.initial_steps = *_lower_bound; + } + if ( _upper_bound ) + { + spec.max_nr_steps = *_upper_bound; + } + spec[0] = function; + bool with_dont_cares{ false }; + if ( !kitty::is_const0( dont_cares ) ) + { + spec.set_dont_care( 0, dont_cares ); + with_dont_cares = true; + } + + /* add existing functions */ + for ( const auto& f : existing_functions ) + { + spec.add_function( f.second ); + } + + auto c = [&]() -> std::optional { + if ( !with_dont_cares && _ps.cache ) + { + const auto it = _ps.cache->find( function ); + if ( it != _ps.cache->end() ) + { + return it->second; + } + } + if ( !with_dont_cares && _ps.blacklist_cache ) + { + const auto it = _ps.blacklist_cache->find( function ); + if ( it != _ps.blacklist_cache->end() && ( it->second == 0 || _ps.conflict_limit <= it->second ) ) + { + return std::nullopt; + } + } + + percy::chain c; + if ( const auto result = percy::synthesize( spec, c, _ps.solver_type, + _ps.encoder_type, + _ps.synthesis_method ); + result != percy::success ) + { + if ( !with_dont_cares && _ps.blacklist_cache ) + { + ( *_ps.blacklist_cache )[function] = ( result == percy::timeout ) ? _ps.conflict_limit : 0; + } + return std::nullopt; + } + + assert( kitty::to_hex( c.simulate()[0u] ) == kitty::to_hex( function ) ); + + if ( !with_dont_cares && _ps.cache ) + { + ( *_ps.cache )[function] = c; + } + return c; + }(); + + if ( !c ) + { + return; + } + + std::vector signals( begin, end ); + for ( const auto& f : existing_functions ) + { + signals.emplace_back( f.first ); + } + + for ( auto i = 0; i < c->get_nr_steps(); ++i ) + { + auto const c1 = signals[c->get_step( i )[0]]; + auto const c2 = signals[c->get_step( i )[1]]; + + switch ( c->get_operator( i )._bits[0] ) + { + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( c->get_operator( i ) ) << "\n"; + assert( false ); + break; + case 0x8: + signals.emplace_back( ntk.create_and( c1, c2 ) ); + break; + case 0x4: + signals.emplace_back( ntk.create_and( !c1, c2 ) ); + break; + case 0x2: + signals.emplace_back( ntk.create_and( c1, !c2 ) ); + break; + case 0xe: + signals.emplace_back( !ntk.create_and( !c1, !c2 ) ); + break; + case 0x6: + signals.emplace_back( ntk.create_xor( c1, c2 ) ); + break; + } + } + + fn( c->is_output_inverted( 0 ) ? !signals.back() : signals.back() ); + } + + void set_bounds( std::optional const& lower_bound, std::optional const& upper_bound ) + { + _lower_bound = lower_bound; + _upper_bound = upper_bound; + } + +private: + bool _allow_xor = false; + exact_resynthesis_params _ps; + + std::vector> existing_functions; + + std::optional _lower_bound; + std::optional _upper_bound; +}; + +struct exact_xmg_resynthesis_params +{ + uint32_t num_candidates{ 10u }; + bool use_only_self_dual_gates{ false }; + bool use_xor3{ true }; + int conflict_limit{ 0 }; +}; + +/*! \brief Resynthesis function based on exact synthesis for XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthesized in terms of an optimum size XMG network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + xmg_network aig = ...; + + exact_xmg_resynthesis resyn; + xmg = cut_rewriting( xmg, resyn ); + \endverbatim + * + * + The underlying engine for this resynthesis function is percy_. + \verbatim embed:rst + + .. _percy: https://github.com/lsils/percy + \endverbatim + * + */ +template +class exact_xmg_resynthesis +{ +public: + explicit exact_xmg_resynthesis( exact_xmg_resynthesis_params const& ps = {} ) + : ps( ps ) + { + } + + template + void operator()( Ntk& ntk, TT const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + static_assert( kitty::is_complete_truth_table::value, "Truth table must be complete" ); + + using signal = mockturtle::signal; + auto const tt = function.num_vars() < 3u ? kitty::extend_to( function, 3u ) : function; + bool const normal = kitty::is_normal( tt ); + + percy::chain chain; + percy::spec spec; + spec.verbosity = 0; + spec.fanin = 3; + spec.conflict_limit = ps.conflict_limit; + + /* specify local normalized gate primitives */ + kitty::dynamic_truth_table const0{ 3 }; + kitty::dynamic_truth_table a{ 3 }; + kitty::dynamic_truth_table b{ 3 }; + kitty::dynamic_truth_table c{ 3 }; + kitty::create_nth_var( a, 0 ); + kitty::create_nth_var( b, 1 ); + kitty::create_nth_var( c, 2 ); + + spec.add_primitive( const0 ); // 00 + spec.add_primitive( a ); // aa + spec.add_primitive( b ); // cc + spec.add_primitive( c ); // f0 + + /* add self dual gate functions */ + spec.add_primitive( kitty::ternary_majority( a, b, c ) ); // e8 + spec.add_primitive( kitty::ternary_majority( ~a, b, c ) ); // d4 + spec.add_primitive( kitty::ternary_majority( a, ~b, c ) ); // b2 + spec.add_primitive( kitty::ternary_majority( a, b, ~c ) ); // 8e + spec.add_primitive( a ^ b ); // 66 + spec.add_primitive( a ^ c ); // 3c + spec.add_primitive( b ^ c ); // 5a + if ( ps.use_xor3 ) + { + spec.add_primitive( a ^ b ^ c ); // 96 + } + + /* add non-self dual gate functions */ + if ( !ps.use_only_self_dual_gates ) + { + spec.add_primitive( kitty::ternary_majority( const0, b, c ) ); // c0 + spec.add_primitive( kitty::ternary_majority( ~const0, b, c ) ); // fc + spec.add_primitive( kitty::ternary_majority( const0, ~b, c ) ); // 30 + spec.add_primitive( kitty::ternary_majority( const0, b, ~c ) ); // 0c + spec.add_primitive( kitty::ternary_majority( a, const0, c ) ); // a0 + spec.add_primitive( kitty::ternary_majority( ~a, const0, c ) ); // 50 + spec.add_primitive( kitty::ternary_majority( a, ~const0, c ) ); // fa + spec.add_primitive( kitty::ternary_majority( a, const0, ~c ) ); // 0a + spec.add_primitive( kitty::ternary_majority( a, b, const0 ) ); // 88 + spec.add_primitive( kitty::ternary_majority( a, b, ~const0 ) ); // ee + spec.add_primitive( kitty::ternary_majority( ~a, b, const0 ) ); // 44 + spec.add_primitive( kitty::ternary_majority( a, ~b, const0 ) ); // 22 + } + + percy::bsat_wrapper solver; + percy::ssv_encoder encoder( solver ); + + spec[0] = normal ? tt : ~tt; + + for ( auto i = 0u; i < ps.num_candidates; ++i ) + { + auto const result = percy::next_struct_solution( spec, chain, solver, encoder ); + if ( result != percy::success ) + break; + + assert( result == percy::success ); + + auto const sim = chain.simulate(); + assert( chain.simulate()[0] == spec[0] ); + + std::vector signals( tt.num_vars(), ntk.get_constant( false ) ); + std::copy( begin, end, signals.begin() ); + + for ( auto i = 0; i < chain.get_nr_steps(); ++i ) + { + auto const c1 = signals[chain.get_step( i )[0]]; + auto const c2 = signals[chain.get_step( i )[1]]; + auto const c3 = signals[chain.get_step( i )[2]]; + + switch ( chain.get_operator( i )._bits[0] ) + { + case 0x00: + signals.emplace_back( ntk.get_constant( false ) ); + break; + case 0xe8: + signals.emplace_back( ntk.create_maj( c1, c2, c3 ) ); + break; + case 0xd4: + signals.emplace_back( ntk.create_maj( !c1, c2, c3 ) ); + break; + case 0xb2: + signals.emplace_back( ntk.create_maj( c1, !c2, c3 ) ); + break; + case 0x8e: + signals.emplace_back( ntk.create_maj( c1, c2, !c3 ) ); + break; + case 0x96: + signals.emplace_back( ntk.create_xor3( c1, c2, c3 ) ); + break; + case 0xc0: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), c2, c3 ) ); // c0 + break; + case 0xfc: + signals.emplace_back( ntk.create_maj( !ntk.get_constant( false ), c2, c3 ) ); // fc + break; + case 0x30: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), !c2, c3 ) ); // 30 + break; + case 0x0c: + signals.emplace_back( ntk.create_maj( ntk.get_constant( false ), c2, !c3 ) ); // 0c + break; + case 0xa0: + signals.emplace_back( ntk.create_maj( c1, ntk.get_constant( false ), c3 ) ); // 0a + break; + case 0x50: + signals.emplace_back( ntk.create_maj( !c1, ntk.get_constant( false ), c3 ) ); // 50 + break; + case 0xfa: + signals.emplace_back( ntk.create_maj( c1, !ntk.get_constant( false ), c3 ) ); // fa + break; + case 0x0a: + signals.emplace_back( ntk.create_maj( c1, ntk.get_constant( false ), !c3 ) ); // 0a + break; + case 0x88: + signals.emplace_back( ntk.create_maj( c1, c2, ntk.get_constant( false ) ) ); // 88 + break; + case 0xee: + signals.emplace_back( ntk.create_maj( c1, c2, !ntk.get_constant( false ) ) ); // ee + break; + case 0x44: + signals.emplace_back( ntk.create_maj( !c1, c2, ntk.get_constant( false ) ) ); // 44 + break; + case 0x22: + signals.emplace_back( ntk.create_maj( c1, !c2, ntk.get_constant( false ) ) ); // 22 + break; + case 0x66: + signals.emplace_back( ntk.create_xor( c1, c2 ) ); + break; + case 0x3c: + signals.emplace_back( ntk.create_xor( c2, c3 ) ); + break; + case 0x5a: + signals.emplace_back( ntk.create_xor( c1, c3 ) ); + break; + default: + std::cerr << "[e] unsupported operation " << kitty::to_hex( chain.get_operator( i ) ) << "\n"; + assert( false ); + break; + } + } + + assert( chain.get_outputs().size() > 0u ); + uint32_t const output_index = ( chain.get_outputs()[0u] >> 1u ); + auto const output_signal = output_index == 0u ? ntk.get_constant( false ) : signals[output_index - 1]; + if ( !fn( chain.is_output_inverted( 0 ) ^ normal ? output_signal : !output_signal ) ) + { + return; /* quit */ + } + } + } + +protected: + exact_xmg_resynthesis_params const ps; +}; /* exact_xmg_resynthesis */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp b/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp new file mode 100644 index 0000000..4f8fb78 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/mig_npn.hpp @@ -0,0 +1,236 @@ +/* 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_npn.hpp + \brief Replace with size-optimum MIGs from NPN + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../networks/mig.hpp" +#include "../../traits.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on pre-computed size-optimum MIGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. It will produce an MIG based on + * pre-computed size-optimum MIGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + mig_npn_resynthesis resyn; + const auto mig = node_resynthesis( klut, resyn ); + \endverbatim + */ +class mig_npn_resynthesis +{ +public: + /*! \brief Default constructor. + * + * \param use_multiple If true, up to 10 structures are tried for each + * function. + */ + mig_npn_resynthesis( bool use_multiple = false ) + { + if ( use_multiple ) + { + build_db10(); + } + else + { + build_db(); + } + } + + template + void operator()( mig_network& mig, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 4 ); + const auto fe = kitty::extend_to( function, 4 ); + const auto config = kitty::exact_npn_canonization( fe ); + + const auto it = class2signal.find( static_cast( std::get<0>( config )._bits[0] ) ); + + std::vector pis( 4, mig.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector pis_perm( 4 ); + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm[i] = pis[perm[i]]; + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = !pis_perm[i]; + } + } + + for ( auto const& po : it->second ) + { + topo_view topo{ db, po }; + auto f = cleanup_dangling( topo, mig, pis_perm.begin(), pis_perm.end() ).front(); + + mockturtle::print(topo); + if ( !fn( ( ( phase >> 4 ) & 1 ) ? !f : f ) ) + { + return; /* quit */ + } + } + } + +private: + void build_db() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + auto p = nodes.begin(); + for ( auto i = 0u; i < *p; ++i ) + { + signals.push_back( db.create_pi() ); + } + + p++; /* point to number of outputs */ + p += *p + 1; /* move past number of output */ + + /* create nodes */ + while ( p != nodes.end() ) + { + const auto c1 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c2 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c3 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + signals.push_back( db.create_maj( c1, c2, c3 ) ); + } + + /* create POs */ + p = nodes.begin() + 2; + for ( auto i = 0u; i < nodes[1]; ++i ) + { + const auto driver = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + db.create_po( driver ); + class2signal[classes[i]].push_back( driver ); + } + } + + void build_db10() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + auto p = nodes10.begin(); + for ( auto i = 0u; i < *p; ++i ) + { + signals.push_back( db.create_pi() ); + } + + p++; /* point to number of outputs */ + const auto num_functions = *p++; + + for ( auto i = 0u; i < num_functions; ++i ) + { + p += *p + 1; + } + + /* create nodes */ + while ( p != nodes10.end() ) + { + const auto c1 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c2 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + const auto c3 = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + signals.push_back( db.create_maj( c1, c2, c3 ) ); + } + + /* create PIs */ + p = nodes10.begin() + 2; + for ( auto i = 0u; i < nodes10[1]; ++i ) + { + const auto functions = *p++; + for ( auto j = 0u; j < functions; ++j ) + { + const auto driver = signals[*p >> 1] ^ ( *p & 1 ); + ++p; + + db.create_po( driver ); + class2signal[classes[i]].push_back( driver ); + } + } + } + + mig_network db; + std::unordered_map> class2signal; + + inline static const std::vector classes{ { 0x1ee1, 0x1be4, 0x1bd8, 0x18e7, 0x17e8, 0x17ac, 0x1798, 0x1796, 0x178e, 0x177e, 0x16e9, 0x16bc, 0x169e, 0x003f, 0x0359, 0x0672, 0x07e9, 0x0693, 0x0358, 0x01bf, 0x6996, 0x0356, 0x01bd, 0x001f, 0x01ac, 0x001e, 0x0676, 0x01ab, 0x01aa, 0x001b, 0x07e1, 0x07e0, 0x0189, 0x03de, 0x035a, 0x1686, 0x0186, 0x03db, 0x0357, 0x01be, 0x1683, 0x0368, 0x0183, 0x03d8, 0x07e6, 0x0182, 0x03d7, 0x0181, 0x03d6, 0x167e, 0x016a, 0x007e, 0x0169, 0x006f, 0x0069, 0x0168, 0x0001, 0x019a, 0x036b, 0x1697, 0x0369, 0x0199, 0x0000, 0x169b, 0x003d, 0x036f, 0x0666, 0x019b, 0x0187, 0x03dc, 0x0667, 0x0003, 0x168e, 0x06b6, 0x01eb, 0x07e2, 0x017e, 0x07b6, 0x007f, 0x19e3, 0x06b7, 0x011a, 0x077e, 0x018b, 0x00ff, 0x0673, 0x01a8, 0x000f, 0x1696, 0x036a, 0x011b, 0x0018, 0x0117, 0x1698, 0x036c, 0x01af, 0x0016, 0x067a, 0x0118, 0x0017, 0x067b, 0x0119, 0x169a, 0x003c, 0x036e, 0x07e3, 0x017f, 0x03d4, 0x06f0, 0x011e, 0x037c, 0x012c, 0x19e6, 0x01ef, 0x16a9, 0x037d, 0x006b, 0x012d, 0x012f, 0x01fe, 0x0019, 0x03fc, 0x179a, 0x013c, 0x016b, 0x06f2, 0x03c0, 0x033c, 0x1668, 0x0669, 0x019e, 0x013d, 0x0006, 0x019f, 0x013e, 0x0776, 0x013f, 0x016e, 0x03c3, 0x3cc3, 0x033f, 0x166b, 0x016f, 0x011f, 0x035e, 0x0690, 0x0180, 0x03d5, 0x06f1, 0x06b0, 0x037e, 0x03c1, 0x03c5, 0x03c6, 0x01a9, 0x166e, 0x03cf, 0x03d9, 0x07bc, 0x01bc, 0x1681, 0x03dd, 0x03c7, 0x06f9, 0x0660, 0x0196, 0x0661, 0x0197, 0x0662, 0x07f0, 0x0198, 0x0663, 0x07f1, 0x0007, 0x066b, 0x033d, 0x1669, 0x066f, 0x01ad, 0x0678, 0x01ae, 0x0679, 0x067e, 0x168b, 0x035f, 0x0691, 0x0696, 0x0697, 0x06b1, 0x0778, 0x16ac, 0x06b2, 0x0779, 0x16ad, 0x01e8, 0x06b3, 0x0116, 0x077a, 0x01e9, 0x06b4, 0x19e1, 0x01ea, 0x06b5, 0x01ee, 0x06b9, 0x06bd, 0x06f6, 0x07b0, 0x07b1, 0x07b4, 0x07b5, 0x07f2, 0x07f8, 0x018f, 0x0ff0, 0x166a, 0x035b, 0x1687, 0x1689, 0x036d, 0x069f, 0x1699 } }; + inline static const std::vector nodes{ { 4, 222, 17, 24, 34, 41, 46, 56, 68, 76, 84, 96, 109, 116, 122, 127, 137, 142, 151, 157, 166, 173, 182, 188, 193, 199, 208, 214, 220, 227, 232, 239, 247, 256, 259, 264, 272, 278, 286, 293, 297, 300, 307, 312, 321, 328, 336, 344, 351, 355, 362, 372, 378, 384, 387, 389, 393, 398, 401, 408, 417, 421, 425, 433, 0, 439, 445, 451, 454, 459, 467, 472, 475, 477, 482, 486, 491, 498, 502, 506, 509, 517, 523, 526, 532, 537, 9, 545, 548, 335, 554, 560, 563, 568, 573, 576, 580, 583, 586, 594, 596, 599, 603, 605, 612, 616, 622, 627, 629, 630, 634, 638, 640, 644, 650, 657, 665, 669, 675, 677, 679, 686, 691, 696, 702, 708, 713, 718, 722, 728, 738, 747, 750, 755, 756, 761, 766, 770, 773, 778, 785, 789, 159, 797, 801, 803, 810, 812, 820, 827, 831, 836, 844, 853, 857, 862, 869, 876, 879, 887, 892, 900, 911, 915, 921, 927, 930, 938, 941, 951, 954, 960, 966, 971, 975, 977, 985, 991, 1003, 1007, 1011, 1014, 1020, 1027, 1030, 1037, 1039, 1041, 1044, 1049, 1053, 1060, 1066, 1070, 1073, 1077, 1082, 1093, 1096, 1100, 1107, 1112, 1119, 1124, 1135, 1138, 1141, 1147, 1148, 1152, 1159, 1166, 1175, 1178, 1186, 1191, 1194, 1202, 1205, 1213, 1221, 1229, 1231, 1237, 1, 2, 4, 6, 8, 11, 9, 10, 12, 7, 12, 14, 0, 2, 7, 8, 10, 19, 8, 10, 21, 18, 20, 23, 5, 6, 8, 2, 4, 6, 0, 26, 28, 1, 26, 28, 0, 31, 32, 6, 9, 28, 8, 29, 36, 7, 36, 38, 0, 8, 28, 0, 8, 43, 28, 43, 44, 0, 5, 8, 4, 7, 48, 0, 2, 8, 2, 6, 48, 50, 53, 54, 0, 4, 9, 0, 2, 59, 0, 2, 58, 7, 8, 62, 2, 5, 6, 61, 64, 66, 0, 6, 9, 1, 4, 8, 2, 71, 72, 29, 70, 74, 0, 4, 8, 2, 4, 7, 0, 3, 8, 79, 80, 82, 0, 7, 8, 2, 7, 86, 4, 6, 87, 0, 6, 8, 2, 4, 92, 88, 90, 95, 0, 2, 4, 1, 6, 98, 2, 4, 99, 8, 100, 103, 101, 102, 104, 9, 104, 106, 1, 4, 6, 0, 9, 110, 2, 8, 110, 29, 112, 114, 0, 3, 6, 4, 52, 118, 80, 118, 121, 0, 4, 6, 1, 8, 124, 0, 2, 9, 0, 4, 128, 6, 9, 130, 4, 6, 131, 128, 133, 134, 0, 2, 5, 3, 6, 78, 93, 138, 140, 0, 9, 28, 2, 4, 9, 0, 6, 147, 145, 146, 148, 4, 8, 71, 2, 4, 8, 28, 152, 155, 4, 6, 8, 0, 8, 159, 2, 5, 158, 2, 6, 83, 160, 163, 164, 1, 2, 6, 0, 3, 4, 8, 168, 170, 3, 6, 18, 1, 18, 174, 4, 9, 176, 5, 8, 176, 177, 178, 180, 2, 82, 110, 2, 83, 110, 82, 185, 186, 2, 7, 78, 99, 158, 190, 0, 5, 6, 0, 3, 194, 6, 8, 197, 4, 8, 52, 4, 7, 8, 2, 6, 200, 1, 202, 204, 0, 201, 206, 0, 6, 10, 6, 9, 10, 0, 211, 212, 6, 9, 98, 1, 80, 216, 0, 99, 218, 4, 6, 53, 3, 52, 222, 1, 52, 224, 3, 6, 170, 2, 8, 228, 8, 128, 231, 2, 6, 8, 3, 4, 8, 1, 234, 236, 1, 6, 146, 6, 146, 241, 0, 9, 242, 0, 240, 245, 2, 5, 8, 4, 6, 248, 1, 8, 250, 0, 9, 252, 251, 252, 254, 10, 131, 194, 4, 7, 248, 0, 6, 249, 79, 260, 262, 0, 4, 7, 6, 8, 266, 3, 6, 8, 18, 269, 270, 2, 7, 8, 4, 82, 275, 5, 80, 276, 2, 8, 266, 4, 8, 281, 2, 7, 282, 0, 281, 284, 5, 8, 28, 0, 4, 29, 110, 288, 290, 0, 2, 110, 8, 110, 294, 1, 6, 236, 128, 159, 298, 4, 6, 83, 2, 4, 303, 274, 302, 305, 6, 58, 128, 8, 159, 308, 129, 308, 310, 5, 6, 170, 2, 7, 170, 4, 8, 316, 1, 314, 318, 2, 6, 9, 0, 249, 322, 0, 110, 325, 8, 324, 327, 2, 9, 170, 4, 6, 171, 1, 6, 8, 330, 333, 334, 2, 5, 266, 6, 8, 338, 2, 8, 267, 0, 341, 342, 3, 4, 6, 0, 8, 110, 110, 347, 348, 4, 8, 170, 125, 168, 352, 0, 9, 346, 1, 2, 8, 4, 194, 358, 356, 358, 361, 3, 6, 266, 1, 2, 266, 0, 2, 6, 4, 8, 368, 364, 366, 371, 7, 26, 128, 3, 6, 374, 27, 374, 376, 4, 9, 66, 0, 67, 380, 5, 380, 382, 2, 145, 346, 8, 66, 139, 7, 66, 346, 1, 8, 390, 6, 8, 80, 0, 28, 395, 8, 395, 396, 1, 10, 12, 0, 3, 26, 2, 9, 402, 0, 6, 26, 402, 404, 407, 4, 6, 129, 8, 128, 410, 4, 7, 412, 5, 410, 414, 2, 8, 158, 8, 28, 419, 4, 71, 128, 5, 410, 422, 1, 6, 10, 3, 8, 10, 0, 5, 10, 426, 428, 430, 3, 4, 86, 2, 26, 434, 87, 434, 436, 2, 7, 266, 8, 267, 440, 4, 267, 442, 4, 6, 369, 8, 368, 446, 5, 446, 448, 2, 4, 93, 3, 138, 452, 2, 8, 194, 3, 10, 456, 0, 8, 154, 6, 155, 460, 0, 7, 154, 1, 462, 464, 4, 9, 196, 4, 194, 469, 8, 468, 471, 1, 12, 98, 1, 4, 334, 1, 6, 80, 2, 8, 479, 48, 80, 481, 2, 29, 70, 10, 29, 484, 0, 4, 70, 52, 346, 489, 0, 8, 93, 2, 93, 492, 4, 7, 92, 170, 494, 497, 3, 6, 160, 146, 159, 500, 1, 2, 202, 29, 70, 504, 8, 98, 334, 4, 6, 78, 4, 6, 9, 3, 78, 512, 154, 511, 514, 0, 2, 67, 8, 171, 518, 6, 67, 520, 0, 2, 27, 26, 235, 524, 6, 8, 524, 5, 26, 524, 4, 529, 530, 3, 4, 194, 1, 456, 534, 1, 4, 270, 5, 6, 538, 0, 8, 540, 271, 538, 542, 1, 2, 110, 112, 358, 547, 4, 9, 82, 2, 6, 29, 29, 550, 552, 4, 128, 202, 4, 202, 557, 128, 557, 558, 3, 10, 234, 0, 5, 346, 4, 9, 346, 347, 564, 566, 4, 6, 358, 1, 234, 570, 0, 6, 29, 43, 154, 574, 5, 86, 368, 4, 371, 578, 6, 129, 190, 4, 9, 168, 0, 29, 584, 6, 8, 98, 2, 118, 589, 4, 8, 98, 589, 590, 592, 130, 159, 270, 1, 8, 28, 0, 4, 271, 8, 465, 600, 10, 248, 346, 0, 2, 249, 6, 8, 249, 1, 2, 608, 29, 606, 610, 6, 8, 195, 4, 194, 615, 5, 8, 128, 8, 128, 158, 4, 618, 621, 0, 147, 240, 202, 240, 624, 2, 8, 346, 1, 160, 356, 8, 81, 98, 8, 70, 633, 3, 4, 26, 159, 524, 636, 26, 58, 589, 0, 28, 159, 159, 236, 642, 5, 8, 18, 2, 8, 18, 146, 646, 649, 4, 6, 139, 4, 8, 138, 5, 652, 654, 3, 8, 110, 2, 111, 658, 0, 8, 513, 658, 660, 663, 2, 6, 73, 71, 158, 666, 0, 6, 67, 3, 4, 66, 8, 671, 672, 66, 158, 369, 6, 129, 154, 0, 4, 169, 8, 169, 680, 8, 680, 683, 168, 682, 685, 4, 8, 19, 2, 99, 688, 1, 8, 110, 0, 9, 692, 111, 692, 694, 7, 8, 128, 0, 4, 129, 346, 698, 701, 9, 194, 202, 2, 5, 202, 3, 704, 706, 2, 9, 124, 2, 346, 711, 4, 8, 70, 1, 2, 714, 29, 70, 716, 6, 26, 407, 0, 407, 720, 0, 4, 159, 7, 8, 724, 6, 159, 726, 6, 8, 159, 2, 4, 730, 1, 158, 732, 158, 732, 735, 0, 734, 737, 2, 4, 335, 2, 5, 334, 3, 740, 742, 1, 92, 744, 2, 7, 72, 9, 402, 748, 0, 2, 29, 1, 158, 752, 0, 29, 146, 4, 7, 358, 8, 10, 759, 4, 8, 66, 8, 66, 763, 266, 763, 764, 5, 6, 274, 93, 170, 768, 2, 129, 158, 0, 4, 235, 2, 9, 774, 235, 248, 776, 5, 6, 78, 1, 4, 780, 7, 780, 782, 5, 6, 202, 9, 202, 786, 0, 3, 158, 6, 202, 791, 2, 159, 790, 0, 792, 795, 0, 9, 146, 2, 346, 799, 6, 8, 10, 4, 8, 92, 4, 6, 805, 0, 3, 806, 274, 805, 808, 29, 70, 154, 6, 8, 81, 1, 6, 814, 0, 7, 816, 815, 816, 818, 4, 8, 269, 2, 266, 823, 1, 268, 824, 0, 8, 81, 71, 146, 828, 0, 2, 71, 4, 6, 832, 70, 154, 835, 5, 6, 128, 4, 8, 839, 4, 8, 838, 838, 840, 843, 0, 4, 202, 2, 6, 203, 1, 4, 848, 5, 846, 850, 0, 9, 18, 59, 110, 854, 0, 4, 275, 4, 93, 274, 5, 858, 860, 1, 4, 66, 0, 7, 66, 129, 864, 866, 6, 8, 791, 0, 4, 870, 2, 4, 159, 790, 873, 874, 1, 78, 194, 2, 8, 99, 4, 7, 98, 1, 86, 98, 880, 882, 885, 8, 111, 170, 6, 111, 170, 112, 888, 891, 3, 8, 512, 0, 4, 894, 0, 7, 894, 512, 897, 898, 2, 4, 147, 6, 8, 903, 7, 146, 904, 0, 8, 903, 904, 906, 909, 2, 8, 98, 2, 268, 913, 1, 8, 18, 4, 194, 916, 1, 194, 918, 2, 4, 155, 0, 7, 922, 8, 465, 924, 2, 4, 334, 0, 95, 928, 3, 6, 72, 0, 9, 932, 4, 6, 935, 128, 932, 937, 1, 12, 740, 1, 2, 170, 5, 170, 942, 6, 8, 944, 7, 942, 946, 945, 946, 948, 2, 93, 158, 0, 95, 952, 6, 8, 93, 8, 92, 98, 0, 956, 959, 4, 8, 195, 2, 9, 194, 11, 962, 964, 4, 270, 539, 92, 538, 969, 0, 7, 146, 1, 92, 972, 1, 334, 928, 6, 8, 589, 3, 4, 978, 0, 4, 978, 588, 980, 983, 1, 4, 274, 4, 8, 159, 158, 986, 989, 2, 8, 155, 4, 6, 992, 1, 154, 994, 4, 992, 997, 0, 155, 996, 6, 998, 1001, 0, 99, 102, 6, 8, 1005, 2, 6, 266, 168, 268, 1009, 0, 6, 155, 154, 589, 1012, 1, 8, 158, 3, 4, 1016, 128, 159, 1018, 1, 6, 154, 2, 4, 1023, 8, 465, 1024, 4, 7, 18, 6, 589, 1028, 6, 124, 237, 4, 6, 82, 236, 1032, 1035, 8, 110, 368, 87, 236, 706, 3, 4, 70, 2, 29, 1042, 2, 6, 138, 28, 248, 1047, 3, 6, 48, 71, 146, 1050, 7, 8, 98, 0, 6, 99, 8, 99, 1056, 9, 1054, 1058, 4, 52, 81, 1, 4, 234, 0, 1063, 1064, 0, 3, 154, 66, 93, 1068, 99, 588, 740, 2, 8, 111, 49, 1050, 1074, 3, 358, 570, 0, 9, 570, 571, 1078, 1080, 2, 8, 124, 7, 124, 1084, 4, 8, 1086, 4, 1084, 1089, 8, 1089, 1090, 159, 248, 346, 0, 235, 1094, 0, 358, 589, 6, 589, 1098, 0, 8, 478, 2, 4, 81, 478, 1102, 1105, 4, 8, 81, 0, 3, 80, 334, 1109, 1110, 4, 71, 138, 5, 6, 1114, 235, 1114, 1116, 1, 6, 26, 2, 6, 26, 128, 1120, 1123, 3, 4, 52, 6, 52, 1127, 5, 8, 52, 2, 6, 53, 1129, 1130, 1132, 1, 4, 698, 128, 155, 1136, 87, 236, 646, 3, 6, 52, 5, 6, 52, 248, 1142, 1145, 10, 29, 70, 70, 147, 358, 7, 70, 1150, 2, 4, 86, 4, 18, 1155, 8, 87, 1156, 0, 8, 237, 6, 52, 236, 6, 236, 1161, 1160, 1163, 1164, 0, 3, 146, 6, 8, 1168, 6, 1168, 1171, 146, 1170, 1173, 0, 29, 274, 9, 334, 1176, 7, 8, 28, 0, 29, 1180, 1, 6, 1180, 9, 1182, 1184, 2, 8, 170, 1, 314, 1188, 0, 8, 87, 6, 86, 1193, 2, 6, 158, 0, 154, 1197, 1, 2, 158, 155, 1198, 1200, 19, 234, 266, 4, 8, 235, 0, 6, 234, 3, 4, 1208, 6, 1206, 1211, 1, 6, 922, 8, 154, 1215, 9, 1214, 1216, 155, 1216, 1218, 2, 9, 368, 4, 7, 1222, 4, 9, 368, 6, 1224, 1227, 3, 28, 288, 4, 86, 237, 2, 4, 1233, 86, 1233, 1234 } }; + inline static const std::vector nodes10{ { 4, 222, 10, 17, 21, 25, 29, 33, 35, 17, 29, 21, 37, 10, 46, 56, 66, 70, 74, 78, 82, 84, 88, 90, 10, 100, 108, 112, 116, 118, 122, 126, 130, 140, 144, 10, 153, 159, 163, 167, 171, 175, 177, 181, 185, 189, 10, 194, 198, 198, 204, 206, 208, 212, 204, 214, 206, 2, 224, 230, 10, 240, 250, 258, 264, 276, 280, 286, 294, 296, 300, 10, 308, 316, 320, 326, 330, 334, 336, 340, 344, 350, 4, 352, 360, 362, 364, 10, 374, 384, 390, 392, 398, 404, 414, 398, 392, 422, 10, 431, 439, 443, 453, 463, 473, 481, 489, 499, 443, 2, 506, 510, 10, 514, 518, 526, 530, 536, 538, 542, 546, 550, 552, 4, 557, 559, 561, 563, 9, 571, 579, 587, 593, 599, 607, 613, 617, 623, 2, 626, 632, 10, 639, 643, 639, 643, 649, 651, 653, 655, 655, 649, 10, 663, 673, 679, 685, 689, 693, 697, 701, 705, 709, 10, 718, 728, 738, 744, 754, 758, 764, 768, 776, 780, 2, 783, 785, 10, 792, 800, 804, 812, 804, 816, 824, 824, 832, 840, 10, 846, 852, 856, 860, 864, 868, 872, 876, 878, 882, 10, 889, 893, 899, 903, 909, 913, 917, 923, 929, 935, 10, 937, 941, 943, 945, 949, 953, 955, 957, 961, 965, 10, 970, 980, 988, 996, 1000, 1006, 1014, 1024, 1032, 1038, 10, 1044, 1046, 1050, 1052, 1056, 1060, 1062, 1064, 1068, 1062, 10, 1070, 1074, 1080, 1070, 1082, 1084, 1088, 1096, 1102, 1108, 10, 1111, 1115, 1119, 1123, 1129, 1131, 1135, 1141, 1145, 1149, 10, 1156, 1164, 1168, 1170, 1174, 1178, 1180, 1182, 1184, 1186, 2, 1191, 1193, 10, 1197, 1203, 1205, 1209, 1211, 1203, 1215, 1217, 1221, 1223, 10, 1228, 1236, 1240, 1244, 1252, 1256, 1264, 1268, 1274, 1278, 10, 1283, 1287, 1293, 1297, 1305, 1311, 1319, 1323, 1325, 1327, 8, 1332, 1338, 1344, 1350, 1358, 1364, 1368, 1374, 10, 1378, 1380, 1384, 1388, 1394, 1396, 1402, 1406, 1410, 1412, 10, 1418, 1422, 1428, 1432, 1436, 1440, 1442, 1444, 1448, 1452, 2, 1458, 1462, 10, 1469, 1473, 1477, 1483, 1487, 1489, 1493, 1487, 1497, 1501, 7, 875, 1503, 1507, 1509, 845, 1511, 1513, 8, 1516, 1524, 1530, 1538, 1542, 1550, 1558, 1566, 10, 1573, 1577, 1581, 1589, 1593, 1597, 1601, 1607, 1611, 1615, 10, 1620, 1630, 1636, 1642, 1648, 1654, 1658, 1664, 1668, 1674, 10, 1681, 1685, 1691, 1697, 1701, 1707, 1711, 1713, 1717, 1719, 10, 1724, 1728, 1732, 1734, 1738, 1744, 1750, 1752, 1760, 1764, 2, 1768, 1772, 10, 1778, 1782, 1786, 1790, 1798, 1806, 1810, 1812, 1816, 1822, 6, 1825, 1829, 1833, 1837, 1841, 1843, 10, 1845, 1849, 1853, 1859, 1863, 1865, 1869, 1875, 1879, 1885, 10, 1888, 1892, 1898, 1904, 1908, 1912, 1920, 1926, 1934, 1938, 10, 1944, 1948, 1956, 1962, 1962, 1972, 1976, 1986, 1990, 1994, 10, 2002, 2006, 2010, 2016, 2020, 2024, 2016, 2028, 2032, 2036, 10, 2042, 2044, 2046, 2048, 2050, 2052, 2054, 2056, 2058, 2060, 9, 2063, 2063, 2067, 2069, 2071, 2073, 2069, 2077, 2071, 2, 2079, 2081, 10, 2085, 809, 809, 2089, 2085, 789, 2093, 797, 797, 2097, 10, 2100, 2104, 2108, 2114, 2120, 2124, 2130, 2134, 2136, 2140, 10, 2143, 2145, 2147, 2151, 2153, 2155, 2159, 2161, 2165, 2167, 10, 2172, 2176, 2172, 2182, 2186, 2190, 2190, 2194, 2186, 2200, 10, 2207, 2211, 2217, 2225, 2227, 2231, 2233, 2237, 2239, 2241, 10, 2245, 2249, 2251, 2253, 2255, 2259, 2263, 2267, 2269, 2271, 10, 2277, 2281, 2283, 2285, 2287, 2289, 2293, 2297, 2301, 2303, 10, 2309, 2315, 2321, 2329, 2331, 2335, 2331, 2337, 2343, 2345, 1, 0, 10, 2351, 2355, 2359, 2363, 2371, 2375, 2379, 2381, 2383, 2387, 10, 2391, 2395, 2399, 2403, 2407, 2411, 2413, 2417, 2419, 2425, 10, 2431, 2435, 2441, 2445, 2447, 2447, 2445, 2453, 2459, 2465, 10, 2468, 2474, 2478, 2482, 2484, 2486, 2490, 2492, 2494, 2498, 7, 2503, 2505, 2507, 2511, 2513, 2515, 2521, 10, 2527, 2533, 2535, 2541, 2543, 2545, 2549, 2551, 2555, 2557, 10, 2560, 2564, 2566, 2568, 2570, 2578, 2584, 2590, 2594, 2596, 10, 2601, 2603, 2607, 2609, 2611, 2603, 2615, 2617, 2619, 2617, 4, 2621, 2623, 1535, 1547, 5, 2626, 2632, 2636, 2640, 2632, 10, 2644, 2650, 2656, 2660, 2662, 2666, 2670, 2672, 2674, 2678, 10, 2683, 2687, 2693, 2697, 2701, 2707, 2711, 2717, 2721, 2729, 10, 2732, 2738, 2746, 2752, 2758, 2764, 2772, 2780, 2786, 2790, 10, 2794, 2800, 2806, 2808, 2810, 2814, 2816, 2822, 2828, 2830, 8, 2832, 2834, 2832, 2834, 2834, 2832, 2832, 2834, 10, 2837, 2841, 2845, 2847, 2849, 2851, 2855, 2857, 2859, 2861, 10, 2867, 2877, 2885, 2889, 2893, 2897, 2905, 2911, 2915, 2919, 10, 2923, 2931, 2937, 2941, 2947, 2953, 2959, 2963, 2969, 2971, 10, 2972, 2978, 2980, 2982, 2972, 2986, 2988, 2994, 2998, 2982, 10, 3006, 3014, 3018, 3022, 3026, 3030, 3034, 3038, 3042, 3046, 5, 3049, 3051, 3053, 3057, 3059, 1, 9, 10, 3065, 3069, 3077, 3069, 3081, 3087, 3095, 3099, 3081, 3103, 10, 3104, 3112, 3114, 3120, 3122, 3128, 3136, 3138, 3142, 3144, 1, 657, 10, 3146, 3154, 3160, 3164, 3172, 3180, 3186, 3190, 3192, 3194, 10, 3196, 3200, 3202, 3204, 3206, 3214, 3218, 3220, 3222, 3228, 6, 3233, 3235, 3237, 3239, 3241, 3243, 10, 3246, 3248, 3250, 3252, 3254, 3256, 3260, 3262, 3268, 3270, 10, 3273, 3277, 487, 3281, 3283, 3285, 3287, 3291, 3293, 441, 2, 3294, 3294, 10, 3296, 3302, 3306, 3310, 3314, 3296, 3318, 3324, 3330, 3332, 10, 3335, 3339, 3341, 3345, 3349, 3351, 3355, 3359, 3361, 3365, 10, 3366, 3368, 3370, 3372, 3376, 3378, 3366, 3378, 3380, 3370, 10, 3386, 3392, 3396, 3400, 3410, 3414, 3418, 3426, 3432, 3436, 10, 3440, 3442, 3446, 3452, 3454, 3458, 3462, 3464, 3468, 3474, 1, 191, 6, 3477, 3479, 3477, 3485, 3477, 3491, 10, 3493, 3495, 3497, 3499, 3503, 3507, 3511, 3513, 3515, 3519, 10, 3524, 3530, 3534, 3540, 3548, 3556, 3562, 3570, 3574, 3582, 10, 3584, 3586, 3588, 3590, 3584, 3592, 3594, 3596, 3598, 3600, 10, 3604, 3608, 3612, 3616, 3618, 3622, 3628, 3634, 3640, 3646, 10, 3653, 3653, 3657, 3663, 3665, 3667, 3669, 3671, 3673, 3679, 4, 1997, 1425, 2133, 2041, 10, 3680, 3682, 3684, 3688, 3690, 3694, 3696, 3700, 3702, 3704, 10, 3706, 3712, 3716, 3720, 3724, 3728, 3706, 3734, 3736, 3740, 10, 3744, 3748, 3752, 3756, 3760, 3764, 3766, 3772, 3776, 3782, 10, 3788, 3792, 3796, 3800, 3802, 3806, 3792, 3808, 3814, 3800, 10, 3820, 3824, 3830, 3834, 3836, 3838, 3844, 3848, 3852, 3860, 10, 3868, 3872, 3880, 3884, 3892, 3900, 3908, 3916, 3922, 3926, 10, 3929, 3933, 3939, 3941, 3943, 3947, 3949, 3951, 3953, 3957, 10, 3963, 3973, 3981, 3989, 3995, 4001, 4009, 4013, 4019, 4025, 10, 4027, 4033, 4027, 4039, 4047, 4051, 4055, 4061, 4065, 4067, 10, 4071, 4075, 4077, 4081, 4083, 4087, 4091, 4087, 4077, 4097, 3, 4099, 4101, 4103, 2, 4105, 4107, 10, 4108, 4110, 4112, 4116, 4120, 4124, 4130, 4136, 4142, 4148, 10, 4153, 4157, 4161, 4165, 4169, 4173, 4177, 4179, 4181, 4187, 10, 4188, 4192, 4194, 4196, 4198, 4200, 4204, 4206, 4198, 4200, 1, 4210, 10, 4214, 4216, 4222, 4226, 4230, 4234, 4238, 4242, 4246, 4252, 6, 4257, 4259, 4261, 4265, 4267, 4269, 10, 4270, 4276, 4280, 4284, 4288, 4294, 4294, 4270, 4288, 4300, 10, 4302, 4304, 4306, 4308, 4310, 4316, 4320, 4324, 4326, 4330, 10, 4332, 4336, 4340, 4346, 4350, 4356, 4360, 4364, 4368, 4374, 10, 4382, 4388, 4394, 4400, 4404, 4408, 4414, 4416, 4418, 4422, 10, 4433, 4437, 4447, 4453, 4457, 4459, 4465, 4467, 4471, 4475, 5, 4480, 4486, 4490, 4496, 4498, 10, 4501, 4505, 4507, 4509, 4511, 4513, 4517, 4519, 4525, 4529, 2, 4530, 4532, 10, 4537, 4539, 4541, 4543, 4545, 4549, 4551, 4555, 4559, 4563, 10, 4568, 4574, 4578, 4582, 4588, 4594, 4602, 4608, 4588, 4594, 10, 4610, 4612, 4614, 4616, 4618, 4622, 4624, 4626, 4628, 4630, 10, 4635, 4637, 4639, 4641, 4643, 4645, 4649, 4651, 4653, 4655, 10, 4660, 4664, 4666, 4672, 4678, 4682, 4688, 4692, 4696, 4700, 10, 4705, 4707, 4709, 4711, 4715, 4719, 4723, 4727, 4729, 4731, 10, 4735, 4737, 4739, 4741, 4743, 4743, 4739, 4737, 4745, 4745, 1, 927, 10, 4751, 4755, 4757, 4763, 4765, 4769, 4773, 4775, 4773, 4775, 10, 4777, 4781, 4785, 4787, 4789, 4793, 4795, 4797, 4799, 4803, 1, 19, 10, 4808, 4812, 4816, 4824, 4830, 4834, 4838, 4842, 4850, 4858, 4, 4860, 4862, 4864, 4866, 10, 4870, 4876, 4880, 4886, 4888, 4894, 4896, 4902, 4908, 4914, 10, 4921, 4927, 4931, 4935, 4937, 4927, 4943, 4949, 4953, 4959, 7, 4963, 4967, 4971, 4973, 4967, 4975, 4979, 2, 4982, 4984, 10, 4990, 4994, 4996, 5002, 5004, 5010, 5014, 5018, 5024, 5028, 10, 5031, 5039, 5043, 5051, 5055, 5059, 5061, 5063, 5067, 5071, 10, 5077, 5081, 5085, 5089, 5085, 5089, 5093, 5097, 5101, 5105, 10, 5110, 5114, 5118, 5110, 5122, 5128, 5132, 5136, 5138, 5144, 10, 5149, 5155, 5157, 5161, 5163, 5167, 5171, 5173, 5177, 5181, 10, 5186, 5192, 5198, 5186, 5204, 5208, 5212, 5218, 5224, 5204, 10, 5227, 5229, 5231, 5233, 5235, 5237, 5239, 5241, 5243, 5239, 10, 5249, 5255, 5259, 5267, 5271, 5277, 5283, 5287, 5297, 5305, 10, 5312, 5314, 5318, 5322, 5330, 5336, 5340, 5344, 5348, 5352, 3, 5358, 5362, 5366, 10, 5373, 5381, 5387, 5391, 5393, 5397, 5397, 5407, 5417, 5421, 10, 5423, 5427, 5429, 5435, 5441, 5445, 5449, 5455, 5457, 5461, 10, 5463, 5467, 5473, 5479, 5485, 5489, 5493, 5497, 5505, 5509, 10, 5515, 5517, 5521, 5521, 5527, 5531, 5535, 5539, 5545, 5549, 10, 5550, 5556, 5560, 5564, 5564, 5560, 5566, 5570, 5572, 5576, 10, 5580, 5590, 5594, 5600, 5604, 5608, 5612, 5620, 5626, 5630, 5, 5635, 5639, 5635, 5639, 5641, 10, 5647, 5653, 5657, 5661, 5663, 5669, 5675, 5681, 5689, 5695, 8, 5696, 5698, 5696, 5702, 5706, 5698, 5710, 5714, 10, 5718, 5722, 5724, 5728, 5734, 5740, 5746, 5752, 5758, 5764, 10, 5770, 5774, 5778, 5784, 5788, 5790, 5794, 5798, 5800, 5806, 10, 5809, 5811, 5811, 5809, 5815, 5819, 5823, 5825, 5829, 5815, 10, 5833, 5835, 5835, 5839, 5841, 1207, 5843, 5845, 5847, 5845, 10, 5849, 5851, 5853, 5855, 5857, 5859, 5863, 5865, 5867, 5869, 10, 5875, 5883, 5887, 5893, 5897, 5901, 5907, 5913, 5915, 5917, 10, 5919, 5923, 5929, 5935, 5941, 5947, 5955, 5961, 5967, 5973, 10, 5979, 5985, 5991, 5997, 6003, 6009, 6015, 6021, 6025, 6025, 10, 6029, 6033, 6033, 6037, 6037, 5977, 829, 829, 6029, 821, 10, 6039, 6045, 6047, 6051, 6057, 6063, 6065, 6069, 6071, 6073, 9, 6078, 6084, 6086, 6088, 6092, 6094, 6096, 6098, 6102, 10, 6106, 6110, 6112, 6118, 6122, 6126, 6134, 6138, 6142, 6144, 7, 6151, 6155, 6157, 6157, 6161, 6163, 6155, 10, 6166, 6170, 6172, 6178, 6180, 6184, 6186, 6192, 6194, 6196, 10, 6207, 6211, 6217, 6221, 6231, 6237, 6241, 6249, 6253, 6259, 2, 1387, 4837, 10, + 6265, 6269, 6273, 6277, 6283, 6285, 6287, 6291, 6293, 6295, 10, 6296, 6298, 6300, 6304, 6306, 6308, 6310, 6296, 6312, 6314, 10, 6319, 6323, 6327, 6331, 6335, 6339, 6341, 6343, 6349, 6353, 2, 6357, 6361, 10, 6362, 6366, 6370, 6376, 6380, 6386, 6390, 6394, 6398, 6402, 10, 6408, 6414, 6420, 6426, 6432, 6438, 6442, 6450, 6456, 6460, 10, 6462, 6464, 6462, 6468, 6472, 6474, 6462, 6478, 6462, 6482, 10, 6489, 6493, 6495, 6497, 6499, 6503, 6505, 6507, 6499, 6509, 10, 6515, 6517, 6521, 6523, 6525, 6529, 6531, 6537, 6539, 6545, 1, 6550, 10, 6555, 6559, 6563, 6567, 6573, 6577, 6581, 6583, 6585, 6589, 10, 6592, 6594, 6602, 6608, 6614, 6618, 6620, 6626, 6632, 6636, 10, 6640, 6646, 6652, 6658, 6662, 6664, 6668, 6672, 6676, 6680, 10, 6683, 6689, 6691, 6697, 6703, 6709, 6715, 6721, 6723, 6725, 2, 6728, 6728, 10, 6731, 6737, 6741, 6749, 6755, 6759, 6763, 6769, 6775, 6781, 10, 6784, 6788, 6794, 6798, 6784, 6802, 6804, 6810, 6812, 6816, 10, 6823, 6831, 6837, 6845, 6851, 6857, 6865, 6869, 6879, 6885, 10, 6886, 6892, 6898, 6902, 6906, 6910, 6912, 6914, 6916, 6920, 6, 6925, 6929, 6933, 6935, 6937, 6941, 4, 6945, 6947, 6949, 6953, 4, 6954, 6956, 6958, 6960, 10, 6964, 6968, 6970, 6974, 6980, 6984, 6988, 6990, 6994, 6998, 10, 7003, 7009, 7011, 7015, 7023, 7027, 7029, 7031, 7035, 7039, 10, 7046, 7054, 7060, 7064, 7068, 7072, 7076, 7082, 7088, 7096, 10, 7103, 7107, 7115, 7119, 7123, 7127, 7131, 7133, 7135, 7139, 10, 7142, 7144, 7148, 7154, 7158, 7162, 7166, 7172, 7176, 7180, 10, 7186, 7190, 7196, 7200, 7206, 7208, 7214, 7218, 7222, 7226, 10, 7229, 7231, 7233, 7235, 7237, 7239, 7241, 7243, 7245, 7247, 10, 7248, 7250, 7252, 7254, 7250, 7254, 7252, 5760, 5760, 7256, 10, 7262, 7268, 7272, 7276, 7280, 7284, 7286, 7288, 7290, 7292, 5, 7295, 7297, 7299, 7301, 7303, 9, 7305, 7307, 7311, 7317, 7319, 7323, 7329, 7333, 7339, 10, 7343, 7349, 7349, 7355, 7359, 7363, 7367, 7371, 7349, 7375, 7, 7381, 7383, 7387, 7391, 7393, 7399, 7403, 10, 7405, 7407, 7405, 7411, 7413, 7415, 7417, 7419, 7413, 7421, 10, 7425, 7431, 7437, 7441, 7443, 7447, 7453, 7459, 7463, 7467, 1, 2, 4, 7, 8, 10, 6, 8, 11, 9, 12, 14, 6, 8, 10, 6, 12, 19, 8, 10, 13, 6, 12, 23, 6, 9, 10, 7, 14, 26, 7, 8, 26, 11, 26, 30, 11, 12, 26, 10, 14, 19, 0, 4, 7, 2, 4, 6, 9, 38, 40, 8, 38, 40, 8, 42, 45, 0, 3, 4, 0, 2, 6, 9, 48, 50, 8, 48, 50, 8, 52, 55, 0, 5, 6, 3, 4, 6, 9, 58, 60, 8, 58, 60, 8, 62, 65, 48, 50, 53, 8, 52, 69, 58, 60, 63, 8, 62, 73, 8, 49, 50, 48, 55, 76, 8, 59, 60, 58, 65, 80, 51, 52, 76, 9, 48, 76, 51, 76, 86, 61, 62, 80, 2, 4, 8, 3, 6, 8, 0, 93, 94, 0, 92, 94, 92, 96, 99, 5, 6, 8, 0, 40, 103, 0, 41, 102, 1, 104, 106, 0, 92, 95, 1, 96, 110, 1, 40, 102, 41, 104, 114, 103, 106, 114, 1, 40, 106, 103, 106, 120, 1, 92, 94, 95, 96, 124, 1, 92, 96, 95, 96, 128, 2, 5, 8, 2, 6, 9, 0, 132, 135, 0, 133, 134, 1, 136, 138, 1, 132, 134, 135, 138, 142, 2, 5, 6, 3, 8, 146, 2, 8, 146, 2, 148, 151, 7, 8, 40, 6, 8, 40, 6, 154, 157, 6, 8, 41, 9, 154, 160, 6, 9, 40, 41, 154, 164, 6, 9, 154, 41, 154, 168, 2, 9, 146, 147, 148, 172, 7, 160, 164, 2, 8, 147, 3, 172, 178, 8, 146, 149, 2, 148, 183, 8, 40, 155, 6, 154, 187, 1, 8, 40, 8, 40, 191, 0, 190, 193, 0, 8, 40, 0, 190, 197, 0, 8, 41, 0, 9, 40, 1, 200, 202, 9, 190, 200, 41, 190, 202, 0, 9, 190, 41, 190, 210, 40, 197, 200, 0, 2, 8, 0, 5, 8, 2, 6, 218, 4, 7, 218, 217, 220, 222, 3, 6, 216, 4, 6, 217, 218, 227, 228, 0, 7, 8, 2, 4, 232, 5, 6, 232, 2, 216, 236, 234, 236, 239, 1, 2, 8, 5, 8, 242, 0, 6, 245, 8, 41, 246, 242, 245, 248, 0, 9, 92, 7, 8, 252, 6, 41, 254, 1, 252, 256, 2, 4, 255, 6, 254, 261, 1, 252, 262, 0, 2, 4, 0, 9, 266, 7, 8, 268, 2, 4, 271, 6, 270, 273, 1, 268, 274, 6, 41, 270, 1, 268, 278, 0, 6, 9, 8, 41, 282, 1, 252, 284, 0, 9, 234, 2, 4, 233, 6, 232, 291, 1, 288, 292, 1, 252, 292, 6, 41, 232, 1, 252, 298, 0, 4, 9, 7, 242, 302, 2, 4, 305, 6, 304, 307, 1, 4, 8, 0, 2, 9, 6, 311, 312, 41, 310, 314, 7, 310, 312, 5, 60, 318, 1, 8, 60, 5, 312, 322, 7, 60, 324, 7, 312, 322, 5, 60, 328, 6, 243, 302, 41, 242, 332, 41, 312, 322, 4, 6, 61, 312, 322, 339, 2, 4, 319, 6, 318, 343, 4, 6, 313, 1, 8, 346, 41, 312, 348, 41, 310, 312, 0, 3, 8, 0, 4, 8, 2, 4, 7, 354, 357, 358, 217, 218, 358, 41, 242, 302, 2, 6, 49, 3, 8, 48, 0, 4, 369, 6, 8, 370, 366, 368, 373, 0, 2, 5, 4, 6, 377, 5, 8, 376, 6, 8, 266, 378, 380, 383, 0, 2, 378, 6, 8, 386, 378, 380, 389, 366, 368, 383, 0, 6, 11, 1, 8, 10, 383, 394, 396, 0, 4, 366, 6, 8, 400, 366, 368, 403, 0, 6, 359, 7, 8, 358, 2, 4, 409, 0, 8, 410, 406, 408, 413, 2, 7, 48, 1, 48, 102, 2, 8, 48, 416, 418, 421, 1, 6, 266, 9, 10, 424, 10, 424, 427, 8, 426, 429, 1, 2, 60, 8, 338, 432, 9, 338, 432, 8, 435, 436, 8, 10, 424, 8, 426, 441, 2, 4, 11, 1, 6, 444, 9, 10, 446, 8, 10, 446, 8, 448, 451, 0, 5, 10, 2, 6, 455, 9, 10, 456, 8, 10, 456, 8, 458, 461, 0, 3, 10, 4, 6, 465, 9, 10, 466, 8, 10, 466, 8, 468, 471, 4, 6, 49, 9, 10, 474, 8, 10, 474, 8, 476, 479, 2, 6, 377, 9, 10, 482, 8, 10, 482, 8, 484, 487, 1, 2, 6, 4, 6, 490, 9, 10, 492, 8, 10, 492, 8, 494, 497, 1, 4, 6, 0, 9, 500, 2, 8, 500, 41, 502, 504, 3, 8, 40, 200, 500, 509, 4, 312, 491, 41, 490, 512, 5, 312, 490, 4, 41, 516, 1, 6, 358, 0, 9, 358, 3, 520, 522, 5, 358, 524, 5, 520, 522, 3, 358, 528, 2, 9, 376, 4, 6, 376, 60, 532, 535, 41, 520, 522, 4, 8, 48, 60, 416, 541, 3, 6, 48, 358, 541, 544, 2, 6, 517, 4, 516, 549, 41, 378, 532, 0, 4, 6, 1, 8, 554, 4, 8, 39, 6, 8, 59, 6, 8, 310, 4, 7, 266, 2, 9, 564, 6, 8, 564, 267, 566, 568, 1, 2, 38, 2, 8, 38, 6, 8, 38, 572, 575, 576, 2, 9, 266, 4, 6, 267, 6, 9, 266, 580, 582, 585, 2, 8, 267, 7, 8, 266, 564, 588, 591, 4, 7, 580, 6, 8, 580, 267, 594, 596, 1, 4, 312, 5, 6, 312, 6, 9, 312, 600, 602, 605, 3, 8, 38, 7, 8, 38, 572, 608, 611, 6, 8, 267, 582, 588, 615, 4, 6, 312, 6, 8, 312, 600, 619, 620, 2, 7, 356, 267, 282, 624, 0, 6, 8, 3, 6, 356, 376, 629, 630, 2, 4, 9, 0, 6, 635, 203, 634, 636, 0, 7, 634, 6, 203, 640, 1, 6, 634, 0, 8, 644, 41, 644, 646, 8, 191, 644, 0, 203, 644, 41, 196, 644, 1, 6, 8, 0, 4, 656, 3, 4, 8, 40, 659, 660, 2, 6, 8, 4, 9, 664, 1, 6, 666, 4, 664, 669, 8, 666, 671, 0, 6, 358, 4, 8, 675, 359, 634, 676, 0, 8, 147, 4, 6, 681, 132, 147, 682, 4, 8, 283, 359, 634, 686, 4, 8, 555, 359, 634, 690, 6, 9, 310, 132, 147, 694, 6, 9, 500, 132, 147, 698, 7, 8, 500, 359, 634, 702, 7, 8, 310, 359, 634, 706, 6, 8, 313, 4, 7, 710, 0, 5, 712, 312, 712, 714, 9, 710, 716, 0, 3, 134, 5, 8, 720, 1, 4, 722, 134, 722, 724, 7, 720, 726, 6, 8, 39, 0, 3, 730, 6, 9, 38, 2, 730, 734, 7, 732, 736, 2, 9, 614, 7, 218, 266, 3, 740, 742, 0, 3, 6, 1, 8, 746, 5, 6, 748, 9, 266, 746, 7, 750, 752, 4, 7, 748, 5, 752, 756, 0, 5, 590, 9, 590, 746, 1, 760, 762, 1, 4, 590, 5, 762, 766, 6, 8, 565, 0, 3, 770, 9, 266, 772, 7, 770, 774, 9, 574, 732, 7, 730, 778, 8, 48, 490, 8, 10, 746, 6, 41, 358, 1, 8, 786, 0, 8, 786, 0, 788, 791, 3, 146, 358, 1, 8, 794, 0, 8, 794, 0, 796, 799, 8, 786, 789, 0, 788, 803, 4, 41, 146, 1, 8, 806, 8, 806, 809, 0, 808, 811, 0, 8, 806, 0, 808, 815, 1, 48, 376, 6, 8, 819, 6, 8, 818, 818, 820, 823, 5, 10, 48, 6, 8, 827, 6, 8, 826, 826, 828, 831, 5, 10, 464, 6, 8, 835, 6, 8, 834, 834, 836, 839, 2, 355, 500, 2, 354, 500, 354, 842, 845, 8, 313, 500, 9, 312, 848, 501, 848, 850, 3, 354, 842, 501, 842, 854, 3, 354, 500, 501, 842, 858, 9, 312, 500, 501, 848, 862, 2, 354, 501, 3, 842, 866, 8, 312, 501, 9, 848, 870, 8, 312, 500, 312, 848, 875, 2, 845, 858, 354, 500, 859, 2, 858, 881, 4, 7, 10, 5, 10, 746, 8, 884, 886, 7, 8, 132, 48, 490, 890, 5, 6, 490, 7, 8, 894, 48, 490, 896, 2, 8, 41, 48, 490, 900, 4, 6, 9, 2, 8, 905, 48, 490, 906, 5, 8, 358, 48, 490, 910, 7, 8, 146, 48, 490, 914, 4, 7, 490, 5, 8, 918, 48, 490, 920, 2, 8, 376, 4, 6, 8, 51, 924, 926, 6, 8, 48, 1, 2, 930, 905, 930, 932, 8, 10, 656, 2, 6, 48, 1, 8, 938, 4, 132, 656, 2, 656, 660, 0, 6, 10, 1, 8, 946, 4, 6, 50, 1, 8, 950, 8, 634, 641, 1, 8, 534, 0, 6, 634, 1, 8, 958, 1, 2, 660, 6, 8, 962, 0, 2, 474, 8, 48, 475, 421, 966, 968, 2, 7, 8, 0, 3, 972, 4, 7, 974, 2, 973, 976, 357, 974, 978, 9, 38, 664, 0, 8, 983, 38, 665, 984, 39, 982, 986, 9, 38, 50, 3, 8, 990, 6, 39, 992, 7, 990, 994, 39, 50, 992, 7, 990, 998, 7, 8, 990, 2, 39, 1002, 3, 990, 1004, 8, 747, 904, 2, 4, 1008, 0, 8, 1011, 9, 1008, 1012, 4, 6, 747, 8, 747, 1016, 2, 4, 1018, 0, 8, 1021, 9, 1018, 1022, 6, 9, 48, 0, 2, 1026, 4, 7, 8, 421, 1028, 1030, 217, 218, 904, 0, 2, 1034, 7, 1034, 1036, 0, 3, 146, 4, 9, 146, 7, 1040, 1042, 7, 26, 394, 6, 9, 634, 7, 636, 1048, 9, 12, 394, 7, 8, 634, 9, 636, 1054, 0, 7, 10, 9, 394, 1058, 9, 636, 640, 9, 14, 1058, 6, 8, 635, 9, 640, 1066, 60, 376, 383, 0, 5, 490, 48, 629, 1072, 9, 358, 656, 2, 4, 1077, 0, 1076, 1079, 376, 544, 629, 282, 445, 1058, 2, 60, 267, 0, 383, 1086, 0, 8, 359, 6, 358, 1091, 2, 4, 1093, 0, 1092, 1095, 9, 358, 520, 2, 4, 1099, 0, 1098, 1101, 6, 358, 629, 2, 4, 1105, 0, 1104, 1107, 216, 313, 500, 0, 2, 60, 60, 216, 1113, 0, 2, 500, 216, 500, 1117, 0, 3, 432, 8, 432, 1120, 0, 3, 500, 1, 2, 1124, 8, 1124, 1126, 3, 216, 520, 3, 6, 38, 1, 216, 1132, 4, 7, 216, 3, 6, 1136, 1, 216, 1138, 1, 6, 1136, 3, 216, 1142, 1, 4, 146, 3, 216, 1146, 3, 8, 60, 0, 61, 1150, 1, 2, 1152, 9, 1152, 1154, 0, 3, 132, 0, 7, 132, 1, 2, 1160, 9, 1158, 1162, 3, 4, 58, 312, 354, 1167, 312, 354, 631, 1, 6, 92, 242, 312, 1173, 0, 9, 242, 242, 1173, 1176, 242, 312, 433, 242, 312, 1147, 242, 1147, 1176, 312, 354, 1125, 0, 2, 7, 8, 10, 1189, 1, 660, 664, 0, 283, 634, 1, 636, 1194, 7, 628, 634, 6, 628, 635, 1, 1198, 1200, 635, 644, 1198, 7, 628, 644, 635, 644, 1206, 1, 636, 1198, 1, 6, 1198, 635, 1198, 1212, 1, 640, 1200, 8, 634, 657, 1, 636, 1218, 635, 644, 1218, 0, 6, 148, 0, 7, 148, 6, 1225, 1226, 2, 6, 660, 1, 8, 1230, 0, 9, 1232, 1231, 1232, 1234, 0, 9, 1230, 1231, 1232, 1238, 1, 6, 148, 0, 1225, 1242, 5, 8, 60, 0, 6, 1246, 1, 6, 1246, 0, 1249, 1250, 1, 8, 634, 282, 645, 1254, 2, 4, 282, 1, 6, 1258, 1, 8, 1258, 282, 1261, 1262, 0, 7, 40, 202, 232, 1267, 0, 7, 290, 0, 9, 290, 232, 1271, 1272, 0, 8, 1230, 0, 1232, 1277, 2, 5, 356, 60, 1113, 1280, 5, 356, 490, 1, 48, 1284, 4, 9, 48, 0, 2, 1288, 48, 366, 1291, 2, 6, 1289, 48, 1291, 1294, 4, 9, 746, 0, 2, 1298, + 2, 4, 747, 746, 1301, 1302, 5, 8, 10, 0, 2, 1307, 10, 394, 1309, 5, 8, 490, 0, 2, 1313, 0, 4, 491, 490, 1315, 1316, 5, 216, 490, 1, 48, 1320, 3, 10, 1320, 3, 10, 1284, 1, 6, 132, 4, 6, 132, 302, 1328, 1331, 0, 6, 243, 4, 7, 242, 357, 1334, 1336, 0, 7, 242, 4, 6, 243, 357, 1340, 1342, 0, 6, 242, 5, 6, 242, 302, 1347, 1348, 2, 8, 303, 1, 6, 1352, 4, 6, 1352, 302, 1354, 1357, 0, 6, 1352, 5, 6, 1352, 302, 1361, 1362, 4, 6, 133, 357, 1160, 1366, 0, 6, 133, 4, 7, 132, 357, 1370, 1372, 5, 134, 310, 7, 720, 1376, 357, 1334, 1340, 0, 7, 102, 51, 134, 1382, 6, 242, 356, 242, 1334, 1387, 6, 242, 357, 0, 7, 1390, 243, 1390, 1392, 0, 1387, 1390, 7, 8, 312, 6, 218, 313, 9, 1398, 1400, 1, 102, 134, 7, 720, 1404, 1, 134, 722, 7, 720, 1408, 7, 1334, 1390, 5, 312, 664, 3, 4, 1414, 7, 1414, 1416, 3, 522, 926, 5, 358, 1420, 6, 8, 358, 3, 522, 1424, 5, 358, 1426, 5, 522, 1424, 3, 358, 1430, 5, 312, 1424, 3, 358, 1434, 5, 522, 664, 3, 358, 1438, 3, 358, 1414, 7, 60, 1414, 2, 155, 200, 4, 41, 1446, 4, 155, 200, 2, 41, 1450, 3, 6, 1336, 4, 8, 1455, 0, 1336, 1457, 4, 8, 1188, 0, 1336, 1461, 2, 9, 1370, 0, 4, 1465, 6, 1371, 1466, 1, 132, 608, 6, 38, 1470, 8, 267, 572, 6, 38, 1474, 2, 8, 58, 0, 134, 1479, 4, 58, 1481, 0, 4, 135, 132, 500, 1484, 38, 582, 588, 1, 6, 1484, 132, 1484, 1490, 0, 6, 132, 1, 1484, 1494, 1, 4, 134, 135, 1494, 1498, 6, 38, 242, 0, 4, 242, 6, 242, 1504, 0, 242, 500, 8, 500, 1116, 4, 58, 242, 3, 6, 310, 312, 927, 1514, 1, 4, 354, 2, 7, 1518, 5, 6, 354, 9, 1520, 1522, 3, 4, 242, 0, 7, 1526, 9, 1348, 1528, 0, 3, 102, 1, 4, 102, 2, 7, 1534, 9, 1532, 1536, 1, 6, 660, 312, 927, 1540, 0, 3, 1030, 1, 6, 1030, 2, 5, 1546, 9, 1544, 1548, 1, 2, 1030, 3, 6, 1030, 0, 5, 1554, 9, 1552, 1556, 1, 2, 102, 3, 4, 102, 0, 7, 1562, 9, 1560, 1564, 4, 6, 355, 6, 92, 1569, 8, 1568, 1571, 3, 6, 132, 7, 1568, 1574, 6, 8, 93, 7, 1568, 1578, 3, 8, 554, 4, 7, 1582, 4, 6, 1582, 6, 1584, 1587, 4, 146, 1569, 8, 1568, 1591, 6, 358, 1569, 8, 1568, 1595, 2, 60, 1569, 8, 1568, 1599, 4, 6, 354, 2, 1569, 1602, 8, 1568, 1605, 2, 926, 1569, 8, 1568, 1609, 5, 6, 1582, 4, 1587, 1612, 8, 312, 927, 6, 313, 1616, 302, 1616, 1618, 4, 8, 313, 5, 6, 1622, 0, 9, 1624, 4, 312, 1626, 7, 1624, 1628, 4, 312, 1624, 0, 9, 1632, 7, 1624, 1634, 2, 5, 310, 6, 310, 1638, 0, 383, 1640, 0, 2, 383, 4, 6, 1644, 218, 383, 1646, 0, 2, 346, 4, 8, 1651, 282, 347, 1652, 6, 8, 1651, 302, 347, 1656, 7, 102, 312, 6, 313, 1660, 302, 1660, 1662, 6, 102, 313, 302, 1660, 1666, 5, 312, 1030, 6, 313, 1670, 302, 1670, 1672, 5, 6, 216, 0, 146, 1677, 4, 1676, 1679, 6, 216, 267, 1, 38, 1682, 0, 5, 1146, 6, 8, 1686, 147, 1146, 1688, 6, 9, 376, 0, 2, 1692, 376, 500, 1695, 0, 2, 590, 267, 500, 1698, 2, 6, 376, 0, 9, 1702, 376, 500, 1705, 0, 4, 146, 216, 500, 1709, 216, 500, 675, 0, 4, 147, 102, 1146, 1714, 406, 520, 1030, 8, 377, 490, 0, 9, 1720, 501, 1720, 1722, 4, 8, 58, 0, 1720, 1727, 0, 8, 500, 0, 1720, 1731, 0, 577, 1720, 0, 4, 753, 752, 1030, 1737, 0, 134, 1281, 6, 8, 357, 7, 1740, 1742, 2, 6, 357, 0, 133, 1746, 7, 1742, 1748, 7, 138, 1742, 6, 92, 377, 0, 8, 1754, 1, 8, 1754, 0, 1757, 1758, 6, 8, 1737, 7, 752, 1762, 4, 9, 376, 483, 656, 1766, 2, 9, 48, 475, 656, 1770, 6, 8, 217, 2, 5, 1774, 0, 1677, 1776, 2, 7, 558, 0, 575, 1780, 2, 5, 560, 0, 1479, 1784, 6, 8, 1527, 0, 1348, 1789, 2, 8, 39, 2, 5, 38, 6, 8, 1794, 0, 1792, 1797, 2, 8, 59, 2, 7, 58, 4, 8, 1802, 0, 1800, 1805, 3, 4, 656, 312, 501, 1808, 312, 501, 1514, 6, 8, 377, 312, 501, 1814, 4, 8, 217, 2, 7, 1818, 0, 1137, 1820, 61, 500, 1730, 1, 8, 1802, 4, 58, 1826, 1, 8, 1794, 6, 38, 1830, 0, 8, 60, 61, 500, 1834, 0, 9, 60, 4, 58, 1839, 6, 38, 1839, 216, 432, 1113, 3, 216, 432, 61, 432, 1846, 3, 60, 216, 61, 432, 1850, 5, 6, 48, 2, 7, 540, 1, 1854, 1856, 2, 61, 216, 1, 1850, 1860, 51, 500, 924, 2, 8, 1188, 267, 500, 1866, 0, 8, 1146, 5, 146, 1870, 147, 1146, 1872, 0, 2, 61, 1, 1850, 1876, 4, 7, 376, 6, 313, 376, 1, 1880, 1882, 3, 4, 282, 357, 1160, 1886, 5, 58, 132, 59, 1838, 1890, 0, 5, 318, 8, 312, 1895, 6, 318, 1897, 0, 2, 323, 9, 500, 1900, 322, 501, 1902, 7, 38, 1830, 6, 1833, 1906, 5, 58, 1826, 4, 1829, 1910, 0, 8, 501, 0, 2, 501, 9, 60, 1916, 1, 1914, 1918, 4, 6, 1839, 8, 312, 1923, 1, 1838, 1924, 0, 9, 346, 4, 6, 1929, 8, 312, 1931, 1, 1928, 1932, 6, 39, 1830, 38, 1833, 1936, 4, 146, 197, 4, 146, 196, 0, 1940, 1943, 2, 60, 197, 3, 1876, 1946, 4, 6, 41, 0, 3, 1950, 0, 2, 1951, 197, 1952, 1954, 6, 196, 358, 6, 197, 358, 0, 1959, 1960, 5, 6, 376, 0, 2, 1965, 6, 8, 1966, 4, 376, 1969, 377, 1964, 1970, 2, 4, 382, 500, 1644, 1975, 2, 4, 267, 0, 7, 1978, 8, 266, 1981, 0, 1978, 1982, 6, 1980, 1985, 0, 10, 382, 6, 1058, 1989, 2, 48, 382, 6, 416, 1993, 4, 8, 146, 6, 8, 147, 0, 2, 1999, 1997, 1998, 2000, 6, 103, 312, 3, 1660, 2004, 4, 312, 1031, 3, 1670, 2008, 2, 354, 927, 3, 354, 926, 9, 2012, 2014, 9, 312, 926, 3, 1616, 2018, 3, 8, 2018, 927, 2018, 2022, 2, 9, 2014, 927, 2014, 2026, 3, 6, 1660, 103, 1660, 2030, 3, 4, 1670, 1031, 1670, 2034, 2, 9, 60, 2, 8, 60, 0, 2038, 2041, 0, 432, 2041, 60, 1876, 2041, 0, 1146, 1997, 0, 520, 1425, 358, 406, 1425, 146, 1714, 1997, 48, 134, 927, 9, 1150, 1876, 3, 1876, 2038, 2, 60, 203, 0, 9, 338, 2, 60, 2065, 6, 203, 358, 4, 146, 203, 2, 203, 1950, 2, 4, 203, 6, 203, 2074, 8, 49, 60, 8, 146, 377, 2, 41, 60, 1, 8, 2082, 2, 60, 339, 1, 8, 2086, 5, 60, 358, 1, 8, 2090, 7, 60, 146, 1, 8, 2094, 3, 40, 1030, 5, 202, 2098, 4, 8, 147, 5, 202, 2102, 1, 8, 338, 0, 2041, 2106, 5, 8, 202, 2, 6, 2110, 202, 2110, 2113, 5, 8, 40, 2, 6, 2116, 202, 2116, 2119, 2, 4, 154, 154, 202, 2123, 7, 8, 202, 2, 4, 2126, 202, 2126, 2129, 6, 8, 92, 0, 190, 2133, 0, 190, 1425, 2, 6, 203, 132, 202, 2139, 1, 2, 1150, 1, 10, 14, 1, 8, 26, 2, 8, 61, 1, 60, 2148, 1, 8, 1042, 1, 146, 2102, 5, 8, 146, 1, 4, 2156, 1, 8, 2038, 6, 9, 358, 1, 8, 2162, 1, 6, 12, 5, 8, 58, 0, 3, 2168, 2, 1479, 2170, 8, 58, 2171, 2, 2170, 2175, 6, 102, 1561, 2, 8, 2178, 0, 1560, 2181, 0, 2, 2169, 1479, 2168, 2184, 1, 2, 2168, 0, 1479, 2188, 2, 9, 58, 59, 2170, 2192, 5, 8, 2192, 0, 3, 2196, 59, 2192, 2198, 5, 312, 346, 7, 8, 346, 346, 2202, 2204, 4, 7, 620, 313, 602, 2208, 6, 102, 312, 4, 7, 2212, 313, 2212, 2214, 4, 6, 501, 0, 2, 2218, 2, 9, 2218, 500, 2220, 2223, 346, 602, 2204, 4, 313, 602, 602, 1030, 2228, 346, 602, 1030, 4, 312, 1030, 313, 602, 2234, 60, 382, 555, 346, 605, 2202, 2, 5, 408, 4, 359, 2242, 3, 6, 2156, 2, 147, 2246, 8, 338, 2087, 8, 40, 807, 8, 40, 2083, 4, 41, 664, 8, 40, 2257, 6, 41, 92, 8, 40, 2261, 2, 41, 926, 8, 40, 2265, 8, 40, 1425, 8, 40, 787, 0, 9, 618, 4, 312, 2273, 6, 619, 2274, 4, 7, 312, 303, 346, 2278, 4, 203, 602, 4, 602, 2273, 203, 312, 346, 312, 346, 2273, 4, 6, 2273, 312, 2273, 2290, 4, 6, 203, 203, 312, 2294, 302, 312, 2279, 6, 2278, 2299, 6, 2273, 2278, 6, 8, 356, 2, 5, 2304, 1, 48, 2306, 0, 2, 1306, 6, 1306, 2310, 3, 10, 2312, 8, 10, 267, 0, 6, 2316, 10, 2316, 2318, 0, 8, 490, 3, 4, 2322, 2, 5, 2322, 1, 2324, 2326, 10, 660, 1964, 5, 6, 1188, 10, 660, 2332, 10, 544, 1306, 1, 2, 48, 5, 8, 2338, 544, 2338, 2340, 10, 660, 1072, 6, 8, 235, 2, 232, 2346, 4, 235, 2348, 2, 102, 232, 3, 290, 2352, 3, 4, 232, 102, 290, 2356, 3, 232, 290, 102, 290, 2360, 2, 5, 232, 6, 8, 2364, 2, 232, 2367, 4, 2364, 2369, 2, 103, 232, 4, 2364, 2373, 2, 102, 233, 102, 290, 2377, 233, 2352, 2356, 4, 2352, 2373, 3, 4, 2352, 233, 2352, 2384, 2, 4, 58, 2, 560, 2389, 1, 4, 906, 6, 906, 2392, 1, 4, 900, 6, 900, 2396, 1, 4, 910, 6, 910, 2400, 1, 4, 914, 6, 914, 2404, 1, 4, 890, 6, 890, 2408, 4, 890, 1328, 4, 7, 1328, 8, 1328, 2414, 8, 103, 1328, 2, 6, 39, 5, 6, 38, 8, 2420, 2423, 4, 7, 50, 4, 9, 50, 6, 2426, 2429, 5, 8, 346, 312, 346, 2432, 4, 6, 51, 5, 8, 2436, 50, 2436, 2438, 5, 6, 50, 8, 2436, 2442, 8, 346, 602, 0, 9, 10, 2, 6, 11, 4, 2449, 2450, 4, 8, 50, 5, 6, 2454, 51, 2454, 2456, 4, 8, 312, 5, 6, 2460, 313, 2460, 2462, 2, 4, 628, 0, 10, 2467, 2, 4, 629, 0, 3, 2470, 5, 2470, 2472, 2, 377, 628, 4, 376, 2477, 4, 49, 628, 2, 48, 2481, 3, 376, 2470, 5, 48, 2470, 2, 4, 14, 0, 10, 2489, 48, 376, 629, 0, 2467, 2470, 2, 5, 628, 48, 629, 2496, 2, 8, 1854, 1, 48, 2500, 1, 48, 1478, 3, 10, 1478, 3, 8, 10, 10, 58, 2508, 10, 58, 2193, 4, 267, 1478, 0, 6, 267, 2, 8, 2516, 4, 267, 2518, 1, 6, 1460, 2, 4, 2523, 1460, 2522, 2525, 0, 93, 1172, 6, 9, 2528, 1172, 2528, 2531, 500, 523, 1188, 3, 6, 1876, 4, 8, 1876, 1, 2536, 2538, 267, 424, 1460, 267, 424, 574, 3, 6, 376, 1, 1460, 2546, 1, 1460, 1854, 5, 6, 746, 1, 1460, 2552, 1, 574, 2546, 5, 8, 746, 302, 629, 2558, 0, 9, 1514, 310, 927, 2562, 310, 927, 1838, 232, 357, 1298, 357, 1030, 1838, 4, 9, 58, 0, 3, 2572, 5, 8, 2574, 59, 2572, 2576, 0, 5, 1030, 1, 60, 1030, 9, 2580, 2582, 0, 5, 322, 4, 7, 322, 9, 2586, 2588, 4, 9, 2170, 59, 2168, 2592, 302, 322, 927, 6, 8, 1979, 1, 266, 2598, 4, 14, 49, 6, 8, 2339, 4, 49, 2604, 4, 14, 465, 2, 14, 377, 1, 2, 14, 4, 14, 2612, 1, 14, 266, 1, 14, 444, 1, 8, 904, 1, 8, 500, 0, 5, 1424, 217, 358, 2624, 6, 8, 216, 0, 5, 2628, 217, 358, 2630, 4, 8, 521, 354, 358, 2635, 0, 3, 1424, 357, 358, 2638, 0, 9, 146, 267, 358, 2642, 1, 4, 282, 2, 7, 2646, 5, 1886, 2648, 4, 8, 267, 0, 6, 2653, 267, 358, 2654, 6, 48, 629, 5, 358, 2658, 7, 146, 2658, 6, 48, 628, 48, 146, 2665, 2, 283, 2646, 41, 282, 2668, 41, 282, 2648, 282, 1259, 2648, 2, 4, 746, 282, 358, 2677, 4, 8, 216, 60, 555, 2680, 2, 41, 1124, 8, 500, 2684, 4, 6, 1125, 2, 1124, 2689, 8, 500, 2690, 5, 358, 1124, 8, 500, 2694, 7, 146, 1124, 8, 500, 2698, 2, 5, 1124, 7, 1124, 2702, 8, 500, 2704, 1, 58, 358, 8, 1166, 2708, 8, 41, 500, 0, 2, 2712, 500, 2712, 2714, 1, 38, 146, 8, 1132, 2718, 0, 2, 927, 4, 6, 2723, 8, 927, 2724, 2722, 2724, 2726, 4, 7, 628, 974, 2470, 2731, 1, 2, 232, 267, 554, 2734, 9, 232, 2736, 3, 232, 376, 4, 6, 232, 9, 376, 2742, 1, 2740, 2744, 4, 6, 2740, 9, 376, 2748, 1, 2740, 2750, 2, 5, 354, 1, 1602, 2754, 9, 232, 2756, 7, 354, 376, 1, 6, 2760, 1766, 2760, 2762, 4, 282, 376, 3, 8, 2766, 7, 376, 2768, 1, 2766, 2770, 9, 376, 554, 3, 8, 2774, 7, 376, 2776, 1, 2774, 2778, 7, 8, 2774, 3, 376, 2782, 1, 2774, 2784, 8, 38, 233, 48, 2734, 2789, 1, 60, 242, 0, 2041, 2792, 0, 8, 2039, + 2, 61, 2796, 3, 2038, 2798, 9, 60, 1876, 3, 8, 2802, 61, 2802, 2804, 61, 1150, 2802, 0, 2041, 2142, 1, 6, 408, 0, 1425, 2812, 0, 1997, 2158, 8, 358, 1425, 0, 7, 2818, 6, 1425, 2820, 8, 146, 1997, 0, 5, 2824, 4, 1997, 2826, 94, 927, 2448, 146, 629, 1544, 41, 282, 1552, 8, 377, 1702, 0, 7, 266, 8, 266, 2839, 0, 7, 92, 8, 92, 2843, 8, 242, 554, 4, 8, 1348, 6, 8, 1336, 0, 6, 266, 1, 8, 2852, 8, 60, 242, 8, 266, 656, 8, 242, 926, 0, 2, 102, 4, 283, 2862, 102, 133, 2864, 3, 4, 134, 1, 2, 2868, 0, 7, 2868, 5, 2870, 2872, 8, 2868, 2874, 1, 2, 1366, 0, 7, 1366, 5, 2878, 2880, 8, 1366, 2882, 8, 38, 2878, 5, 1366, 2886, 8, 2878, 2880, 5, 1366, 2890, 8, 38, 2870, 5, 2868, 2894, 4, 9, 60, 0, 7, 2898, 8, 10, 2900, 5, 2898, 2902, 3, 4, 26, 8, 10, 38, 5, 2906, 2908, 9, 10, 160, 38, 160, 2912, 9, 10, 1066, 38, 1066, 2916, 10, 41, 50, 8, 40, 2921, 5, 8, 554, 3, 6, 2924, 2, 555, 2924, 4, 2926, 2928, 3, 4, 702, 2, 501, 702, 6, 2932, 2934, 8, 10, 50, 8, 40, 2939, 8, 147, 500, 2, 6, 2942, 501, 2942, 2944, 8, 359, 554, 2, 4, 2948, 555, 2948, 2950, 1, 8, 1188, 4, 6, 1188, 359, 2954, 2956, 1, 8, 38, 40, 573, 2960, 0, 5, 508, 0, 6, 509, 40, 2964, 2967, 61, 226, 1818, 7, 134, 1532, 5, 8, 134, 0, 3, 2974, 7, 134, 2976, 0, 665, 1560, 2, 665, 1532, 0, 2, 103, 102, 665, 2984, 9, 972, 1532, 5, 6, 972, 0, 3, 2990, 9, 972, 2992, 6, 8, 1533, 2, 1532, 2997, 0, 4, 95, 3, 94, 3000, 6, 8, 3000, 2, 3002, 3005, 2, 4, 657, 1, 656, 3008, 6, 8, 3008, 0, 3010, 3013, 0, 656, 3013, 3008, 3013, 3016, 2, 94, 3005, 3000, 3005, 3020, 0, 656, 3009, 3008, 3013, 3024, 2, 94, 3001, 3000, 3005, 3028, 6, 8, 3029, 3000, 3028, 3033, 6, 8, 3025, 3008, 3024, 3037, 7, 8, 3000, 9, 3028, 3040, 7, 8, 3008, 9, 3024, 3044, 216, 267, 500, 1, 1166, 1478, 10, 58, 313, 3, 4, 216, 1, 1676, 3054, 313, 376, 500, 1, 4, 1090, 2, 9, 358, 1090, 3060, 3062, 0, 4, 3062, 1, 1090, 3066, 0, 4, 94, 6, 94, 3071, 0, 8, 3072, 4, 3071, 3074, 1, 8, 358, 359, 3066, 3078, 2, 9, 3078, 0, 4, 3082, 359, 3078, 3084, 1, 4, 94, 5, 6, 3088, 0, 8, 3090, 95, 3088, 3092, 0, 8, 358, 8, 3066, 3097, 1, 8, 3066, 359, 3066, 3100, 354, 734, 1133, 2, 8, 501, 0, 3, 3106, 1, 2, 3108, 3107, 3108, 3110, 217, 1116, 1914, 0, 2, 228, 0, 8, 229, 217, 3116, 3118, 354, 1116, 1731, 0, 9, 504, 1, 8, 3124, 505, 3124, 3126, 3, 8, 500, 0, 501, 3130, 1, 500, 3132, 3131, 3132, 3134, 312, 1914, 1917, 2, 6, 38, 354, 577, 3140, 354, 1727, 2388, 0, 2082, 2255, 0, 2, 661, 5, 6, 3148, 4, 7, 3148, 3, 3150, 3152, 0, 2, 2103, 4, 7, 3156, 3, 146, 3158, 4, 7, 2000, 3, 146, 3162, 4, 8, 359, 0, 2, 3167, 5, 6, 3168, 3, 358, 3170, 6, 8, 359, 0, 2, 3175, 5, 6, 3176, 3, 358, 3178, 0, 2, 1151, 4, 7, 3182, 5, 60, 3184, 5, 6, 3182, 7, 60, 3188, 5, 60, 3152, 7, 60, 3150, 355, 2012, 2014, 4, 313, 1030, 5, 2008, 3198, 7, 1666, 2004, 1030, 2008, 2235, 6, 1660, 2213, 2, 9, 926, 0, 3, 3208, 1, 8, 3208, 927, 3210, 3212, 5, 8, 312, 618, 621, 3216, 312, 2235, 3198, 312, 1666, 2213, 0, 243, 926, 0, 242, 926, 242, 3224, 3227, 3, 6, 10, 8, 10, 3230, 6, 10, 135, 8, 10, 973, 3, 10, 664, 4, 267, 664, 1, 48, 664, 0, 7, 164, 41, 164, 3244, 11, 38, 134, 11, 904, 1188, 38, 661, 746, 0, 151, 172, 0, 157, 164, 4, 8, 60, 0, 2898, 3259, 58, 266, 665, 0, 5, 60, 8, 60, 3265, 4, 3264, 3267, 2, 151, 1040, 1, 40, 1996, 1, 2, 926, 8, 40, 3274, 1, 2, 1950, 8, 40, 3278, 8, 40, 432, 6, 10, 190, 6, 190, 358, 1, 4, 664, 8, 40, 3288, 8, 40, 1146, 41, 252, 1172, 232, 2428, 2455, 5, 50, 232, 4, 9, 3298, 51, 3298, 3300, 4, 51, 232, 9, 3298, 3304, 5, 8, 50, 9, 3304, 3308, 4, 8, 51, 9, 3298, 3312, 4, 50, 232, 232, 2428, 3317, 0, 7, 3308, 4, 51, 3320, 9, 3308, 3322, 0, 7, 3312, 5, 50, 3326, 9, 3312, 3328, 4, 2455, 3298, 1, 746, 2680, 1, 2, 218, 8, 490, 3337, 8, 746, 1159, 2, 8, 1316, 3, 490, 3342, 4, 8, 1072, 3, 490, 3346, 1, 216, 630, 3, 6, 2680, 1, 216, 3352, 1, 6, 2680, 3, 216, 3356, 3, 490, 2680, 0, 8, 1302, 1, 746, 3362, 0, 41, 1042, 0, 339, 2038, 0, 41, 2162, 0, 41, 1048, 2, 9, 1950, 0, 41, 3374, 0, 41, 2038, 0, 26, 41, 5, 8, 266, 2, 614, 3383, 0, 383, 3384, 0, 7, 3274, 2, 628, 926, 6, 3388, 3391, 0, 6, 3275, 3274, 3391, 3394, 0, 146, 927, 665, 3274, 3398, 0, 2, 926, 6, 926, 3403, 1, 2, 3404, 6, 8, 3402, 0, 3406, 3409, 0, 3, 3404, 2, 3409, 3412, 0, 2, 3405, 3404, 3409, 3416, 4, 8, 266, 6, 267, 3420, 1, 2, 3422, 0, 383, 3424, 9, 3274, 3388, 2, 926, 3429, 6, 3388, 3431, 2, 746, 927, 665, 3274, 3434, 1, 2, 2156, 0, 151, 3438, 9, 590, 1532, 9, 102, 266, 7, 1532, 3444, 0, 5, 94, 9, 94, 266, 7, 3448, 3450, 9, 590, 3448, 7, 102, 266, 9, 1532, 3456, 7, 94, 266, 9, 3448, 3460, 2991, 2992, 2994, 9, 972, 2990, 2991, 2992, 3466, 2, 5, 926, 6, 972, 3471, 0, 927, 3472, 8, 2843, 3000, 8, 625, 3008, 1, 6, 624, 2, 4, 3481, 8, 625, 3482, 3, 6, 2842, 0, 4, 3487, 8, 2843, 3488, 8, 591, 1978, 2, 267, 3258, 4, 150, 267, 10, 146, 660, 3, 4, 18, 5, 10, 3500, 3, 4, 664, 5, 10, 3504, 2, 4, 383, 267, 382, 3508, 1, 376, 3504, 2, 267, 3504, 3, 4, 382, 2, 267, 3516, 2, 58, 357, 7, 356, 3520, 60, 3520, 3522, 0, 8, 48, 2, 7, 3526, 60, 541, 3528, 5, 312, 432, 60, 1398, 3532, 3, 146, 232, 102, 232, 3537, 2, 3536, 3539, 1, 2, 302, 4, 6, 3542, 302, 500, 3543, 2, 3545, 3546, 3, 302, 500, 4, 6, 3551, 302, 500, 3552, 2, 3550, 3555, 6, 61, 356, 2, 58, 3559, 356, 3559, 3560, 3, 58, 356, 4, 6, 3565, 5, 356, 3566, 2, 3564, 3569, 58, 356, 3566, 2, 3564, 3573, 4, 8, 282, 2, 6, 3576, 3, 282, 926, 2, 3579, 3580, 0, 904, 927, 5, 38, 904, 6, 38, 559, 4, 58, 561, 4, 58, 927, 6, 38, 927, 0, 500, 927, 0, 555, 904, 9, 38, 58, 4, 9, 312, 7, 1624, 3602, 4, 8, 2450, 0, 396, 3607, 7, 10, 660, 5, 2448, 3610, 3, 10, 1030, 5, 2448, 3614, 9, 712, 3216, 9, 312, 1622, 7, 1624, 3620, 7, 8, 2448, 3, 10, 3624, 5, 2448, 3626, 3, 8, 2448, 7, 10, 3630, 5, 2448, 3632, 6, 313, 3216, 4, 7, 3636, 9, 3216, 3638, 7, 8, 3602, 5, 312, 3642, 313, 3602, 3644, 0, 634, 1031, 0, 634, 1030, 6, 3649, 3650, 0, 635, 1030, 636, 1030, 3655, 4, 8, 640, 0, 634, 3659, 6, 640, 3661, 6, 640, 3649, 1, 636, 3650, 635, 644, 3650, 636, 644, 1030, 6, 959, 3650, 4, 8, 959, 0, 634, 3674, 6, 959, 3676, 347, 348, 1928, 0, 322, 2305, 0, 322, 577, 0, 8, 346, 0, 348, 3687, 8, 1928, 3687, 5, 6, 310, 7, 1838, 3692, 60, 1382, 1835, 4, 6, 242, 282, 310, 3699, 302, 656, 3699, 60, 1835, 2580, 92, 282, 2467, 2, 4, 656, 8, 40, 3709, 0, 656, 3711, 8, 359, 3008, 0, 656, 3715, 8, 266, 358, 267, 282, 3718, 8, 267, 282, 282, 358, 3722, 8, 266, 359, 0, 656, 3727, 0, 6, 92, 2, 4, 3730, 92, 282, 3733, 10, 282, 291, 2, 4, 522, 282, 358, 3739, 0, 3, 656, 927, 3008, 3742, 0, 10, 657, 19, 656, 3746, 0, 10, 103, 19, 102, 3750, 0, 10, 95, 19, 94, 3754, 0, 10, 15, 14, 19, 3758, 6, 8, 3758, 14, 3758, 3763, 358, 1425, 3448, 2, 4, 95, 6, 8, 3768, 3448, 3768, 3771, 6, 8, 634, 634, 3448, 3775, 2, 4, 3449, 6, 8, 3778, 3448, 3778, 3781, 0, 5, 614, 4, 7, 614, 9, 3784, 3786, 6, 8, 2984, 102, 302, 3791, 4, 8, 3403, 282, 927, 3794, 6, 8, 3403, 302, 927, 3798, 218, 383, 904, 6, 218, 267, 9, 222, 3804, 102, 302, 383, 2, 4, 731, 6, 8, 3810, 38, 730, 3813, 0, 6, 376, 4, 8, 376, 1030, 3816, 3819, 0, 665, 1030, 5, 666, 3822, 5, 50, 354, 4, 7, 3826, 9, 3826, 3828, 4, 7, 354, 5, 2428, 3832, 7, 1522, 2428, 7, 904, 3826, 0, 2, 904, 5, 354, 3840, 7, 904, 3842, 4, 242, 1189, 0, 927, 3846, 1, 6, 376, 102, 1766, 3851, 3, 8, 904, 0, 7, 3854, 4, 6, 3854, 904, 3856, 3859, 4, 7, 746, 3, 8, 3862, 2, 8, 3862, 2, 3864, 3867, 3, 8, 640, 634, 3659, 3870, 3, 4, 1188, 2, 8, 3875, 2, 8, 3874, 3874, 3876, 3879, 2, 9, 3874, 8, 3879, 3882, 2, 8, 1189, 3, 4, 3886, 5, 1188, 3886, 9, 3888, 3890, 4, 8, 1189, 2, 5, 3894, 3, 1188, 3894, 9, 3896, 3898, 3, 8, 1188, 2, 5, 3902, 4, 1189, 3902, 9, 3904, 3906, 5, 8, 1188, 3, 4, 3910, 2, 1189, 3910, 9, 3912, 3914, 4, 9, 1876, 5, 8, 1876, 1877, 3918, 3920, 5, 8, 3918, 1877, 3918, 3924, 5, 378, 3818, 1, 6, 10, 8, 394, 3930, 1, 4, 746, 2, 5, 746, 8, 3934, 3936, 8, 432, 3264, 58, 92, 313, 0, 8, 10, 1, 394, 3944, 3, 366, 420, 11, 3930, 3944, 49, 420, 544, 2, 8, 544, 49, 544, 3954, 1, 6, 3106, 4, 9, 3958, 3106, 3130, 3960, 0, 8, 555, 4, 6, 555, 2, 3964, 3966, 2, 3965, 3966, 8, 3969, 3970, 0, 7, 3130, 5, 8, 3974, 500, 3131, 3976, 2, 3130, 3979, 4, 6, 3106, 1, 2, 3982, 9, 500, 3984, 3, 3106, 3986, 1, 2, 2218, 9, 500, 3990, 3, 3106, 3992, 1, 2, 904, 9, 500, 3996, 3, 3106, 3998, 1, 4, 3106, 9, 500, 3106, 6, 4002, 4004, 3, 3106, 4006, 4, 3958, 4004, 3, 3106, 4010, 1, 4, 4004, 6, 4004, 4014, 3, 3106, 4016, 3, 8, 3966, 3964, 3966, 4021, 2, 4020, 4023, 303, 926, 2984, 6, 8, 303, 0, 2, 4029, 303, 926, 4030, 1, 4, 2984, 5, 6, 2984, 8, 4034, 4036, 0, 2, 1031, 1, 6, 4040, 4, 7, 4040, 8, 4042, 4044, 2, 38, 103, 39, 576, 4048, 6, 8, 4048, 39, 4048, 4052, 4, 8, 59, 2, 6, 4057, 59, 1726, 4058, 4, 8, 4058, 59, 4058, 4062, 39, 576, 3810, 0, 3, 40, 8, 1950, 4069, 0, 3, 338, 8, 60, 4073, 8, 60, 4069, 0, 3, 926, 8, 60, 4079, 8, 60, 3071, 0, 6, 660, 8, 60, 4085, 0, 6, 48, 8, 60, 4089, 2, 5, 60, 0, 6, 4093, 8, 60, 4095, 60, 490, 905, 51, 146, 926, 41, 490, 926, 8, 60, 490, 6, 92, 313, 217, 502, 3106, 312, 1150, 1835, 312, 1731, 3130, 3, 8, 346, 312, 3687, 4114, 2, 9, 500, 217, 1914, 4118, 0, 8, 61, 217, 2038, 4122, 0, 8, 3931, 0, 9, 3930, 1, 4126, 4128, 0, 8, 433, 0, 9, 432, 1, 4132, 4134, 0, 8, 1147, 0, 9, 1146, 1, 4138, 4140, 0, 8, 521, 0, 9, 520, 1, 4144, 4146, 4, 6, 48, 1, 132, 4150, 3, 4, 50, 1, 132, 4154, 1, 4, 132, 60, 132, 4158, 0, 133, 1188, 4, 132, 4163, 4, 6, 491, 1, 132, 4166, 0, 2, 1030, 8, 10, 4171, 0, 4, 972, 8, 10, 4175, 1, 132, 3258, 132, 1189, 4158, 2, 7, 376, 4, 8, 4183, 1, 376, 4184, 0, 1731, 2622, 8, 38, 611, 6, 610, 4191, 8, 577, 734, 302, 656, 927, 9, 610, 730, 39, 610, 734, 6, 9, 610, 39, 610, 4202, 7, 730, 734, 4, 8, 243, 60, 1340, 4209, 0, 8, 3275, 500, 927, 4212, 927, 3584, 3854, 0, 3, 906, 5, 6, 4218, 7, 904, 4220, 0, 5, 1578, 4, 927, 4224, 0, 5, 3174, 4, 927, 4228, 0, 7, 2102, 6, 927, 4232, 1, 4, 1578, 0, 927, 4236, 1, 6, 2102, 0, 927, 4240, 1, 4, 3174, 0, 927, 4244, 0, 8, 3997, 5, 6, 4248, 7, 904, 4250, 2, 9, 554, 4, 146, 4255, 6, 358, 4255, 2, 60, 4255, 2, 8, 554, 60, 555, 4262, 60, 555, 2040, 8, 60, 1583, 282, 358, 3067, 3, 8, 282, 0, 4, 4273, 282, 358, + 4275, 2, 7, 3420, 267, 282, 4278, 2, 7, 3576, 267, 282, 4282, 1, 2, 3576, 41, 282, 4286, 4, 8, 40, 1, 2, 4290, 41, 282, 4292, 2, 9, 10, 4, 6, 4296, 10, 282, 4299, 218, 554, 629, 302, 501, 656, 282, 310, 501, 59, 218, 282, 39, 232, 302, 0, 9, 926, 1, 8, 4312, 927, 4312, 4314, 1, 6, 1382, 103, 1382, 4318, 1, 4, 2580, 1031, 2580, 4322, 0, 558, 577, 6, 8, 311, 0, 3692, 4329, 38, 102, 927, 0, 5, 1546, 9, 1030, 4334, 0, 9, 1546, 5, 1030, 4338, 0, 4, 103, 6, 9, 4342, 7, 102, 4344, 0, 7, 1534, 9, 102, 4348, 0, 6, 1031, 5, 8, 4352, 9, 1030, 4354, 4, 9, 4352, 5, 1030, 4358, 0, 9, 1534, 7, 102, 4362, 7, 8, 4342, 9, 102, 4366, 6, 8, 927, 1, 4, 4370, 0, 927, 4372, 2, 8, 1950, 1, 40, 4376, 0, 41, 4376, 4377, 4378, 4380, 0, 338, 2040, 0, 338, 2041, 2040, 4385, 4386, 1, 40, 2132, 0, 40, 2132, 0, 4390, 4393, 1, 40, 1424, 0, 40, 1424, 0, 4396, 4399, 0, 41, 2132, 40, 4393, 4402, 0, 41, 1424, 40, 4399, 4406, 1, 338, 2040, 338, 2040, 4411, 0, 4410, 4413, 2133, 4390, 4402, 1425, 4396, 4406, 0, 41, 4390, 2133, 4390, 4420, 1, 8, 282, 2, 4, 4425, 2, 4, 4424, 7, 282, 4428, 4424, 4426, 4431, 1, 628, 3008, 5, 1808, 4434, 6, 8, 629, 3, 4, 4438, 2, 4, 4439, 1, 628, 4442, 5, 4440, 4444, 3, 4, 4424, 6, 283, 4426, 5, 4448, 4450, 2, 5, 4424, 3, 4450, 4454, 4, 4431, 4454, 2, 5, 656, 7, 282, 3708, 4, 4460, 4463, 2, 4431, 4448, 2, 5, 4438, 3, 4444, 4468, 4439, 4440, 4468, 1, 628, 4472, 0, 3, 4460, 4, 7, 4460, 9, 4476, 4478, 1, 2, 3448, 4, 7, 4482, 9, 3448, 4484, 0, 5, 1454, 9, 1336, 4488, 0, 5, 1808, 2, 7, 1808, 9, 4492, 4494, 9, 1336, 3448, 2, 926, 2389, 2, 6, 266, 2, 926, 4503, 2, 926, 3141, 2, 926, 1117, 2, 926, 3841, 905, 926, 3996, 4, 6, 3997, 905, 3996, 4514, 1, 926, 1916, 2, 6, 267, 4, 7, 4520, 8, 4520, 4522, 4, 7, 3850, 8, 3850, 4526, 0, 93, 358, 0, 41, 634, 1, 4, 376, 8, 2546, 4534, 8, 10, 2546, 8, 10, 2516, 8, 10, 1854, 8, 10, 2552, 5, 6, 464, 8, 10, 4546, 8, 10, 406, 0, 6, 93, 8, 10, 4552, 0, 6, 445, 8, 10, 4556, 3, 6, 454, 8, 10, 4560, 0, 5, 2162, 8, 359, 4564, 7, 2162, 4566, 7, 312, 660, 5, 6, 4570, 9, 4570, 4572, 3, 38, 972, 9, 2990, 4576, 3, 58, 132, 9, 1372, 4580, 8, 38, 147, 9, 38, 146, 5, 4584, 4586, 8, 58, 359, 9, 58, 358, 7, 4590, 4592, 2, 4, 59, 8, 58, 4597, 9, 58, 4596, 7, 4598, 4600, 8, 38, 2421, 9, 38, 2420, 5, 4604, 4606, 48, 629, 2156, 376, 629, 1562, 376, 629, 1150, 232, 267, 1042, 267, 282, 1372, 2, 7, 660, 267, 282, 4620, 232, 267, 2038, 267, 282, 408, 267, 282, 1054, 232, 267, 2162, 1, 4, 216, 6, 8, 4632, 4, 8, 3850, 4, 8, 2420, 4, 8, 4520, 6, 8, 4596, 8, 926, 3274, 5, 6, 10, 4, 8, 4646, 6, 8, 884, 1, 216, 926, 8, 10, 926, 4, 8, 93, 0, 2, 4657, 2133, 4656, 4658, 8, 927, 2722, 3, 634, 4662, 4, 93, 4662, 0, 2, 2133, 8, 2133, 4668, 4, 93, 4670, 0, 4, 665, 8, 665, 4674, 5, 634, 4676, 0, 665, 1254, 5, 634, 4680, 0, 8, 635, 3, 4, 4684, 634, 927, 4686, 0, 927, 1254, 3, 634, 4690, 4, 665, 4684, 5, 634, 4694, 5, 972, 3148, 9, 660, 4698, 0, 9, 554, 0, 500, 4703, 1, 58, 2730, 58, 1030, 2581, 6, 38, 4703, 0, 5, 500, 500, 1030, 4712, 0, 7, 500, 102, 500, 4716, 1, 6, 38, 38, 102, 4720, 5, 6, 356, 1, 38, 4724, 8, 500, 559, 38, 303, 500, 6, 8, 103, 4, 102, 4733, 4, 102, 927, 6, 927, 1030, 9, 102, 1030, 8, 904, 927, 5, 904, 1030, 6, 1030, 4079, 0, 3, 4746, 926, 4746, 4748, 3, 2722, 3274, 6, 1030, 4753, 2723, 3402, 4738, 1, 2, 4738, 0, 3, 4758, 926, 4758, 4760, 3, 3402, 4758, 0, 3, 4738, 1, 3402, 4766, 6, 1030, 3275, 1, 3402, 4770, 3, 3402, 4746, 2, 346, 635, 0, 4, 2041, 60, 2040, 4779, 0, 2, 1997, 146, 1996, 4783, 5, 92, 346, 8, 146, 3934, 2, 5, 346, 8, 346, 4790, 49, 60, 420, 49, 60, 2040, 92, 313, 602, 5, 6, 92, 92, 313, 4800, 4, 8, 1346, 242, 904, 4805, 0, 1347, 4806, 0, 3, 2622, 1188, 1731, 4810, 0, 1189, 3130, 1188, 1731, 4814, 1, 242, 500, 6, 8, 500, 0, 243, 4820, 4818, 4821, 4822, 0, 4, 2039, 2, 6, 4826, 2038, 4122, 4829, 51, 904, 972, 5, 302, 4832, 8, 50, 500, 0, 4818, 4837, 5, 310, 746, 7, 2162, 4840, 1, 4, 1348, 9, 1348, 4844, 6, 242, 4847, 0, 4844, 4849, 0, 3, 904, 4, 6, 972, 0, 8, 4854, 972, 4852, 4857, 133, 146, 232, 282, 358, 635, 60, 232, 661, 41, 92, 282, 1, 2, 346, 312, 348, 4869, 0, 7, 1574, 1, 6, 4872, 1575, 4872, 4874, 0, 4, 973, 49, 974, 4878, 0, 5, 2102, 1, 4, 4882, 2103, 4882, 4884, 354, 1112, 1835, 0, 2, 1602, 0, 8, 1602, 354, 4890, 4893, 354, 4079, 4312, 0, 3, 1602, 0, 9, 1602, 354, 4899, 4900, 0, 9, 1996, 1, 8, 4904, 1997, 4904, 4906, 0, 147, 2156, 1, 146, 4910, 2157, 4910, 4912, 4, 242, 1031, 0, 5, 4916, 1030, 4916, 4918, 8, 927, 3274, 9, 926, 3274, 0, 4922, 4924, 0, 2, 559, 1, 576, 4928, 0, 2, 561, 1, 1726, 4932, 9, 576, 1830, 0, 927, 3208, 1, 8, 4938, 926, 4938, 4940, 1, 8, 2722, 0, 9, 4944, 926, 4944, 4946, 0, 9, 4922, 926, 4922, 4950, 2, 8, 4313, 0, 9, 4954, 926, 4954, 4956, 0, 358, 656, 8, 3008, 4961, 0, 358, 635, 8, 640, 4965, 6, 10, 635, 8, 644, 4969, 283, 634, 1090, 14, 283, 634, 6, 10, 232, 8, 290, 4977, 2, 8, 2646, 41, 282, 4980, 282, 635, 2648, 6, 8, 2278, 6, 8, 2279, 2278, 4987, 4988, 6, 313, 1030, 9, 1670, 4992, 604, 927, 1622, 2, 4, 354, 2, 7, 354, 904, 4999, 5000, 904, 1340, 1505, 9, 312, 346, 8, 313, 346, 347, 5006, 5008, 8, 313, 5006, 347, 5006, 5012, 4, 7, 3216, 9, 3636, 5016, 6, 9, 2278, 7, 8, 5020, 2279, 5020, 5022, 7, 312, 1622, 9, 1624, 5026, 58, 283, 1498, 2, 4, 103, 0, 7, 5032, 1, 6, 5032, 102, 5034, 5036, 1, 6, 5034, 102, 5034, 5040, 2, 6, 1031, 0, 5, 5044, 1, 4, 5044, 1030, 5046, 5048, 1, 4, 5046, 1030, 5046, 5052, 5, 6, 640, 1, 3658, 5056, 356, 555, 644, 555, 628, 1498, 1, 4, 2192, 58, 283, 5064, 0, 5, 4118, 283, 500, 5068, 0, 103, 972, 1, 6, 5072, 102, 5072, 5074, 7, 312, 356, 1, 4724, 5078, 0, 2, 657, 303, 500, 5082, 1, 102, 972, 6, 5072, 5086, 9, 356, 972, 1, 4724, 5090, 9, 356, 624, 1, 4724, 5094, 0, 6, 5086, 103, 5086, 5098, 9, 58, 1188, 1, 1726, 5102, 1, 4, 972, 4, 628, 972, 0, 5106, 5109, 0, 5, 972, 629, 4878, 5112, 4, 629, 972, 5, 4878, 5116, 4, 628, 4879, 972, 4878, 5121, 6, 8, 5107, 4, 972, 5124, 0, 5106, 5127, 5, 972, 4878, 629, 4878, 5130, 5, 628, 972, 629, 4878, 5134, 0, 4175, 5116, 6, 8, 4174, 4, 972, 5141, 0, 4175, 5142, 4, 58, 312, 2, 1166, 5147, 3, 216, 228, 1, 2, 5150, 229, 5150, 5152, 2, 3117, 5150, 8, 501, 1126, 1124, 1126, 5158, 1124, 1126, 3106, 8, 500, 1125, 8, 1126, 5165, 9, 500, 1124, 1124, 1126, 5169, 503, 1124, 1126, 0, 2, 735, 1, 1132, 5174, 0, 2, 2573, 1, 1166, 5178, 0, 3, 664, 0, 4, 664, 2470, 5182, 5185, 1, 2, 382, 0, 4, 383, 1975, 5188, 5190, 0, 3, 382, 0, 4, 382, 3508, 5194, 5197, 4, 376, 382, 4, 376, 383, 382, 5201, 5202, 2, 48, 383, 382, 1993, 5206, 1, 2, 628, 2467, 4674, 5210, 6, 8, 5210, 0, 4, 5215, 2467, 5210, 5216, 6, 8, 2466, 0, 4, 5221, 2467, 5210, 5222, 4, 58, 303, 6, 38, 303, 0, 303, 500, 8, 311, 500, 8, 58, 219, 6, 356, 555, 6, 303, 356, 1, 58, 356, 5, 356, 500, 4, 9, 588, 0, 7, 5244, 582, 588, 5246, 6, 132, 134, 1, 4, 5250, 132, 1484, 5252, 5, 628, 634, 135, 1498, 5256, 4, 135, 628, 5, 134, 628, 0, 2, 5262, 1, 5260, 5264, 7, 500, 580, 267, 1730, 5268, 2, 9, 1730, 7, 500, 5272, 267, 1730, 5274, 2, 9, 576, 1, 38, 5278, 267, 576, 5280, 0, 2, 310, 572, 576, 5285, 6, 9, 132, 2, 4, 5289, 0, 3, 5290, 1, 4, 5288, 132, 5292, 5294, 0, 6, 581, 4, 267, 5298, 9, 266, 5298, 580, 5300, 5303, 4, 7, 200, 1, 2, 5306, 8, 40, 5309, 6, 5306, 5311, 512, 656, 927, 8, 40, 2735, 4, 236, 5317, 8, 40, 1553, 0, 1546, 5321, 4, 243, 1188, 7, 8, 5324, 6, 8, 5324, 6, 5326, 5329, 0, 2, 905, 6, 660, 5332, 0, 2620, 5335, 8, 5324, 5327, 6, 5326, 5339, 6, 8, 5325, 5324, 5329, 5342, 4, 216, 746, 232, 904, 5347, 4, 8, 746, 228, 232, 5351, 0, 5, 3854, 0, 6, 3854, 904, 5354, 5357, 0, 5, 660, 904, 4085, 5360, 0, 4, 3854, 904, 3856, 5365, 5, 6, 660, 6, 634, 5361, 7, 5368, 5370, 7, 8, 92, 6, 9, 92, 1, 634, 5376, 93, 5374, 5378, 6, 9, 5374, 1, 634, 5382, 93, 5374, 5384, 8, 93, 644, 7, 5376, 5388, 201, 408, 3174, 9, 40, 520, 7, 3174, 5394, 3, 8, 634, 4, 7, 5398, 6, 5399, 5400, 1, 634, 5402, 5, 5400, 5404, 5, 8, 634, 2, 7, 5408, 6, 5409, 5410, 1, 634, 5412, 3, 5410, 5414, 9, 92, 644, 7, 1578, 5418, 0, 1352, 1915, 0, 2, 311, 1, 1730, 5424, 1, 1726, 5424, 2, 9, 2304, 1, 4, 5430, 5, 2304, 5432, 2, 9, 1726, 1, 4, 5436, 5, 1726, 5438, 1, 356, 5430, 5, 2304, 5442, 5, 356, 5430, 1, 2304, 5446, 2, 9, 356, 5, 628, 5450, 1, 356, 5452, 1, 356, 5256, 2, 4, 219, 5, 2304, 5458, 356, 500, 4151, 4, 8, 5082, 5, 500, 5464, 7, 8, 242, 0, 4, 5468, 5, 500, 5470, 7, 8, 490, 0, 4, 5474, 5, 500, 5476, 2, 8, 51, 0, 4, 5480, 5, 500, 5482, 4, 61, 1726, 1, 58, 5486, 4, 61, 1834, 1, 58, 5490, 4, 61, 64, 1, 58, 5494, 5, 8, 500, 1, 2, 5498, 7, 5498, 5500, 0, 500, 5502, 6, 40, 357, 356, 555, 5506, 8, 10, 41, 8, 10, 40, 40, 5510, 5513, 358, 3719, 3726, 9, 266, 358, 267, 3726, 5518, 8, 11, 40, 9, 10, 40, 41, 5522, 5524, 9, 10, 5522, 41, 5522, 5528, 8, 267, 358, 359, 5518, 5532, 9, 266, 5532, 359, 5532, 5536, 8, 38, 1189, 9, 38, 1188, 39, 5540, 5542, 9, 1188, 5540, 39, 5540, 5546, 0, 2467, 3708, 0, 665, 926, 0, 664, 926, 664, 5552, 5555, 0, 664, 927, 1, 5552, 5558, 0, 359, 634, 1, 4964, 5562, 0, 18, 383, 1, 358, 634, 359, 4964, 5568, 635, 5562, 5568, 1, 358, 5562, 635, 5562, 5574, 0, 9, 1348, 1526, 3699, 5578, 1, 8, 312, 4, 6, 5583, 3, 5582, 5584, 7, 312, 5586, 5, 5584, 5588, 5, 312, 5586, 7, 5584, 5592, 3, 242, 1342, 5, 312, 5596, 7, 1342, 5598, 5, 312, 1454, 243, 1336, 5602, 7, 312, 5596, 5, 1342, 5606, 7, 312, 1526, 243, 1348, 5610, 5, 6, 5582, 3, 4, 5582, 7, 312, 5616, 5583, 5614, 5618, 2, 6, 3078, 5, 6, 3078, 522, 5623, 5624, 0, 9, 3078, 5623, 5624, 5628, 2, 4, 19, 1, 14, 5632, 6, 8, 3709, 1, 3008, 5636, 1, 14, 3008, 2, 8, 2853, 6, 216, 2853, 4, 5642, 5644, 9, 60, 216, 4, 6, 5649, 216, 1113, 5650, 4, 7, 930, 102, 932, 5654, 4, 7, 48, 102, 932, 5658, 932, 1030, 1854, 4, 8, 1854, 1, 2, 5664, 1030, 1854, 5666, 4, 8, 2546, 1, 2, 5670, 1030, 2546, 5672, 8, 48, 1854, 1, 2, 5676, 1030, 1854, 5678, 0, 3, 482, 4, 7, 5682, 1, 376, 5684, 8, 482, 5686, 3, 216, 3274, 4, 6, 3274, 927, 5690, 5692, 376, 629, 3070, 376, 629, 4078, 2, 267, 926, 0, 383, 5700, 5, 10, 94, 0, 15, 5704, 2, 629, 926, 0, 2467, 5708, 4, 95, 628, 0, 2470, 5713, 8, 629, 1258, 282, 1259, 5716, 1, 6, + 2740, 9, 2740, 5720, 9, 232, 5720, 1, 6, 3536, 9, 232, 5726, 8, 282, 1259, 0, 7, 5730, 1, 282, 5732, 0, 40, 232, 8, 233, 5736, 6, 232, 5739, 0, 232, 634, 8, 233, 5742, 6, 232, 5745, 0, 232, 290, 8, 233, 5748, 6, 232, 5751, 5, 60, 232, 1, 6, 5754, 9, 232, 5756, 1, 232, 282, 2, 5, 282, 3, 5760, 5762, 2, 8, 395, 4, 9, 394, 11, 5766, 5768, 3, 8, 746, 266, 5351, 5772, 3, 8, 58, 266, 1727, 5776, 3, 8, 2516, 4, 8, 2516, 266, 5780, 5783, 2, 8, 746, 266, 2558, 5787, 266, 1479, 2168, 5, 8, 2516, 266, 2519, 5792, 2, 8, 747, 11, 1298, 5796, 11, 1800, 2572, 2, 9, 746, 4, 8, 747, 11, 5802, 5804, 628, 3071, 3088, 102, 3000, 3088, 4, 94, 628, 628, 3088, 5813, 0, 94, 102, 4, 3071, 5816, 6, 8, 3448, 4, 3071, 5820, 1, 3000, 5820, 5, 94, 628, 1, 3000, 5826, 1, 6, 640, 8, 640, 5830, 8, 640, 644, 0, 7, 644, 8, 644, 5836, 628, 634, 959, 1, 628, 640, 283, 628, 634, 283, 634, 636, 1, 6, 590, 1, 40, 160, 1, 266, 614, 1, 6, 178, 1, 8, 482, 1, 8, 474, 4, 8, 61, 1, 6, 5860, 1, 6, 154, 1, 8, 492, 1, 266, 656, 1, 2, 94, 4, 664, 5871, 1, 3448, 5872, 0, 4, 4733, 3, 102, 5876, 1, 2, 5878, 4732, 5878, 5880, 2, 94, 3288, 664, 3088, 5885, 0, 2, 4733, 2, 103, 5876, 102, 5889, 5890, 4, 927, 2984, 102, 2723, 5894, 4, 628, 3742, 628, 1808, 5899, 0, 3, 3708, 4, 656, 3709, 628, 5903, 5904, 6, 8, 657, 2, 658, 5908, 659, 1808, 5910, 664, 3088, 3275, 664, 3088, 3709, 926, 2863, 3470, 1, 8, 5332, 9, 926, 5920, 7, 926, 3274, 5, 8, 5924, 9, 926, 5926, 5, 926, 3274, 7, 8, 5930, 9, 926, 5932, 2, 905, 926, 1, 8, 5936, 9, 926, 5938, 5, 6, 3208, 0, 7, 5942, 1, 926, 5944, 2, 7, 926, 4, 9, 5948, 0, 5, 5950, 1, 926, 5952, 6, 9, 3470, 0, 7, 5956, 1, 926, 5958, 9, 926, 3470, 0, 7, 5962, 1, 926, 5964, 9, 926, 5948, 0, 5, 5968, 1, 926, 5970, 0, 10, 267, 6, 8, 5975, 10, 441, 5976, 7, 10, 828, 8, 826, 5980, 9, 828, 5982, 7, 10, 836, 8, 834, 5986, 9, 836, 5988, 6, 191, 358, 1, 8, 5992, 787, 5992, 5994, 4, 146, 191, 1, 8, 5998, 807, 5998, 6000, 0, 201, 806, 1, 8, 6004, 807, 6004, 6006, 0, 201, 786, 1, 8, 6010, 787, 6010, 6012, 0, 8, 2083, 9, 1112, 2082, 1, 6016, 6018, 0, 8, 787, 788, 2162, 6022, 3, 10, 376, 6, 8, 6027, 2, 48, 267, 6, 8, 6031, 4, 267, 376, 6, 8, 6035, 1, 630, 5332, 4, 8, 5332, 3, 6, 6040, 1, 5332, 6042, 630, 905, 3996, 8, 358, 376, 3, 490, 6048, 3, 6, 1916, 8, 500, 6052, 1, 1916, 6054, 3, 6, 1730, 2, 501, 1730, 1, 6058, 6060, 6, 624, 3841, 2, 356, 905, 6, 3841, 6066, 51, 3818, 3850, 51, 3944, 4646, 2, 4, 1173, 6, 8, 6074, 0, 1172, 6077, 2, 4, 614, 0, 7, 6080, 9, 614, 6082, 6, 383, 2842, 0, 383, 1172, 8, 266, 2843, 6, 2842, 6091, 92, 383, 4552, 9, 614, 2842, 267, 584, 2842, 6, 9, 2842, 267, 2842, 6100, 2, 310, 927, 9, 354, 6104, 2, 7, 1540, 9, 5360, 6108, 9, 1544, 1638, 1, 4, 1544, 2, 5, 6114, 9, 1544, 6116, 2, 7, 4158, 9, 1158, 6120, 0, 7, 1514, 9, 1638, 6124, 2, 8, 49, 0, 3, 6128, 7, 48, 6130, 9, 6128, 6132, 2, 357, 1030, 3, 312, 6136, 0, 93, 1030, 1, 312, 6140, 0, 217, 6136, 1, 6, 2842, 2, 4, 6147, 8, 2843, 6148, 0, 7, 3708, 8, 3008, 6153, 8, 2843, 6074, 0, 7, 1172, 8, 6074, 6159, 8, 2843, 3008, 5, 6, 1644, 4, 383, 6164, 1, 2, 582, 0, 383, 6168, 0, 383, 432, 2, 4, 394, 6, 8, 6174, 10, 394, 6177, 376, 378, 389, 0, 2, 583, 383, 582, 6182, 60, 383, 1876, 0, 2, 2333, 6, 8, 6188, 4, 2332, 6191, 4, 1964, 1969, 383, 500, 1644, 0, 3, 92, 4, 6, 6199, 8, 92, 6201, 8, 93, 6200, 9, 6202, 6204, 2, 491, 1312, 3, 1568, 6208, 6, 133, 354, 0, 132, 6213, 3, 1568, 6214, 0, 132, 747, 3, 1568, 6218, 4, 8, 354, 0, 7, 6222, 3, 4, 6224, 5, 6, 6222, 355, 6226, 6228, 0, 7, 660, 3, 4, 6232, 355, 5368, 6234, 3, 4, 1160, 355, 1574, 6238, 1, 92, 628, 0, 3, 6242, 4, 6, 6245, 8, 6243, 6246, 2, 8, 3935, 3, 1568, 6250, 3, 4, 490, 2, 8, 6255, 3, 1568, 6256, 8, 282, 358, 2, 4, 6261, 8, 6261, 6262, 6, 9, 432, 61, 660, 6266, 4, 146, 232, 4, 132, 6271, 2, 8, 2743, 3, 290, 6274, 2, 6, 232, 4, 8, 6279, 5, 290, 6280, 233, 660, 6274, 233, 2356, 6274, 1, 6, 2038, 61, 660, 6288, 61, 644, 660, 61, 644, 5398, 41, 282, 358, 7, 1886, 5762, 7, 60, 5762, 2, 5, 1886, 7, 1886, 6302, 5, 358, 1886, 3, 358, 5762, 282, 358, 1259, 2, 41, 1886, 4, 41, 5762, 8, 50, 358, 8, 40, 6317, 2, 51, 132, 3, 40, 6320, 4, 51, 660, 5, 40, 6324, 4, 283, 660, 5, 40, 6328, 2, 102, 283, 3, 40, 6332, 4, 94, 283, 5, 40, 6336, 2, 5368, 6324, 2, 5368, 6328, 3, 4, 2954, 2, 5, 2954, 6, 6344, 6346, 2, 5, 6344, 6, 6344, 6350, 2, 7, 218, 233, 660, 6354, 3, 6, 218, 283, 634, 6358, 0, 383, 5852, 0, 267, 5848, 9, 590, 6364, 0, 7, 5852, 9, 614, 6368, 8, 266, 383, 0, 7, 6372, 6, 383, 6374, 0, 6, 6373, 383, 6372, 6378, 1, 8, 584, 0, 267, 6382, 7, 584, 6384, 0, 9, 5852, 7, 614, 6388, 0, 6, 591, 383, 590, 6392, 0, 7, 6382, 267, 584, 6396, 0, 9, 5848, 267, 590, 6400, 0, 5, 664, 4, 217, 6404, 358, 6404, 6406, 3, 312, 500, 2, 7, 6410, 218, 6410, 6412, 9, 354, 500, 2, 7, 6416, 218, 6416, 6418, 5, 302, 664, 4, 7, 6422, 217, 6422, 6424, 7, 218, 228, 2, 9, 6428, 228, 6428, 6430, 1, 38, 664, 0, 9, 6434, 41, 6434, 6436, 7, 218, 1124, 2, 5168, 6440, 3, 4, 218, 7, 218, 6444, 9, 500, 6444, 2, 6446, 6448, 7, 60, 218, 2, 9, 6452, 500, 6452, 6454, 2, 9, 6446, 500, 6446, 6458, 146, 629, 6198, 146, 629, 3526, 1, 2, 356, 41, 282, 6466, 5, 8, 216, 146, 232, 6471, 146, 232, 3383, 2, 8, 11, 146, 232, 6477, 0, 8, 49, 146, 232, 6481, 2, 4, 615, 7, 266, 614, 8, 6484, 6487, 6, 8, 6484, 267, 6484, 6490, 267, 382, 3008, 267, 3008, 3012, 6, 5518, 5532, 6, 8, 5518, 267, 5518, 6500, 6, 585, 5518, 267, 382, 5518, 8, 591, 6484, 2, 6, 219, 3, 8, 6510, 501, 6510, 6512, 3, 3106, 6510, 2, 218, 3107, 6, 3106, 6519, 8, 505, 6510, 501, 3130, 6510, 3, 6, 3106, 219, 3106, 6526, 219, 3106, 6358, 0, 5, 504, 2, 6, 6533, 8, 505, 6534, 6, 221, 3106, 1, 4, 220, 2, 8, 6541, 6, 221, 6542, 0, 9, 3698, 3, 242, 6546, 3699, 6546, 6548, 9, 660, 1188, 310, 5368, 6552, 9, 356, 656, 40, 660, 6557, 4, 8, 2853, 359, 5518, 6560, 9, 266, 6560, 359, 6560, 6564, 6, 635, 3288, 8, 665, 3288, 6, 6569, 6570, 1, 6, 302, 660, 6354, 6574, 6, 266, 310, 93, 706, 6578, 310, 640, 3174, 310, 640, 1578, 6, 555, 634, 147, 2924, 6586, 9, 2102, 2156, 0, 41, 6590, 41, 134, 6140, 0, 5, 26, 0, 8, 11, 3, 26, 6598, 7, 6596, 6600, 0, 3, 26, 5, 26, 6598, 7, 6604, 6606, 0, 3, 2162, 5, 1090, 2162, 7, 6610, 6612, 3, 1090, 2162, 7, 4564, 6616, 19, 132, 6604, 8, 10, 19, 0, 3, 6622, 19, 146, 6624, 2, 4, 94, 10, 94, 665, 0, 6629, 6630, 10, 656, 665, 0, 3709, 6634, 0, 2, 95, 94, 3005, 6638, 7, 8, 2984, 8, 2984, 6643, 6, 6642, 6645, 2, 9, 3742, 5, 656, 3742, 7, 6648, 6650, 0, 9, 5870, 5, 94, 5870, 7, 6654, 6656, 0, 2, 615, 383, 614, 6660, 267, 590, 5802, 7, 8, 5802, 267, 5802, 6666, 6, 9, 2984, 2985, 6642, 6670, 7, 8, 6670, 2985, 6670, 6674, 7, 8, 1188, 267, 584, 6678, 41, 3930, 3944, 0, 3, 520, 5, 358, 6684, 8, 520, 6686, 41, 196, 432, 0, 3, 1146, 7, 146, 6692, 8, 1146, 6694, 0, 7, 1146, 3, 146, 6698, 8, 1146, 6700, 0, 7, 432, 5, 60, 6704, 8, 432, 6706, 0, 5, 432, 7, 60, 6710, 8, 432, 6712, 0, 5, 520, 3, 358, 6716, 8, 520, 6718, 0, 203, 432, 60, 203, 1876, 0, 3, 358, 656, 3167, 6726, 26, 157, 3944, 0, 10, 283, 2, 6, 661, 3, 6732, 6734, 4, 282, 2869, 0, 2870, 6739, 4, 9, 94, 1, 2, 6742, 4, 282, 6743, 0, 6744, 6747, 1, 2, 2898, 4, 282, 2899, 0, 6750, 6753, 0, 3, 2898, 2, 6753, 6756, 0, 3, 6742, 2, 6747, 6760, 0, 2, 12, 5, 6, 6764, 12, 133, 6766, 7, 8, 2870, 5, 2868, 6770, 0, 2870, 6772, 7, 8, 6744, 5, 6742, 6776, 0, 6744, 6778, 8, 926, 2722, 926, 4662, 6783, 9, 926, 2722, 8, 6783, 6786, 2, 9, 4078, 2, 8, 4078, 8, 6790, 6793, 2, 8, 4079, 3, 6790, 6796, 3, 8, 6790, 4079, 6790, 6800, 4078, 6793, 6796, 0, 9, 3274, 1, 8, 6806, 3275, 6806, 6808, 2723, 4662, 6786, 9, 926, 4662, 2723, 4662, 6814, 2, 218, 283, 6, 218, 661, 6, 6818, 6821, 0, 2, 4801, 1, 6, 6824, 92, 4800, 6825, 8, 6826, 6829, 0, 5, 5480, 3, 6, 6832, 634, 5480, 6834, 2, 8, 283, 0, 5, 6838, 3, 6, 6840, 634, 6838, 6842, 0, 5, 5474, 3, 6, 6846, 634, 5474, 6848, 0, 5, 2954, 3, 6, 6852, 634, 2954, 6854, 4, 9, 358, 0, 2, 6858, 8, 675, 6858, 359, 6860, 6862, 6, 8, 6829, 1, 6824, 6866, 5, 8, 282, 1, 6, 6870, 0, 3, 6872, 2, 6, 6871, 283, 6874, 6876, 0, 7, 6510, 6, 218, 660, 1, 6880, 6882, 242, 302, 2041, 2, 8, 4675, 2, 9, 4674, 665, 6888, 6890, 0, 8, 2471, 0, 9, 2470, 629, 6894, 6896, 9, 664, 4674, 665, 6888, 6900, 9, 628, 2470, 629, 6894, 6904, 6, 8, 1188, 132, 302, 6909, 132, 302, 2629, 132, 302, 5787, 8, 1770, 3955, 6, 48, 94, 94, 1770, 6919, 2, 232, 500, 8, 290, 6923, 4, 93, 1188, 8, 2843, 6926, 2, 500, 635, 8, 644, 6931, 233, 660, 3910, 233, 660, 3106, 4, 233, 1188, 8, 235, 6938, 4, 6, 219, 660, 6510, 6943, 223, 660, 6354, 132, 226, 1677, 4, 6, 218, 132, 221, 6950, 48, 146, 629, 10, 41, 282, 60, 376, 629, 267, 282, 358, 6, 242, 635, 9, 232, 6962, 9, 146, 1188, 232, 1189, 6966, 282, 291, 2734, 8, 41, 746, 282, 747, 6972, 2, 7, 656, 8, 40, 6977, 0, 656, 6979, 8, 146, 1189, 7, 282, 6982, 6, 216, 661, 217, 232, 6986, 9, 232, 6982, 8, 1189, 5762, 7, 282, 6992, 0, 7, 6982, 9, 6982, 6996, 1, 6, 6926, 8, 6926, 7000, 0, 3, 634, 7, 634, 7004, 6, 283, 7006, 1, 628, 7006, 6, 283, 634, 283, 7004, 7012, 2, 7, 282, 0, 6, 7017, 4, 9, 7016, 283, 7018, 7020, 0, 51, 634, 1, 628, 7024, 1, 628, 6926, 8, 644, 7024, 2, 7, 628, 233, 660, 7032, 0, 3, 7012, 283, 7012, 7036, 4, 41, 232, 1, 2, 7040, 8, 40, 7043, 6, 7040, 7045, 7, 266, 310, 6, 266, 311, 0, 9, 7050, 267, 7048, 7052, 7, 242, 634, 242, 282, 635, 243, 7056, 7058, 7, 60, 6870, 282, 747, 7062, 7, 1886, 6870, 282, 747, 7066, 7, 1886, 2558, 282, 747, 7070, 7, 60, 2558, 282, 747, 7074, 267, 2652, 2654, 1, 6, 7078, 7, 2654, 7080, 5, 266, 282, 6, 267, 310, 7, 7084, 7086, 1, 8, 48, 0, 6, 7091, 0, 475, 7090, 1, 7092, 7094, 6, 8, 242, 6, 242, 634, 8, 7099, 7100, 4, 1188, 3887, 6, 2954, 7104, 6, 8, 146, 0, 2, 7109, 0, 9, 7108, 6, 7110, 7113, 0, 9, 4800, 6, 6824, 7117, 3, 634, 1188, 6, 2954, 7120, 6, 634, 7005, 8, 7006, 7124, 6, 9, 7004, 6, 7006, 7129, 6, 2954, 6926, 6, 5480, 7024, 9, 1188, 3874, 6, 2954, 7136, 6, 242, 266, 242, 282, 7141, 9, 656, 2760, 7, 376, 3742, 9, 656, 7146, 1, 94, 146, 0, 7, 7150, 9, 7150, 7152, 0, 41, 972, 9, 656, 7156, 0, 41, 6976, 9, 656, 7160, 0, 94, 147, 146, 629, 7164, 6, 242, 267, + 0, 7, 7168, 9, 7168, 7170, 0, 95, 146, 94, 629, 7174, 6, 8, 7151, 0, 7150, 7179, 0, 41, 656, 7, 40, 656, 9, 7182, 7184, 0, 8, 475, 9, 5858, 7188, 0, 9, 424, 0, 8, 425, 1, 7192, 7194, 1, 40, 232, 9, 298, 7198, 1, 6, 200, 7, 40, 200, 9, 7202, 7204, 268, 614, 629, 0, 9, 2466, 6, 8, 2467, 629, 7210, 7212, 0, 9, 492, 493, 5866, 7216, 1, 8, 7216, 493, 7216, 7220, 0, 8, 267, 584, 629, 7224, 6, 675, 3096, 8, 406, 1091, 1, 406, 3096, 8, 1172, 4552, 8, 406, 520, 6, 523, 3096, 358, 406, 523, 92, 253, 4552, 1, 2546, 3818, 359, 520, 3096, 0, 656, 5909, 0, 629, 656, 8, 282, 629, 6, 232, 629, 9, 232, 656, 0, 2, 4732, 4, 102, 7258, 4732, 5888, 7261, 2, 926, 3275, 6, 1030, 7264, 0, 3274, 7267, 6, 1030, 3402, 0, 3274, 7271, 4, 102, 3402, 0, 3274, 7275, 8, 40, 3402, 0, 3274, 7279, 8, 554, 3402, 0, 3274, 7283, 2, 4078, 7283, 2, 4078, 7279, 2, 4078, 7271, 2, 4078, 7275, 6, 721, 1484, 38, 664, 1189, 6, 572, 608, 50, 135, 500, 491, 500, 664, 40, 94, 1603, 61, 94, 1568, 4, 6, 1835, 61, 94, 7308, 0, 8, 146, 2, 6, 7313, 102, 147, 7314, 359, 972, 1568, 2, 6, 200, 40, 102, 7321, 2, 5, 1602, 2, 8, 1603, 6, 7325, 7326, 4, 6, 200, 40, 94, 7331, 3, 4, 972, 4, 354, 973, 6, 7334, 7337, 8, 92, 644, 92, 5388, 7341, 2, 132, 645, 2, 133, 644, 3, 7344, 7346, 5, 644, 660, 4, 645, 660, 661, 7350, 7352, 3, 132, 644, 133, 7344, 7356, 2, 645, 7356, 133, 7356, 7360, 4, 645, 7350, 661, 7350, 7364, 8, 92, 645, 9, 5388, 7368, 4, 644, 661, 5, 7352, 7372, 2, 9, 50, 5, 6, 7376, 8, 3309, 7378, 51, 2454, 7378, 4, 8, 7378, 51, 7378, 7384, 4, 7, 7376, 6, 2429, 7388, 4, 2429, 7378, 2, 9, 2454, 5, 6, 7394, 51, 2454, 7396, 4, 6, 7377, 2429, 7376, 7400, 6, 132, 660, 5, 40, 660, 3, 4, 132, 6, 132, 7408, 8, 40, 359, 8, 40, 93, 2, 359, 660, 6, 61, 660, 8, 359, 634, 5, 8, 290, 290, 2356, 7422, 8, 232, 290, 3, 4, 7426, 5, 290, 7428, 5, 232, 290, 2, 8, 7432, 3, 290, 7434, 2, 5, 7426, 3, 290, 7438, 290, 2360, 7422, 4, 8, 2360, 5, 290, 7444, 4, 232, 660, 2, 5, 7448, 233, 7448, 7450, 2, 132, 232, 3, 4, 7454, 233, 7454, 7456, 4, 232, 661, 2, 7448, 7461, 2, 133, 232, 4, 7454, 7465 } }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/mux_resynthesis.hpp b/include/mockturtle/algorithms/node_resynthesis/mux_resynthesis.hpp new file mode 100644 index 0000000..96f0835 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/mux_resynthesis.hpp @@ -0,0 +1,114 @@ +/* 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 null.hpp + \brief mux resynthesis for cut rewriting + + \author Jasper Zwartjes + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + + + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace mockturtle +{ + +template +class mux_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + + uint32_t NUM_INPUTS = function.num_vars(); + std::cout << "\nMux resynthesis called with: " << kitty::to_binary(function) + << ", Num_vars: " << NUM_INPUTS << ", Network:\n"; + //mockturtle::print(ntk); + + + + + + std::vector tts(NUM_INPUTS); + std::vector divs; + + for ( auto i = 0u; i < NUM_INPUTS; ++i ) + { + tts[i] = kitty::dynamic_truth_table(NUM_INPUTS); + kitty::create_nth_var( tts[i], i ); + divs.emplace_back( i ); + } + + null_stats st; + mux_resyn engine( st ); + const auto res = engine(function, ~function.construct(), divs.begin(), divs.end(), tts, 10 ); + /* x1 ? (!x2 ? 0 : x3) : (x2 ? 0 : !x3) */ + + muxig_network ntk_after_synthesis; + + if (res == std::nullopt) { + std::cerr << "No solution found during mux resynthesis.\n"; + return; + } + + + // NOTE RES contains the optimal solution and will be decoded to ntk_after_synthesis + decode( ntk_after_synthesis, *res ); + + //std::cout << "After decoding optimal solution:\n"; + //mockturtle::print(ntk_after_synthesis); + + muxig_signal extern_signal; + ntk_after_synthesis.foreach_po([&]( auto signal ) {extern_signal = signal;}); + + topo_view topo{ntk_after_synthesis, extern_signal}; + auto f = cleanup_dangling( topo, ntk_after_synthesis, begin, end).front(); + //std::cout << "After clean up:\n"; + //mockturtle::print(ntk_after_synthesis); + //std::cout << "------:\n"; + + + std::vector pis( NUM_INPUTS, ntk.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + fn(ntk.get_constant(false)); + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/null.hpp b/include/mockturtle/algorithms/node_resynthesis/null.hpp new file mode 100644 index 0000000..be24ab4 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/null.hpp @@ -0,0 +1,61 @@ +/* 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 null.hpp + \brief No resynthesis (as default synthesis engine) + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../../traits.hpp" +#include +#include +#include +#include + +namespace mockturtle +{ + +template +class null_resynthesis +{ +public: + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + (void)ntk; + (void)function; + (void)begin; + (void)end; + (void)fn; + + } +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/shannon.hpp b/include/mockturtle/algorithms/node_resynthesis/shannon.hpp new file mode 100644 index 0000000..d4ed62e --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/shannon.hpp @@ -0,0 +1,100 @@ +/* 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 shannon.hpp + \brief Use Shannon decomposition for resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../../traits.hpp" +#include "../decomposition.hpp" +#include "null.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on Shannon decomposition. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The given truth table will be + * resynthized based on Shanon decomposition. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + + shannon_resynthesis resyn; + auto xag = node_resynthesis( klut, resyn ); + \endverbatim + * + */ +template> +class shannon_resynthesis +{ +public: + shannon_resynthesis( std::optional const& threshold = {}, ResynFn* resyn = nullptr ) + : threshold_( threshold ), + resyn_( resyn ) {} + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + if ( threshold_ ) + { + std::vector vars( function.num_vars() - std::min( *threshold_, function.num_vars() ) ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = shannon_decomposition( ntk, function, vars, std::vector>( begin, end ), *resyn_ ); + fn( f ); + } + else + { + std::vector vars( function.num_vars() ); + std::iota( vars.begin(), vars.end(), 0u ); + const auto f = shannon_decomposition( ntk, function, vars, std::vector>( begin, end ) ); + fn( f ); + } + } + +private: + std::optional threshold_; + ResynFn* resyn_; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp b/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp new file mode 100644 index 0000000..22adf0d --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/sop_factoring.hpp @@ -0,0 +1,453 @@ +/* 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 sop_factoring.hpp + \brief Resynthesis with SOP factoring + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../utils/sop_utils.hpp" +#include "../../utils/stopwatch.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for sop_factoring function. */ +struct sop_factoring_params +{ + /*! \brief Select divisors with a quick algorithm. */ + bool use_quick_factoring{ true }; + + /*! \brief Factoring is also tried for the negated TT. */ + bool try_both_polarities{ true }; + + /*! \brief Factoring considers input and output inverters as additional cost. */ + bool consider_inverter_cost{ false }; +}; + +/*! \brief Resynthesis function based on SOP factoring. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. The method converts a + * given truth table in an ISOP, then factors the ISOP, and + * returns the factored form. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig = ...; + + sop_factoring resyn; + refactoring( aig, resyn ); + \endverbatim + * + */ +template +class sop_factoring +{ +public: + using signal = typename Ntk::signal; + using sop_t = std::vector; + +public: + explicit sop_factoring( sop_factoring_params const& ps = {} ) + : _ps( ps ) {} + +public: + template + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 31 ); + + if ( kitty::is_const0( function ) ) + { + /* constant 0 */ + fn( dest.get_constant( false ) ); + return; + } + else if ( kitty::is_const0( ~function ) ) + { + /* constant 1 */ + fn( dest.get_constant( true ) ); + return; + } + + /* derive ISOP */ + bool negated; + auto cubes = get_isop( function, negated ); + + /* create literal form of SOP */ + sop_t sop = cubes_to_sop( cubes, function.num_vars() ); + + /* derive the factored form */ + signal f = gen_factor_rec( dest, { begin, end }, sop, 2 * function.num_vars() ); + + fn( negated ? !f : f ); + } + + template + void operator()( Ntk& dest, kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 31 ); + + if ( kitty::is_const0( function & ( ~dc ) ) ) + { + /* constant 0 */ + fn( dest.get_constant( false ) ); + return; + } + else if ( kitty::is_const0( ~( function | dc ) ) ) + { + /* constant 1 */ + fn( dest.get_constant( true ) ); + return; + } + + /* derive ISOP */ + bool negated; + auto cubes = get_isop_dc( function, dc, negated ); + + /* create literal form of SOP */ + sop_t sop = cubes_to_sop( cubes, function.num_vars() ); + + /* derive the factored form */ + signal f = gen_factor_rec( dest, { begin, end }, sop, 2 * function.num_vars() ); + + fn( negated ? !f : f ); + } + +private: + std::vector get_isop( kitty::dynamic_truth_table const& function, bool& negated ) const + { + std::vector cubes = kitty::isop( function ); + + if ( _ps.try_both_polarities ) + { + std::vector n_cubes = kitty::isop( ~function ); + + if ( _ps.consider_inverter_cost ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + kitty::cube n_term; + kitty::cube term; + for ( auto const& c : n_cubes ) + { + n_term._mask |= c._mask & ( ~c )._bits; + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + term._mask |= c._mask & ( ~c )._bits; + lit += c.num_literals(); + } + + /* positive cost: cubes + input negations + output negation */ + uint32_t positive_cost = cubes.size() + term.num_literals() + 1; + /* negative cost: cubes + input negations */ + uint32_t negative_cost = n_cubes.size() + n_term.num_literals(); + + if ( negative_cost < positive_cost ) + { + negated = true; + return n_cubes; + } + } + else + { + if ( n_cubes.size() < cubes.size() ) + { + negated = true; + return n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + negated = true; + return n_cubes; + } + } + } + } + + negated = false; + return cubes; + } + + std::vector get_isop_dc( kitty::dynamic_truth_table const& function, kitty::dynamic_truth_table const& dc, bool& negated ) const + { + std::vector cubes; + kitty::detail::isop_rec( function & ~dc, function | dc, function.num_vars(), cubes ); + + if ( _ps.try_both_polarities ) + { + std::vector n_cubes; + kitty::detail::isop_rec( ~function & ~dc, ~function | dc, function.num_vars(), n_cubes ); + + if ( _ps.consider_inverter_cost ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + kitty::cube n_term; + kitty::cube term; + for ( auto const& c : n_cubes ) + { + n_term._mask |= c._mask & ( ~c )._bits; + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + term._mask |= c._mask & ( ~c )._bits; + lit += c.num_literals(); + } + + /* positive cost: cubes + input negations + output negation */ + uint32_t positive_cost = cubes.size() + term.num_literals() + 1; + /* negative cost: cubes + input negations */ + uint32_t negative_cost = n_cubes.size() + n_term.num_literals(); + + if ( negative_cost < positive_cost ) + { + negated = true; + return n_cubes; + } + } + else + { + if ( n_cubes.size() < cubes.size() ) + { + negated = true; + return n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + negated = true; + return n_cubes; + } + } + } + } + + negated = false; + return cubes; + } + +#pragma region SOP factoring + signal gen_factor_rec( Ntk& ntk, std::vector const& children, sop_t& sop, uint32_t const num_lit ) const + { + sop_t divisor, quotient, reminder; + + assert( sop.size() ); + + /* compute the divisor */ + bool success = _ps.use_quick_factoring ? sop_quick_divisor( sop, divisor, num_lit ) : sop_good_divisor( sop, divisor, num_lit ); + if ( !success ) + { + /* generate trivial sop circuit */ + return gen_andor_circuit_rec( ntk, children, sop.begin(), sop.end(), num_lit ); + } + + /* divide the SOP by the divisor */ + sop_divide( sop, divisor, quotient, reminder ); + + assert( quotient.size() > 0 ); + + if ( quotient.size() == 1 ) + { + return lit_factor_rec( ntk, children, sop, quotient[0], num_lit ); + } + sop_make_cube_free( quotient ); + + /* divide the SOP by the quotient */ + sop_divide( sop, quotient, divisor, reminder ); + + if ( sop_is_cube_free( divisor ) ) + { + signal div_s = gen_factor_rec( ntk, children, divisor, num_lit ); + signal quot_s = gen_factor_rec( ntk, children, quotient, num_lit ); + + /* build (D)*(Q) + R */ + signal dq_and = ntk.create_and( div_s, quot_s ); + + if ( reminder.size() ) + { + signal rem_s = gen_factor_rec( ntk, children, reminder, num_lit ); + return ntk.create_or( dq_and, rem_s ); + } + + return dq_and; + } + + /* get the common cube */ + uint64_t cube = UINT64_MAX; + for ( auto const& c : divisor ) + { + cube &= c; + } + + return lit_factor_rec( ntk, children, sop, cube, num_lit ); + } + + signal lit_factor_rec( Ntk& ntk, std::vector const& children, sop_t const& sop, uint64_t const c_sop, uint32_t const num_lit ) const + { + sop_t divisor, quotient, reminder; + + /* extract the best literal */ + detail::sop_best_literal( sop, divisor, c_sop, num_lit ); + + /* divide SOP by the literal */ + sop_divide_by_cube( sop, divisor, quotient, reminder ); + + /* create the divisor: cube */ + signal div_s = gen_and_circuit_rec( ntk, children, divisor[0], 0, num_lit ); + + /* factor the quotient */ + signal quot_s = gen_factor_rec( ntk, children, quotient, num_lit ); + + /* build l*Q + R */ + signal dq_and = ntk.create_and( div_s, quot_s ); + + /* factor the reminder */ + if ( reminder.size() != 0 ) + { + signal rem_s = gen_factor_rec( ntk, children, reminder, num_lit ); + return ntk.create_or( dq_and, rem_s ); + } + + return dq_and; + } +#pragma endregion + +#pragma region Circuit generation from SOP + signal gen_and_circuit_rec( Ntk& ntk, std::vector const& children, uint64_t const cube, uint32_t const begin, uint32_t const end ) const + { + /* count set literals */ + uint32_t num_lit = 0; + uint32_t lit = begin; + uint32_t i; + for ( i = begin; i < end; ++i ) + { + if ( detail::cube_has_lit( cube, i ) ) + { + ++num_lit; + lit = i; + } + } + + assert( num_lit > 0 ); + + if ( num_lit == 1 ) + { + /* return the coprresponding signal with the correct polarity */ + if ( lit % 2 == 1 ) + return children[lit / 2]; + else + return !children[lit / 2]; + } + + /* find splitting point */ + uint32_t count_lit = 0; + for ( i = begin; i < end; ++i ) + { + if ( detail::cube_has_lit( cube, i ) ) + { + if ( count_lit >= num_lit / 2 ) + break; + + ++count_lit; + } + } + + signal tree1 = gen_and_circuit_rec( ntk, children, cube, begin, i ); + signal tree2 = gen_and_circuit_rec( ntk, children, cube, i, end ); + + return ntk.create_and( tree1, tree2 ); + } + + signal gen_andor_circuit_rec( Ntk& ntk, std::vector const& children, sop_t::const_iterator const& begin, sop_t::const_iterator const& end, uint32_t const num_lit ) const + { + auto num_prod = std::distance( begin, end ); + + assert( num_prod > 0 ); + + if ( num_prod == 1 ) + return gen_and_circuit_rec( ntk, children, *begin, 0, num_lit ); + + /* create or tree */ + signal tree1 = gen_andor_circuit_rec( ntk, children, begin, begin + num_prod / 2, num_lit ); + signal tree2 = gen_andor_circuit_rec( ntk, children, begin + num_prod / 2, end, num_lit ); + + return ntk.create_or( tree1, tree2 ); + } +#pragma endregion + +private: + sop_factoring_params const& _ps; + + mutable stopwatch<>::duration time_factoring{}; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/node_resynthesis/traits.hpp b/include/mockturtle/algorithms/node_resynthesis/traits.hpp new file mode 100644 index 0000000..418786c --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/traits.hpp @@ -0,0 +1,92 @@ +/* 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 traits.hpp + \brief Traits for additional node_resynthesis methods + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../../traits.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +#pragma region has_set_bounds +template +struct has_set_bounds : std::false_type +{ +}; + +template +struct has_set_bounds().set_bounds( std::optional(), std::optional() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_bounds_v = has_set_bounds::value; +#pragma endregion + +#pragma region has_clear_functions +template +struct has_clear_functions : std::false_type +{ +}; + +template +struct has_clear_functions().clear_functions() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_functions_v = has_clear_functions::value; +#pragma endregion + +#pragma region has_add_function +template +struct has_add_function : std::false_type +{ +}; + +template +struct has_add_function().add_function( signal(), kitty::dynamic_truth_table() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_function_v = has_add_function::value; +#pragma endregion + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp b/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp new file mode 100644 index 0000000..b51a757 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/xag_minmc.hpp @@ -0,0 +1,472 @@ +/* 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 xag_minmc.hpp + \brief XAG resynthesis + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../networks/xag.hpp" +#include "../../traits.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/cut_view.hpp" +#include "../cleanup.hpp" +#include "../simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xag_minmc_resynthesis. */ +struct xag_minmc_resynthesis_params +{ + /*! \brief Print statistics when resynthesis object is destroyed. */ + bool print_stats{ false }; + + /*! \brief Threshold for exhaustive don't care search. + * + * If the don't care set is smaller than this size, all possible covers with + * respect to the don't cares are explored. Otherwise all covers are created + * that the on-set is extended by at most one element from the don't care set. + */ + uint32_t exhaustive_dc_limit{ 10u }; + + /*! \brief Verify database when parsing. */ + bool verify_database{ false }; +}; + +/*! \brief Statistics for xag_minmc_resynthesis. */ +struct xag_minmc_resynthesis_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time to parse database. */ + stopwatch<>::duration time_parse_db{ 0 }; + + /*! \brief Overall time to classify functions. */ + stopwatch<>::duration time_classify{ 0 }; + + /*! \brief Overall time to construct candidate. */ + stopwatch<>::duration time_construct{ 0 }; + + /*! \brief Cache hits for classified functions. */ + uint32_t cache_hits{ 0 }; + + /*! \brief Cache misses for classified functions. */ + uint32_t cache_misses{ 0 }; + + /*! \brief Number of aborts due to classification. */ + uint32_t classify_aborts{ 0 }; + + /*! \brief Number of aborts due to unknown function. */ + uint32_t unknown_function_aborts{ 0 }; + + /*! \brief Total number of don't cares considered. */ + uint32_t dont_cares{ 0 }; + + /*! \brief Prints report. */ + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] parse db time = {:>5.2f} secs\n", to_seconds( time_parse_db ) ); + std::cout << fmt::format( "[i] classify time = {:>5.2f} secs\n", to_seconds( time_classify ) ); + std::cout << fmt::format( "[i] - aborts = {:>5}\n", classify_aborts ); + std::cout << fmt::format( "[i] construct time = {:>5.2f} secs\n", to_seconds( time_construct ) ); + std::cout << fmt::format( "[i] cache hits = {:>5}\n", cache_hits ); + std::cout << fmt::format( "[i] cache misses = {:>5}\n", cache_misses ); + std::cout << fmt::format( "[i] unknown func. = {:>5}\n", unknown_function_aborts ); + std::cout << fmt::format( "[i] don't cares = {:>5}\n", dont_cares ); + } +}; + +/*! \brief Resynthesis function to minimize multiplicative complexity in XAGs. + * + * This resynthesis function can be passed to ``cut_rewriting`` with a cut size + * of at most 6. It will produce an XAG based on pre-computed XAGs with a + * minimum multiplicative complexity. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const xag_network xag = ...; + xag_minmc_resynthesis resyn; + xag = cut_rewriting( xag, resyn ); + \endverbatim + */ +class xag_minmc_resynthesis +{ +public: + /*! \brief Default constructor. + * + * \param filename Database file with precomputed functions (information to be added) + * \param ps Parameters + * \param pst Statistics + */ + xag_minmc_resynthesis( std::string const& filename, xag_minmc_resynthesis_params const& ps = {}, xag_minmc_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + db( std::make_shared() ), + db_pis( std::make_shared( 6u ) ), + func_mc( std::make_shared() ), + classify_cache( std::make_shared() ) + { + build_db( filename ); + } + + virtual ~xag_minmc_resynthesis() + { + if ( ps.print_stats ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( xag_network& xag, kitty::dynamic_truth_table function, kitty::dynamic_truth_table const& dont_cares, LeavesIterator begin, LeavesIterator end, Fn&& fn ) + { + if ( !kitty::is_const0( dont_cares ) ) + { + const auto cnt = kitty::count_ones( dont_cares ); + st.dont_cares += cnt; + + if ( cnt <= ps.exhaustive_dc_limit ) + { + std::vector ones; + kitty::for_each_one_bit( dont_cares, [&]( auto bit ) { + ones.push_back( bit ); + kitty::clear_bit( function, bit ); + } ); + + for ( auto i = 0u; i < ( 1u << ones.size() ); ++i ) + { + for ( auto j = 0u; j < ones.size(); ++j ) + { + if ( ( i >> j ) & 1 ) + { + kitty::set_bit( function, ones[j] ); + } + else + { + kitty::clear_bit( function, ones[j] ); + } + } + ( *this )( xag, function, begin, end, fn ); + } + } + else + { + ( *this )( xag, function, begin, end, fn ); + kitty::for_each_one_bit( dont_cares, [&]( auto bit ) { + kitty::flip_bit( function, bit ); + ( *this )( xag, function, begin, end, fn ); + kitty::flip_bit( function, bit ); + } ); + } + } + else + { + ( *this )( xag, function, begin, end, fn ); + } + } + + template + void operator()( xag_network& xag, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) + { + stopwatch t1( st.time_total ); + + const auto func_ext = kitty::extend_to<6u>( function ); + std::vector trans; + kitty::static_truth_table<6u> tt_ext; + + const auto cache_it = classify_cache->find( func_ext ); + + if ( cache_it != classify_cache->end() ) + { + st.cache_hits++; + if ( !std::get<0>( cache_it->second ) ) + { + return; /* quit */ + } + tt_ext = std::get<1>( cache_it->second ); + trans = std::get<2>( cache_it->second ); + } + else + { + st.cache_misses++; + const auto spectral = call_with_stopwatch( st.time_classify, + [&]() { return kitty::exact_spectral_canonization_limit( func_ext, 100000, + [&trans]( auto const& ops ) { + std::copy( ops.begin(), ops.end(), + std::back_inserter( trans ) ); + } ); } ); + classify_cache->insert( { func_ext, { spectral.second, spectral.first, trans } } ); + if ( !spectral.second ) + { + st.classify_aborts++; + return; /* quit */ + } + tt_ext = spectral.first; + } + + xag_network::signal circuit; + + auto search = func_mc->find( kitty::to_hex( tt_ext ) ); + if ( search != func_mc->end() ) + { + unsigned int mc{ 0u }; + std::string original_f; + + std::tie( original_f, mc, circuit ) = search->second; + + kitty::static_truth_table<6u> db_repr; + kitty::create_from_hex_string( db_repr, original_f ); + + call_with_stopwatch( st.time_classify, [&]() { return kitty::exact_spectral_canonization( + db_repr, [&trans]( auto const& ops ) { + std::copy( ops.rbegin(), ops.rend(), + std::back_inserter( trans ) ); + } ); } ); + } + else if ( kitty::is_const0( tt_ext ) ) + { + circuit = db->get_constant( false ); + } + else + { + // std::cout << "[w] unknown " << kitty::to_hex( tt_ext ) << " from " << kitty::to_hex( func_ext ) << "\n"; + st.unknown_function_aborts++; + return; /* quit */ + } + + bool out_neg{ false }; + std::vector final_xor; + std::vector pis( 6, xag.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + stopwatch t2( st.time_construct ); + for ( auto const& t : trans ) + { + switch ( t._kind ) + { + default: + assert( false ); + case kitty::detail::spectral_operation::kind::permutation: + { + const auto v1 = log2( t._var1 ); + const auto v2 = log2( t._var2 ); + std::swap( pis[v1], pis[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::input_negation: + { + const auto v1 = log2( t._var1 ); + pis[v1] = !pis[v1]; + } + break; + case kitty::detail::spectral_operation::kind::output_negation: + out_neg = !out_neg; + break; + case kitty::detail::spectral_operation::kind::spectral_translation: + { + const auto v1 = log2( t._var1 ); + const auto v2 = log2( t._var2 ); + pis[v1] = xag.create_xor( pis[v1], pis[v2] ); + } + break; + case kitty::detail::spectral_operation::kind::disjoint_translation: + { + const auto v1 = log2( t._var1 ); + final_xor.push_back( pis[v1] ); + } + break; + } + } + + xag_network::signal output; + + if ( db->is_constant( db->get_node( circuit ) ) ) + { + output = xag.get_constant( db->is_complemented( circuit ) ); + } + else + { + cut_view topo{ *db, *db_pis, circuit }; + output = cleanup_dangling( topo, xag, pis.begin(), pis.end() ).front(); + } + + for ( auto const& g : final_xor ) + { + output = xag.create_xor( output, g ); + } + + fn( out_neg ? !output : output ); + } + +private: + void build_db( std::string const& filename ) + { + stopwatch t1( st.time_total ); + stopwatch t2( st.time_parse_db ); + + std::generate( db_pis->begin(), db_pis->end(), [&]() { return db->create_pi(); } ); + + std::ifstream file1( filename.c_str(), std::ifstream::in ); + std::string line; + unsigned pos{ 0u }; + + // std::ofstream db_file( "/tmp/db", std::ofstream::out ); + + while ( std::getline( file1, line ) ) + { + pos = static_cast( line.find( '\t' ) ); + const auto name = line.substr( 0, pos++ ); + auto original = line.substr( pos, 16u ); + pos += 17u; + const auto token_f = line.substr( pos, 16u ); + pos += 17u; + auto mc = std::stoul( line.substr( pos, 1u ) ); + pos += 2u; + line.erase( 0, pos ); + + auto circuit = line; + // auto orig_circuit = circuit; + + const std::string delimiter = " "; + std::string token = circuit.substr( 0, circuit.find( ' ' ) ); + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + const auto inputs = std::stoul( token ); + + std::vector hashing_circ( db_pis->begin(), db_pis->begin() + inputs ); + + while ( circuit.size() > 4 ) + { + std::array signals; + std::vector ff( 2 ); + for ( auto j = 0u; j < 2u; j++ ) + { + token = circuit.substr( 0, circuit.find( ' ' ) ); + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + signals[j] = std::stoul( token ); + if ( signals[j] == 0 ) + { + ff[j] = db->get_constant( false ); + } + else if ( signals[j] == 1 ) + { + ff[j] = db->get_constant( true ); + } + else + { + ff[j] = hashing_circ[signals[j] / 2 - 1] ^ ( signals[j] % 2 != 0 ); + } + } + circuit.erase( 0, circuit.find( ' ' ) + 1 ); + + if ( signals[0] > signals[1] ) + { + hashing_circ.push_back( db->create_xor( ff[0], ff[1] ) ); + } + else + { + hashing_circ.push_back( db->create_and( ff[0], ff[1] ) ); + } + } + + const auto output = std::stoul( circuit ); + const auto f = hashing_circ[output / 2 - 1] ^ ( output % 2 != 0 ); + db->create_po( f ); + + /* verify */ + if ( ps.verify_database ) + { + cut_view view{ *db, *db_pis, f }; + kitty::static_truth_table<6u> tt, tt_repr; + kitty::create_from_hex_string( tt, original ); + kitty::create_from_hex_string( tt_repr, token_f ); + auto result = simulate>( view )[0]; + if ( tt != result ) + { + std::cerr << "[w] invalid circuit for " << original << ", got " << kitty::to_hex( result ) << "\n"; + original = kitty::to_hex( result ); + + const auto repr = exact_spectral_canonization( tt ); + if ( repr != tt_repr ) + { + std::cerr << "[e] representatives do not match\n"; + } + } + + // db_file << name << "\t" << token_f << "\t" << original << "\t" << mc << "\t" << orig_circuit << "\n"; + } + + func_mc->insert( { token_f, { original, mc, f } } ); + } + } + +public: + xag_minmc_resynthesis_params ps; + xag_minmc_resynthesis_stats st; + +private: + xag_minmc_resynthesis_stats* pst{ nullptr }; + + std::shared_ptr db; + std::shared_ptr> db_pis; + std::shared_ptr>> func_mc; + std::shared_ptr, std::tuple, std::vector>, kitty::hash>>> classify_cache; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp b/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp new file mode 100644 index 0000000..4c30e46 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp @@ -0,0 +1,172 @@ +/* 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 xag_minmc2.hpp + \brief XAG resynthesis + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../../networks/xag.hpp" +#include "../../utils/index_list.hpp" +#include "../detail/minmc_xags.hpp" +#include "../equivalence_classes.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle::future +{ + +struct xag_minmc_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xag_minmc_resynthesis_stats +{ + /*! \brief Database size in bytes. */ + uint64_t db_size{}; +}; + +template +class xag_minmc_resynthesis +{ +public: + xag_minmc_resynthesis() + { + build_db(); + } + + void load_from_file( std::string const& filename ) + { + if ( ps_.verbose ) + { + fmt::print( "start loading\n" ); + } + kitty::dynamic_truth_table func( 6u ); + std::ifstream in( filename, std::ifstream::in ); + std::string line; + while ( std::getline( in, line ) ) + { + const auto vline = lorina::detail::split( line, " " ); + kitty::create_from_hex_string( func, vline[0] ); + const auto sindexes = lorina::detail::split( vline[3], "," ); + std::vector index_list( sindexes.size() ); + std::transform( sindexes.begin(), sindexes.end(), index_list.begin(), [&]( std::string const& s ) { return static_cast( std::stoul( s ) ); } ); + db_[6u][*func.cbegin()] = index_list; + st_.db_size += sizeof( uint64_t ) + sizeof( sindexes ) + sizeof( uint32_t ) * sindexes.size(); + } + if ( ps_.verbose ) + { + fmt::print( "done loading, size = {:>5.2f} Kb\n", st_.db_size / 1024.0f ); + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + const auto num_vars = function.num_vars(); + uint64_t repr; + std::vector trans; + + if ( const auto itCache = cache_[num_vars].find( *function.cbegin() ); itCache != cache_[num_vars].end() ) + { + repr = itCache->second.first; + trans = itCache->second.second; + } + else + { + repr = *kitty::hybrid_exact_spectral_canonization( function, [&]( auto const& ops ) { trans = ops; } ).cbegin(); + cache_[num_vars][*function.cbegin()] = { repr, trans }; + } + + const auto it = db_[num_vars].find( repr ); + if ( it == db_[num_vars].end() ) + { + fmt::print( "[w] cannot find repr {:x} in database.\n", repr ); + return; + } + + const auto f = apply_spectral_transformations( ntk, trans, std::vector>( begin, end ), [&]( xag_network& ntk, std::vector> const& leaves ) { + xag_index_list il{ it->second }; + std::vector pos; + insert( ntk, std::begin( leaves ), std::begin( leaves ) + il.num_pis(), il, + [&]( xag_network::signal const& f ) { + pos.push_back( f ); + } ); + assert( pos.size() == 1u ); + return pos[0u]; + } ); + + fn( f ); + } + +private: + void build_db() + { + st_.db_size += sizeof( db_ ); + + for ( auto i = 0u; i < detail::minmc_xags.size(); ++i ) + { + for ( auto const& [_, word, repr, expr] : detail::minmc_xags[i] ) + { + (void)_; + (void)expr; + db_[i][word] = repr; + st_.db_size += sizeof( word ) + sizeof( repr ) + sizeof( uint32_t ) * repr.size(); + } + } + + if ( ps_.verbose ) + { + fmt::print( "[i] db size = {:>5.2f} Kb\n", st_.db_size / 1024.0f ); + } + } + +private: + std::vector>> db_{ 7u }; + mutable std::vector>>> cache_{ 7u }; + +private: + xag_minmc_resynthesis_params ps_; + xag_minmc_resynthesis_stats st_; +}; + +} // namespace mockturtle::future \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp b/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp new file mode 100644 index 0000000..36c89c0 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp @@ -0,0 +1,691 @@ +/* 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 xag_npn.hpp + \brief Replace with size-optimum XAGs and AIGs from NPN (from ABC rewrite) + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../algorithms/simulation.hpp" +#include "../../networks/xag.hpp" +#include "../../utils/index_list.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" + +namespace mockturtle +{ + +struct xag_npn_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xag_npn_resynthesis_stats +{ + stopwatch<>::duration time_classes{ 0 }; + stopwatch<>::duration time_db{ 0 }; + + uint32_t db_size; + uint32_t covered_classes; + + void report() const + { + std::cout << fmt::format( "[i] build classes time = {:>5.2f} secs\n", to_seconds( time_classes ) ); + std::cout << fmt::format( "[i] build db time = {:>5.2f} secs\n", to_seconds( time_db ) ); + } +}; + +enum class xag_npn_db_kind : uint32_t +{ + xag_incomplete = 0, + xag_complete = 1, + aig_complete = 2, +}; + +/*! \brief Resynthesis function based on pre-computed AIGs. + * + * This resynthesis function can be passed to ``cut_rewriting``. It will + * produce a network based on pre-computed XAGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + xag_npn_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + + .. note:: + + The implementation of this algorithm was heavily inspired by the rewrite + command in AIG. It uses the same underlying database of subcircuits. + \endverbatim + */ +template +class xag_npn_resynthesis +{ +public: + xag_npn_resynthesis( xag_npn_resynthesis_params const& ps = {}, xag_npn_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + _repr( 1u << 16u ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "DatabaseNtk is not a network type" ); + static_assert( has_get_node_v, "DatabaseNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "DatabaseNtk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "DatabaseNtk does not implement the is_xor method" ); + static_assert( has_size_v, "DatabaseNtk does not implement the size method" ); + static_assert( has_create_pi_v, "DatabaseNtk does not implement the create_pi method" ); + static_assert( has_create_and_v, "DatabaseNtk does not implement the create_and method" ); + static_assert( has_create_xor_v, "DatabaseNtk does not implement the create_xor method" ); + static_assert( has_foreach_fanin_v, "DatabaseNtk does not implement the foreach_fanin method" ); + static_assert( has_foreach_node_v, "DatabaseNtk does not implement the foreach_node method" ); + static_assert( has_make_signal_v, "DatabaseNtk does not implement the make_signal method" ); + + build_classes(); + build_db(); + } + + virtual ~xag_npn_resynthesis() + { + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + kitty::static_truth_table<4u> tt = kitty::extend_to<4u>( function ); + + /* get representative of function */ + const auto [repr, phase, perm] = _repr[*tt.cbegin()]; + + /* check if representative has circuits */ + const auto it = _repr_to_signal.find( repr ); + if ( it == _repr_to_signal.end() ) + { + return; + } + + std::vector> pis( 4, ntk.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::unordered_map, signal> db_to_ntk; + db_to_ntk.insert( { 0, ntk.get_constant( false ) } ); + for ( auto i = 0; i < 4; ++i ) + { + db_to_ntk.insert( { i + 1, ( phase >> perm[i] & 1 ) ? ntk.create_not( pis[perm[i]] ) : pis[perm[i]] } ); + } + + for ( auto const& cand : it->second ) + { + const auto f = copy_db_entry( ntk, _db.get_node( cand ), db_to_ntk ); + if ( !fn( _db.is_complemented( cand ) != ( phase >> 4 & 1 ) ? ntk.create_not( f ) : f ) ) + { + return; + } + } + } + +private: + signal + copy_db_entry( Ntk& ntk, node const& n, std::unordered_map, signal>& db_to_ntk ) const + { + if ( const auto it = db_to_ntk.find( n ); it != db_to_ntk.end() ) + { + return it->second; + } + + std::array, 2> fanin{}; + _db.foreach_fanin( n, [&]( auto const& f, auto i ) { + const auto ntk_f = copy_db_entry( ntk, _db.get_node( f ), db_to_ntk ); + fanin[i] = _db.is_complemented( f ) ? ntk.create_not( ntk_f ) : ntk_f; + } ); + + const auto f = _db.is_xor( n ) ? ntk.create_xor( fanin[0], fanin[1] ) : ntk.create_and( fanin[0], fanin[1] ); + db_to_ntk.insert( { n, f } ); + return f; + } + + void build_classes() + { + stopwatch t( st.time_classes ); + + kitty::static_truth_table<4u> tt; + do + { + _repr[*tt.cbegin()] = kitty::exact_npn_canonization( tt ); + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + } + + void build_db() + { + stopwatch t( st.time_db ); + + if constexpr ( DBKind == xag_npn_db_kind::xag_incomplete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs, subgraphs + sizeof subgraphs / sizeof subgraphs[0] } } ); + } + else if constexpr ( DBKind == xag_npn_db_kind::xag_complete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs_xag, subgraphs_xag + sizeof subgraphs_xag / sizeof subgraphs_xag[0] } } ); + } + else if constexpr ( DBKind == xag_npn_db_kind::aig_complete ) + { + decode( _db, xag_index_list{ std::vector{ subgraphs_aig, subgraphs_aig + sizeof subgraphs_aig / sizeof subgraphs_aig[0] } } ); + } + const auto sim_res = simulate_nodes>( _db ); + + _db.foreach_node( [&]( auto n ) { + if ( std::get<0>( _repr[*sim_res[n].cbegin()] ) == sim_res[n] ) + { + if ( _repr_to_signal.count( sim_res[n] ) == 0 ) + { + _repr_to_signal.insert( { sim_res[n], { _db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[sim_res[n]].push_back( _db.make_signal( n ) ); + } + } + else + { + const auto f = ~sim_res[n]; + if ( std::get<0>( _repr[*f.cbegin()] ) == f ) + { + if ( _repr_to_signal.count( f ) == 0 ) + { + _repr_to_signal.insert( { f, { !_db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[f].push_back( !_db.make_signal( n ) ); + } + } + } + } ); + + st.db_size = _db.size(); + st.covered_classes = static_cast( _repr_to_signal.size() ); + } + + xag_npn_resynthesis_params ps; + xag_npn_resynthesis_stats st; + xag_npn_resynthesis_stats* pst{ nullptr }; + + std::vector, uint32_t, std::vector>> _repr; + std::unordered_map, std::vector>, kitty::hash>> _repr_to_signal; + + DatabaseNtk _db; + + // clang-format off + /* complete XAG database */ + inline static const uint32_t subgraphs_xag[] = { 38460932, 6, 4, 10, 8, + 8, 6, 3, 5, 16, 14, 3, 10, 20, 8, 22, 6, 2, 6, 4, 27, 8, 2, 30, 28, + 7, 8, 2, 35, 4, 37, 6, 9, 40, 2, 42, 38, 4, 2, 7, 46, 17, 49, 50, 8, + 4, 8, 54, 6, 3, 56, 5, 7, 60, 8, 62, 58, 3, 8, 66, 4, 8, 68, 4, 71, + 7, 69, 74, 72, 76, 2, 2, 4, 46, 8, 6, 83, 81, 85, 86, 8, 15, 83, 90, + 80, 4, 15, 2, 95, 96, 14, 98, 54, 4, 6, 3, 102, 8, 105, 106, 2, 108, + 60, 30, 4, 2, 112, 114, 6, 114, 8, 116, 118, 120, 112, 6, 54, 54, 2, + 5, 6, 128, 126, 125, 130, 6, 81, 8, 135, 136, 2, 138, 4, 2, 8, 4, + 142, 6, 145, 146, 4, 148, 2, 6, 2, 152, 4, 6, 31, 154, 157, 7, 55, + 3, 9, 162, 160, 46, 6, 8, 167, 166, 4, 47, 171, 169, 173, 8, 4, 47, + 153, 177, 179, 180, 152, 9, 167, 2, 177, 7, 187, 188, 8, 31, 177, 7, + 193, 194, 8, 7, 145, 198, 8, 6, 67, 11, 203, 9, 10, 206, 2, 208, + 204, 16, 6, 9, 212, 7, 81, 216, 214, 6, 8, 4, 152, 222, 162, 221, + 225, 166, 8, 81, 213, 16, 231, 9, 230, 234, 232, 9, 216, 10, 152, + 14, 241, 4, 31, 7, 163, 246, 8, 245, 249, 14, 46, 14, 2, 254, 4, 7, + 257, 2, 256, 14, 261, 259, 263, 8, 17, 135, 267, 2, 221, 4, 270, + 272, 6, 274, 8, 48, 8, 66, 28, 221, 281, 7, 9, 284, 2, 286, 4, 221, + 288, 3, 6, 267, 293, 2, 7, 8, 297, 298, 2, 298, 4, 300, 302, 304, 6, + 5, 153, 9, 309, 310, 60, 142, 4, 162, 6, 314, 317, 318, 8, 6, 80, 9, + 323, 5, 27, 326, 2, 328, 6, 8, 328, 331, 333, 9, 153, 14, 154, 337, + 339, 2, 15, 4, 343, 344, 2, 346, 6, 46, 284, 9, 223, 7, 154, 354, + 352, 2, 9, 7, 359, 360, 8, 11, 363, 364, 358, 296, 4, 7, 368, 368, + 2, 9, 373, 371, 375, 6, 176, 359, 379, 380, 4, 154, 221, 16, 8, 231, + 387, 8, 103, 390, 4, 2, 14, 394, 392, 8, 217, 17, 399, 400, 216, + 346, 220, 7, 31, 15, 407, 113, 409, 162, 4, 7, 413, 414, 2, 8, 415, + 416, 419, 216, 8, 10, 2, 7, 424, 2, 11, 9, 429, 430, 426, 7, 113, + 434, 8, 4, 9, 6, 439, 154, 441, 442, 8, 80, 6, 47, 447, 8, 449, 450, + 446, 5, 9, 253, 455, 4, 163, 3, 7, 460, 458, 221, 463, 9, 155, 466, + 354, 3, 11, 470, 8, 55, 473, 5, 284, 4, 285, 478, 2, 221, 480, 477, + 483, 15, 461, 14, 4, 488, 2, 487, 491, 2, 63, 494, 60, 255, 489, + 221, 499, 358, 6, 5, 503, 504, 8, 8, 152, 3, 509, 510, 176, 309, + 512, 498, 6, 2, 10, 518, 6, 520, 8, 10, 521, 522, 525, 14, 223, 2, + 103, 9, 531, 532, 60, 8, 61, 47, 537, 11, 14, 163, 541, 4, 14, 544, + 6, 10, 461, 55, 549, 544, 360, 6, 55, 554, 4, 14, 176, 11, 221, 163, + 560, 562, 8, 14, 67, 566, 326, 26, 4, 9, 570, 572, 60, 9, 254, 14, + 257, 577, 579, 8, 47, 11, 583, 584, 2, 7, 176, 176, 2, 9, 590, 589, + 593, 102, 8, 2, 596, 598, 4, 600, 6, 9, 103, 438, 6, 4, 7, 359, 609, + 610, 606, 6, 571, 8, 615, 616, 570, 11, 30, 620, 6, 622, 2, 3, 4, 8, + 609, 627, 629, 630, 6, 5, 293, 8, 635, 2, 637, 638, 634, 9, 425, + 642, 60, 358, 4, 646, 6, 8, 646, 649, 651, 30, 176, 5, 655, 656, 30, + 7, 658, 660, 654, 27, 537, 608, 8, 359, 667, 668, 6, 3, 221, 5, 672, + 285, 675, 9, 152, 61, 679, 14, 583, 14, 30, 176, 684, 28, 2, 9, 689, + 61, 163, 8, 10, 103, 695, 27, 425, 14, 699, 394, 6, 545, 703, 5, + 246, 706, 8, 103, 709, 6, 47, 9, 713, 162, 60, 390, 6, 718, 4, 6, + 359, 5, 723, 724, 8, 7, 16, 728, 8, 674, 8, 14, 47, 734, 6, 3, 176, + 6, 738, 8, 739, 741, 743, 471, 537, 746, 60, 470, 6, 11, 751, 473, + 753, 293, 387, 30, 537, 8, 247, 4, 761, 762, 246, 3, 14, 8, 767, 5, + 14, 770, 766, 769, 773, 4, 153, 3, 777, 778, 4, 8, 780, 776, 6, 784, + 782, 21, 267, 5, 285, 2, 791, 792, 284, 221, 795, 143, 771, 798, 2, + 152, 771, 221, 803, 46, 607, 806, 8, 766, 8, 488, 811, 7, 387, 9, + 47, 816, 814, 102, 2, 596, 821, 822, 60, 387, 713, 5, 30, 8, 829, + 30, 6, 832, 4, 831, 834, 230, 8, 143, 785, 387, 447, 30, 61, 293, + 845, 846, 2, 267, 447, 7, 177, 163, 853, 854, 6, 240, 8, 60, 2, 63, + 860, 55, 620, 3, 285, 5, 867, 868, 2, 221, 871, 14, 654, 874, 8, 4, + 143, 878, 2, 878, 6, 881, 883, 884, 8, 267, 713, 10, 30, 176, 424, + 891, 893, 163, 556, 5, 766, 898, 8, 9, 240, 9, 179, 102, 30, 537, + 906, 46, 441, 910, 8, 460, 4, 7, 915, 914, 2, 9, 918, 917, 921, 8, + 471, 103, 925, 212, 4, 267, 928, 167, 387, 292, 4, 157, 935, 936, + 30, 46, 221, 394, 4, 11, 943, 16, 284, 296, 60, 15, 297, 949, 951, + 176, 6, 743, 954, 15, 163, 11, 959, 3, 60, 962, 604, 9, 17, 966, + 216, 588, 162, 63, 820, 972, 60, 394, 8, 4, 977, 978, 14, 284, 673, + 982, 674, 8, 323, 986, 154, 766, 4, 221, 991, 5, 470, 994, 206, 6, + 187, 4, 67, 1000, 998, 6, 17, 9, 1005, 9, 820, 61, 1009, 386, 6, 81, + 1013, 1005, 1014, 15, 31, 47, 1019, 1020, 6, 2, 61, 1024, 4, 1026, + 6, 9, 1029, 152, 8, 6, 1032, 152, 439, 1036, 4, 1035, 1038, 2, 102, + 9, 1043, 1044, 60, 7, 591, 1048, 8, 739, 1051, 6, 155, 3, 155, 1056, + 8, 1055, 1059, 4, 359, 1062, 2, 7, 1064, 1066, 8, 503, 647, 1070, 8, + 61, 143, 1074, 2, 518, 4, 9, 1079, 460, 8, 47, 1083, 674, 284, 3, + 177, 1088, 6, 1032, 1091, 285, 673, 4, 1095, 1096, 672, 28, 8, 1100, + 6, 689, 1102, 9, 519, 1106, 60, 9, 60, 9, 739, 1112, 188, 155, 267, + 7, 17, 1118, 4, 9, 1121, 503, 591, 1124, 8, 9, 213, 296, 8, 4, 1131, + 359, 1133, 1134, 6, 9, 81, 424, 1138, 634, 2, 221, 1143, 530, 6, 9, + 1146, 61, 1149, 6, 46, 9, 1153, 81, 1154, 187, 424, 1158, 8, 9, 61, + 1162, 6, 46, 1165, 21, 387, 2, 853, 1170, 4, 221, 1173, 2, 455, + 1176, 10, 221, 1178, 13, 18, 24, 32, 45, 52, 65, 79, 89, 93, 101, + 110, 123, 132, 141, 150, 158, 164, 174, 182, 184, 191, 197, 201, + 211, 218, 226, 228, 236, 238, 242, 250, 252, 265, 268, 276, 279, + 282, 290, 294, 307, 312, 321, 324, 334, 341, 348, 350, 356, 366, + 377, 382, 384, 388, 397, 402, 404, 410, 420, 338, 423, 432, 437, 14, + 445, 452, 457, 464, 468, 474, 485, 492, 496, 500, 507, 514, 517, + 526, 528, 534, 63, 538, 543, 547, 550, 552, 557, 558, 565, 568, 574, + 581, 587, 595, 602, 604, 612, 206, 618, 624, 633, 640, 644, 652, + 662, 664, 671, 677, 681, 682, 686, 690, 693, 696, 700, 704, 710, + 714, 716, 720, 727, 731, 9, 733, 737, 744, 748, 754, 756, 758, 764, + 774, 787, 788, 796, 801, 804, 809, 812, 818, 824, 826, 836, 839, + 840, 842, 848, 850, 856, 859, 862, 864, 872, 877, 884, 887, 888, + 895, 897, 901, 902, 904, 908, 913, 923, 926, 930, 932, 938, 940, + 944, 946, 952, 956, 960, 964, 968, 970, 974, 980, 984, 989, 992, + 996, 1003, 1006, 1011, 1016, 1023, 1030, 1040, 1046, 1052, 1060, + 1069, 1073, 1077, 1080, 1084, 1086, 1092, 1098, 1104, 1108, 284, + 1110, 1114, 8, 1116, 1122, 1127, 1128, 1136, 1140, 1144, 1151, 1156, + 1161, 1166, 1168, 1174, 1180 }; + + /* complete AIG database */ + inline static const uint32_t subgraphs_aig [] = { 52223492, 4, 7, 5, + 6, 11, 13, 8, 14, 9, 15, 17, 19, 3, 5, 6, 9, 7, 8, 25, 27, 23, 29, + 22, 28, 31, 33, 3, 4, 2, 6, 37, 39, 8, 40, 9, 41, 43, 45, 2, 5, 2, + 7, 4, 51, 49, 53, 8, 55, 9, 54, 57, 59, 2, 26, 25, 63, 2, 63, 5, + 67, 65, 68, 64, 69, 71, 73, 2, 4, 6, 23, 77, 79, 8, 80, 9, 81, 83, + 85, 5, 39, 8, 88, 2, 8, 6, 39, 93, 95, 89, 96, 91, 99, 4, 8, 2, + 103, 13, 104, 13, 27, 3, 109, 107, 111, 8, 76, 6, 22, 77, 117, 27, + 118, 115, 121, 7, 9, 22, 125, 6, 8, 23, 129, 76, 125, 130, 133, + 127, 135, 5, 8, 3, 139, 93, 141, 4, 141, 7, 145, 142, 146, 143, + 147, 149, 151, 5, 7, 8, 155, 9, 154, 157, 159, 2, 161, 4, 6, 161, + 165, 3, 167, 163, 169, 2, 9, 4, 173, 3, 8, 7, 176, 174, 179, 39, + 179, 5, 183, 181, 185, 4, 9, 155, 189, 2, 190, 165, 190, 3, 195, + 193, 197, 23, 77, 6, 77, 8, 203, 200, 205, 201, 204, 207, 209, 7, + 23, 77, 212, 9, 76, 23, 217, 6, 219, 215, 221, 23, 25, 77, 129, + 224, 226, 225, 227, 229, 231, 3, 9, 7, 103, 235, 236, 234, 237, + 239, 241, 23, 115, 7, 245, 8, 23, 77, 249, 6, 251, 247, 253, 23, + 125, 227, 257, 226, 256, 259, 261, 6, 200, 7, 201, 265, 267, 9, + 269, 49, 177, 7, 273, 25, 275, 7, 173, 4, 177, 278, 281, 25, 283, + 7, 77, 8, 287, 125, 289, 9, 165, 155, 293, 3, 295, 8, 164, 2, 294, + 299, 301, 297, 302, 4, 50, 5, 234, 307, 309, 129, 310, 11, 23, + 177, 315, 129, 317, 37, 49, 6, 320, 7, 321, 323, 325, 8, 326, 9, + 327, 329, 331, 9, 77, 7, 334, 23, 337, 129, 339, 9, 286, 26, 77, + 9, 23, 6, 346, 345, 349, 4, 234, 7, 139, 2, 354, 353, 357, 129, + 358, 77, 256, 129, 362, 2, 124, 4, 366, 3, 125, 4, 371, 367, 373, + 129, 374, 369, 377, 203, 249, 6, 76, 343, 383, 289, 384, 8, 325, + 9, 324, 389, 391, 8, 11, 51, 394, 51, 235, 4, 399, 397, 401, 367, + 371, 4, 405, 5, 404, 407, 409, 129, 411, 3, 6, 249, 415, 5, 176, + 4, 129, 2, 420, 419, 423, 6, 424, 7, 425, 427, 429, 5, 9, 2, 432, + 157, 435, 3, 7, 7, 439, 2, 441, 4, 443, 439, 444, 8, 445, 441, + 448, 447, 451, 9, 383, 24, 37, 325, 457, 9, 37, 11, 461, 50, 463, + 51, 462, 465, 467, 88, 439, 173, 439, 4, 473, 471, 475, 50, 139, + 139, 189, 3, 481, 479, 483, 129, 484, 155, 235, 293, 488, 292, + 489, 491, 493, 6, 321, 9, 51, 320, 499, 497, 501, 5, 172, 129, + 505, 4, 278, 506, 509, 7, 200, 24, 201, 513, 515, 7, 22, 347, 519, + 337, 521, 5, 399, 398, 420, 525, 527, 22, 286, 287, 346, 531, 533, + 3, 129, 4, 536, 2, 128, 4, 125, 541, 543, 537, 544, 539, 547, 8, + 77, 212, 551, 9, 201, 213, 555, 553, 557, 154, 370, 9, 371, 155, + 562, 561, 565, 24, 320, 26, 321, 569, 571, 8, 286, 9, 287, 575, + 577, 2, 542, 3, 543, 7, 583, 4, 543, 9, 587, 585, 589, 581, 591, + 25, 325, 125, 129, 9, 212, 8, 213, 77, 601, 598, 602, 599, 603, + 605, 607, 7, 93, 4, 235, 611, 612, 610, 613, 615, 617, 129, 618, + 5, 370, 7, 623, 8, 625, 373, 627, 3, 236, 129, 631, 77, 632, 125, + 201, 129, 637, 3, 154, 8, 641, 155, 165, 234, 645, 643, 647, 5, + 124, 3, 651, 542, 653, 543, 652, 129, 657, 655, 658, 4, 370, 5, + 367, 371, 664, 663, 667, 129, 669, 173, 641, 3, 124, 5, 674, 581, + 677, 129, 678, 9, 49, 155, 682, 154, 683, 685, 687, 248, 286, 22, + 287, 76, 249, 693, 695, 691, 696, 9, 22, 265, 701, 115, 702, 334, + 415, 157, 707, 6, 460, 4, 37, 7, 713, 8, 714, 711, 717, 2, 165, 9, + 721, 155, 722, 154, 723, 725, 727, 8, 154, 9, 155, 731, 733, 5, + 129, 2, 736, 173, 737, 739, 741, 165, 172, 157, 745, 13, 103, 51, + 188, 155, 751, 9, 50, 748, 755, 4, 24, 155, 759, 7, 138, 759, 763, + 2, 645, 9, 767, 731, 769, 8, 720, 6, 721, 9, 774, 773, 777, 4, + 778, 5, 779, 781, 783, 12, 173, 6, 172, 9, 789, 4, 791, 787, 793, + 2, 237, 4, 796, 129, 799, 631, 800, 125, 219, 7, 218, 77, 806, + 805, 809, 6, 235, 9, 813, 8, 812, 5, 817, 815, 819, 309, 821, 92, + 154, 3, 164, 93, 827, 155, 828, 825, 831, 188, 789, 279, 789, 5, + 837, 835, 839, 9, 644, 9, 39, 5, 844, 737, 845, 847, 849, 3, 138, + 25, 51, 2, 139, 5, 856, 855, 859, 853, 861, 77, 433, 7, 865, 9, + 865, 6, 869, 867, 871, 5, 439, 175, 875, 234, 293, 235, 295, 879, + 881, 188, 836, 839, 885, 27, 77, 25, 201, 889, 891, 888, 890, 893, + 895, 235, 237, 7, 234, 5, 900, 103, 903, 813, 904, 5, 536, 125, + 909, 6, 234, 9, 235, 4, 915, 7, 917, 913, 919, 8, 439, 237, 922, + 236, 923, 925, 927, 4, 172, 6, 930, 7, 852, 933, 935, 53, 682, + 157, 165, 6, 37, 8, 321, 943, 945, 129, 947, 5, 23, 6, 951, 249, + 953, 2, 154, 9, 957, 165, 958, 731, 961, 9, 323, 154, 234, 489, + 967, 160, 165, 5, 415, 9, 972, 7, 972, 8, 977, 975, 979, 8, 518, + 9, 519, 983, 985, 22, 26, 347, 989, 8, 320, 497, 993, 643, 827, 3, + 165, 2, 155, 999, 1001, 157, 1003, 3, 433, 154, 1007, 6, 1006, 9, + 1011, 155, 1013, 1009, 1015, 3, 11, 8, 1019, 27, 1018, 1021, 1023, + 8, 640, 173, 1027, 7, 235, 5, 1030, 4, 1031, 9, 1034, 1033, 1037, + 4, 537, 6, 1041, 9, 1043, 909, 1045, 6, 177, 5, 1049, 179, 1050, + 173, 179, 4, 1055, 1053, 1057, 9, 999, 5, 998, 7, 1062, 1061, + 1065, 129, 675, 581, 1068, 3, 13, 8, 1072, 5, 1074, 2, 12, 9, + 1073, 1079, 1080, 1077, 1083, 7, 857, 4, 857, 6, 1089, 9, 1090, + 1087, 1093, 324, 461, 325, 460, 1097, 1099, 93, 125, 415, 1102, 5, + 1105, 543, 1107, 5, 93, 7, 1110, 6, 1111, 2, 1114, 3, 1115, 1117, + 1119, 9, 1121, 1113, 1123, 8, 998, 2, 164, 9, 1128, 155, 1131, + 1126, 1132, 1127, 1133, 1135, 1137, 6, 335, 249, 701, 1141, 1142, + 77, 701, 7, 1147, 6, 1146, 249, 1151, 1149, 1152, 23, 579, 22, + 578, 1157, 1159, 77, 155, 93, 1163, 4, 415, 1031, 1167, 4, 1030, + 93, 1171, 1169, 1172, 9, 415, 155, 1176, 641, 1179, 287, 383, 249, + 1183, 125, 488, 124, 489, 1187, 1189, 5, 414, 2, 10, 1193, 1195, + 8, 1197, 9, 1196, 1199, 1201, 155, 172, 154, 176, 1205, 1207, 9, + 766, 730, 767, 1211, 1213, 875, 1007, 129, 1217, 455, 519, 23, + 165, 6, 173, 1223, 1225, 383, 641, 9, 1229, 643, 1231, 6, 201, + 249, 1235, 93, 415, 6, 415, 5, 1241, 1238, 1243, 8, 1242, 1239, + 1246, 1245, 1249, 155, 723, 9, 973, 53, 1254, 454, 519, 2, 293, 3, + 292, 157, 1263, 1261, 1264, 23, 1001, 9, 1268, 7, 1268, 8, 1273, + 1271, 1275, 5, 371, 37, 1279, 129, 1281, 165, 643, 49, 103, 7, + 1286, 6, 1287, 9, 1290, 1289, 1293, 7, 335, 265, 1297, 249, 1299, + 1050, 1054, 1057, 1303, 129, 200, 155, 1129, 93, 1309, 22, 124, 3, + 102, 49, 1315, 7, 1317, 25, 1319, 9, 645, 643, 1323, 9, 164, 154, + 173, 1327, 1329, 9, 640, 1284, 1333, 286, 347, 533, 1337, 27, 235, + 10, 1341, 11, 1340, 1343, 1345, 2, 759, 9, 1348, 3, 761, 1351, + 1353, 461, 714, 460, 715, 1357, 1359, 9, 537, 7, 1362, 909, 1365, + 23, 423, 596, 1368, 597, 1369, 1371, 1373, 663, 1279, 129, 1377, + 641, 733, 165, 1381, 3, 189, 7, 1385, 4, 1386, 9, 1385, 6, 1391, + 1389, 1393, 9, 79, 155, 1061, 1129, 1399, 226, 257, 1313, 1402, 8, + 22, 286, 1407, 221, 1409, 9, 1129, 155, 998, 1412, 1415, 4, 439, + 173, 1418, 439, 1049, 5, 1423, 1421, 1425, 160, 1129, 7, 37, 9, + 36, 1431, 1433, 49, 1435, 129, 1437, 5, 51, 1177, 1441, 1176, + 1440, 129, 1445, 1443, 1446, 9, 213, 212, 550, 1451, 1453, 157, + 1129, 3, 158, 1456, 1459, 3, 155, 2, 733, 1463, 1465, 39, 460, + 235, 875, 234, 874, 1471, 1473, 103, 1475, 125, 908, 124, 909, + 1479, 1481, 675, 911, 2, 189, 51, 737, 1487, 1489, 4, 124, 909, + 1493, 675, 1495, 165, 1463, 9, 1499, 731, 1501, 6, 461, 3, 460, 5, + 1507, 1430, 1509, 1505, 1511, 2, 188, 23, 1515, 7, 1517, 6, 1516, + 9, 1520, 1519, 1523, 292, 957, 5, 438, 581, 1529, 129, 1530, 79, + 519, 9, 1534, 7, 347, 77, 1538, 349, 1541, 80, 984, 226, 519, 77, + 415, 9, 1549, 155, 1551, 79, 334, 8, 644, 165, 1557, 2, 1559, + 1323, 1557, 3, 1562, 1561, 1565, 129, 373, 23, 1568, 6, 433, 3, + 1573, 8, 1575, 5, 433, 1574, 1579, 1577, 1581, 9, 13, 2, 1585, 8, + 12, 5, 1589, 3, 1591, 1587, 1593, 8, 48, 1430, 1597, 1505, 1599, + 20, 35, 47, 60, 74, 87, 101, 113, 122, 137, 152, 170, 186, 198, + 210, 223, 233, 243, 254, 263, 270, 277, 285, 290, 304, 312, 318, + 333, 340, 342, 351, 360, 364, 379, 380, 386, 392, 402, 412, 416, + 430, 436, 452, 454, 459, 468, 477, 390, 486, 494, 502, 510, 517, + 522, 529, 535, 549, 558, 567, 573, 579, 592, 595, 596, 608, 620, + 628, 634, 638, 648, 660, 670, 673, 680, 689, 699, 704, 708, 719, + 729, 735, 742, 746, 748, 753, 756, 761, 765, 771, 784, 794, 802, + 811, 822, 833, 292, 841, 842, 850, 863, 872, 876, 883, 887, 897, + 899, 906, 911, 921, 928, 937, 938, 489, 940, 948, 954, 963, 964, + 968, 970, 980, 987, 9, 991, 994, 996, 1004, 1016, 1024, 1029, + 1039, 1047, 1059, 1067, 1070, 1085, 1095, 1101, 1108, 1125, 1139, + 1144, 1154, 1161, 1164, 1174, 1181, 1184, 1190, 1203, 1209, 1215, + 1218, 1221, 1226, 1232, 1236, 1251, 1253, 521, 1256, 1258, 1266, + 1276, 1282, 1284, 1295, 1300, 1305, 1306, 1310, 1312, 1321, 1324, + 1331, 1334, 1339, 1346, 1355, 1361, 1367, 1375, 1378, 1382, 1394, + 1396, 1400, 1404, 1411, 1416, 1426, 1428, 1438, 1448, 1455, 1460, + 1466, 1468, 1476, 1483, 1484, 1490, 1496, 1503, 124, 158, 1512, 8, + 1525, 1526, 1532, 1536, 1543, 1544, 1546, 1553, 1554, 1566, 1570, + 1582, 1594, 1600 }; + + /* incomplete XAG database */ + inline static const uint32_t subgraphs[] = {1780 << 16 | 0 << 8 | 4, + 2, 4, 2, 5, 3, 4, 3, 5, 4, 2, 2, 6, 2, 7, 3, 6, 3, 7, 6, 2, 4, 6, + 4, 7, 5, 6, 5, 7, 6, 4, 2, 8, 2, 9, 3, 8, 3, 9, 8, 2, 4, 8, + 4, 9, 5, 8, 5, 9, 8, 4, 6, 8, 6, 9, 7, 8, 7, 9, 8, 6, 5, 11, + 6, 10, 6, 11, 7, 10, 7, 11, 10, 6, 8, 10, 8, 11, 9, 10, 9, 11, 10, 8, + 6, 12, 6, 13, 7, 12, 7, 13, 12, 6, 9, 12, 9, 13, 12, 8, 5, 15, 6, 14, + 6, 15, 7, 14, 7, 15, 14, 6, 8, 14, 8, 15, 9, 14, 9, 15, 14, 8, 6, 16, + 6, 17, 7, 16, 7, 17, 16, 6, 8, 16, 8, 17, 9, 16, 9, 17, 16, 8, 6, 18, + 6, 19, 7, 18, 7, 19, 18, 6, 8, 19, 9, 18, 9, 19, 18, 8, 4, 20, 4, 21, + 5, 20, 7, 21, 8, 20, 9, 21, 20, 8, 11, 21, 20, 10, 15, 21, 20, 14, 17, 21, + 19, 21, 4, 22, 4, 23, 5, 22, 9, 22, 9, 23, 22, 8, 22, 12, 15, 23, 17, 23, + 18, 23, 4, 24, 7, 25, 9, 25, 24, 8, 11, 25, 13, 25, 15, 25, 24, 14, 19, 25, + 4, 26, 4, 27, 5, 26, 5, 27, 26, 4, 8, 27, 9, 26, 9, 27, 26, 8, 11, 27, + 13, 27, 17, 27, 26, 16, 19, 27, 4, 28, 28, 4, 9, 28, 9, 29, 28, 8, 11, 28, + 11, 29, 13, 29, 17, 29, 18, 29, 19, 28, 19, 29, 2, 30, 2, 31, 3, 30, 5, 31, + 7, 31, 8, 30, 8, 31, 9, 30, 9, 31, 30, 8, 13, 31, 17, 31, 19, 31, 23, 31, + 27, 31, 29, 31, 2, 32, 2, 33, 5, 33, 32, 6, 8, 33, 32, 8, 13, 33, 17, 33, + 21, 33, 25, 33, 27, 33, 28, 33, 32, 28, 2, 34, 3, 35, 34, 4, 7, 35, 34, 8, + 11, 35, 15, 35, 19, 35, 34, 18, 23, 35, 27, 35, 33, 35, 2, 36, 2, 37, 3, 36, + 3, 37, 36, 2, 8, 36, 8, 37, 9, 36, 9, 37, 36, 8, 11, 37, 15, 37, 17, 37, + 18, 37, 19, 37, 21, 37, 25, 37, 27, 37, 29, 37, 2, 38, 3, 38, 38, 2, 8, 38, + 8, 39, 9, 38, 9, 39, 38, 8, 11, 38, 11, 39, 15, 38, 15, 39, 17, 39, 18, 38, + 19, 38, 19, 39, 21, 39, 23, 38, 25, 39, 27, 38, 27, 39, 28, 38, 29, 38, 29, 39, + 4, 40, 4, 41, 6, 40, 9, 41, 13, 41, 15, 41, 19, 41, 23, 41, 25, 41, 29, 41, + 31, 41, 33, 41, 35, 41, 36, 41, 37, 41, 40, 36, 39, 41, 4, 42, 4, 43, 5, 42, + 6, 43, 7, 42, 17, 43, 27, 43, 30, 43, 31, 42, 31, 43, 32, 43, 33, 42, 42, 32, + 36, 43, 37, 42, 37, 43, 42, 36, 39, 42, 39, 43, 42, 38, 7, 45, 9, 45, 11, 45, + 21, 45, 31, 45, 44, 32, 36, 44, 36, 45, 39, 45, 44, 38, 4, 46, 4, 47, 5, 46, + 6, 47, 7, 46, 46, 6, 13, 47, 19, 47, 23, 47, 31, 46, 46, 30, 32, 47, 33, 47, + 34, 47, 35, 47, 36, 46, 36, 47, 37, 46, 37, 47, 46, 36, 38, 47, 39, 47, 4, 49, + 48, 4, 6, 49, 48, 6, 15, 48, 19, 48, 19, 49, 25, 48, 28, 49, 29, 48, 29, 49, + 31, 49, 33, 48, 35, 48, 36, 49, 39, 48, 48, 38, 2, 50, 2, 51, 6, 50, 7, 51, + 9, 51, 13, 51, 19, 51, 21, 51, 23, 51, 25, 51, 26, 51, 50, 26, 31, 51, 35, 51, + 39, 51, 47, 51, 48, 51, 2, 53, 3, 52, 6, 52, 6, 53, 17, 53, 22, 52, 23, 52, + 23, 53, 26, 53, 27, 53, 37, 53, 45, 53, 3, 55, 7, 55, 9, 55, 11, 55, 21, 55, + 22, 55, 23, 55, 26, 54, 26, 55, 31, 55, 43, 55, 53, 55, 2, 56, 3, 56, 6, 57, + 7, 56, 56, 6, 11, 57, 15, 57, 19, 57, 21, 56, 56, 20, 23, 57, 24, 57, 25, 56, + 25, 57, 26, 56, 27, 56, 27, 57, 56, 26, 33, 57, 41, 57, 2, 59, 3, 59, 58, 2, + 6, 59, 7, 58, 7, 59, 58, 6, 13, 59, 17, 59, 19, 59, 58, 20, 25, 59, 26, 59, + 27, 59, 58, 28, 35, 58, 58, 34, 38, 58, 38, 59, 39, 58, 43, 59, 47, 59, 2, 60, + 4, 60, 4, 61, 5, 61, 60, 4, 9, 61, 10, 61, 11, 61, 13, 61, 15, 61, 16, 61, + 17, 61, 18, 61, 19, 61, 23, 61, 27, 61, 33, 61, 39, 61, 43, 61, 47, 61, 48, 61, + 60, 52, 57, 61, 58, 61, 2, 63, 4, 62, 4, 63, 12, 63, 13, 62, 17, 63, 19, 63, + 27, 63, 37, 63, 45, 63, 55, 63, 3, 65, 5, 65, 9, 65, 11, 65, 16, 64, 16, 65, + 18, 65, 21, 65, 31, 65, 43, 65, 53, 65, 57, 65, 63, 65, 2, 66, 2, 67, 3, 66, + 3, 67, 66, 2, 4, 67, 5, 66, 66, 4, 10, 67, 11, 66, 66, 10, 13, 67, 14, 67, + 15, 67, 16, 66, 17, 66, 17, 67, 66, 16, 18, 66, 19, 66, 19, 67, 66, 18, 25, 67, + 35, 67, 41, 67, 51, 67, 57, 67, 3, 69, 68, 2, 4, 68, 4, 69, 5, 68, 68, 4, + 11, 69, 16, 69, 17, 68, 17, 69, 68, 16, 18, 68, 18, 69, 68, 18, 23, 69, 27, 69, + 68, 32, 37, 69, 39, 68, 43, 69, 47, 69, 57, 69, 58, 68, 70, 68, 9, 73, 37, 73, + 41, 73, 45, 73, 51, 73, 55, 73, 61, 73, 65, 73, 74, 2, 74, 4, 74, 16, 74, 18, + 33, 75, 41, 75, 74, 46, 48, 75, 51, 75, 58, 75, 67, 75, 8, 77, 9, 77, 76, 8, + 17, 77, 35, 77, 51, 77, 61, 77, 68, 77, 69, 77, 76, 68, 78, 2, 8, 79, 9, 78, + 9, 79, 78, 8, 17, 79, 78, 16, 31, 79, 63, 79, 9, 80, 17, 80, 48, 81, 51, 81, + 6, 83, 7, 83, 17, 83, 68, 83, 75, 83, 7, 85, 21, 85, 6, 87, 17, 87, 27, 87, + 37, 87, 61, 87, 65, 87, 67, 87, 69, 87, 6, 89, 7, 88, 88, 6, 23, 89, 88, 22, + 25, 88, 33, 89, 61, 89, 90, 74, 9, 93, 15, 93, 9, 94, 9, 95, 15, 95, 94, 14, + 23, 95, 9, 97, 25, 97, 31, 97, 51, 97, 9, 99, 98, 14, 21, 99, 35, 99, 45, 98, + 47, 99, 98, 46, 69, 99, 7, 103, 33, 103, 35, 103, 37, 103, 39, 103, 51, 103, 61, 103, + 6, 105, 21, 104, 27, 104, 27, 105, 104, 26, 31, 104, 37, 105, 104, 36, 55, 105, 65, 105, + 69, 105, 108, 68, 112, 4, 13, 113, 33, 113, 58, 112, 61, 115, 97, 115, 103, 115, 116, 4, + 8, 117, 9, 117, 57, 117, 116, 56, 63, 117, 13, 118, 13, 119, 55, 119, 61, 121, 98, 121, + 122, 2, 23, 125, 61, 125, 6, 127, 126, 6, 21, 126, 27, 127, 37, 126, 37, 127, 126, 36, + 65, 127, 77, 127, 126, 78, 128, 20, 77, 131, 9, 133, 11, 133, 132, 10, 79, 133, 88, 133, + 8, 135, 9, 134, 9, 135, 134, 8, 11, 135, 43, 135, 134, 42, 53, 135, 61, 135, 63, 135, + 73, 135, 87, 135, 134, 86, 134, 88, 136, 4, 136, 6, 9, 136, 9, 137, 136, 8, 47, 137, + 53, 136, 55, 136, 57, 137, 61, 137, 63, 137, 69, 137, 136, 68, 75, 137, 89, 137, 105, 137, + 127, 137, 9, 139, 138, 8, 11, 138, 11, 139, 88, 139, 21, 141, 31, 141, 43, 141, 63, 141, + 73, 141, 133, 141, 7, 143, 25, 143, 27, 143, 35, 143, 39, 143, 47, 143, 67, 143, 75, 143, + 78, 143, 79, 143, 95, 143, 97, 143, 101, 143, 131, 143, 6, 145, 7, 144, 7, 145, 144, 6, + 73, 145, 144, 78, 143, 145, 146, 4, 7, 146, 146, 6, 27, 146, 27, 147, 69, 147, 135, 147, + 7, 148, 148, 6, 25, 149, 61, 149, 81, 149, 101, 149, 131, 149, 150, 90, 9, 153, 143, 153, + 9, 154, 154, 62, 61, 157, 68, 157, 156, 68, 9, 159, 158, 8, 61, 158, 68, 158, 83, 158, + 143, 159, 149, 159, 68, 161, 151, 161, 158, 161, 7, 162, 21, 164, 28, 164, 31, 164, 38, 164, + 164, 134, 166, 6, 68, 167, 166, 72, 158, 167, 9, 169, 168, 166, 170, 48, 174, 58, 158, 177, + 15, 178, 19, 178, 180, 14, 9, 186, 188, 8, 9, 192, 192, 68, 68, 195, 194, 68, 104, 197, + 202, 78, 9, 211, 210, 8, 216, 58, 41, 226, 48, 226, 143, 229, 149, 229, 41, 230, 51, 230, + 61, 231, 178, 235, 9, 236, 236, 42, 236, 86, 236, 164, 238, 58, 178, 241, 19, 243, 159, 243, + 186, 243, 192, 243, 211, 243, 213, 243, 229, 243, 5, 244, 19, 249, 149, 249, 159, 249, 186, 249, + 192, 249, 211, 249, 143, 257, 149, 257, 243, 257, 249, 257, 68, 261, 260, 180, 9, 263, 262, 8, + 61, 262, 68, 262, 83, 262, 143, 263, 149, 263, 177, 262, 243, 263, 249, 263, 19, 264, 264, 36, + 38, 264, 268, 4, 143, 271, 149, 271, 272, 58, 9, 280, 9, 283, 41, 282, 48, 282, 51, 282, + 58, 282, 61, 282, 68, 282, 282, 166, 9, 285, 286, 58, 290, 68, 292, 58, 158, 295, 262, 295, + 296, 38, 19, 300, 300, 36, 97, 300, 300, 134, 199, 300, 223, 300, 300, 236, 252, 300, 302, 36, + 302, 222, 246, 304, 9, 308, 308, 68, 243, 308, 249, 308, 310, 58, 104, 312, 314, 58, 68, 317, + 316, 68, 126, 319, 320, 68, 322, 8, 326, 6, 326, 210, 330, 204, 246, 331, 330, 248, 332, 48, + 126, 335, 334, 128, 143, 339, 149, 339, 41, 341, 48, 341, 346, 8, 348, 58, 350, 4, 350, 252, + 178, 353, 352, 180, 41, 354, 41, 356, 51, 356, 61, 357, 41, 359, 48, 359, 104, 361, 360, 106, + 362, 106, 364, 8, 300, 367, 300, 369, 9, 370, 370, 42, 370, 86, 370, 300, 41, 373, 48, 373, + 372, 48, 41, 374, 48, 374, 300, 375, 376, 104, 376, 178, 376, 264, 376, 300, 19, 379, 21, 379, + 31, 379, 48, 379, 101, 379, 103, 379, 159, 379, 182, 379, 185, 379, 192, 379, 207, 379, 263, 379, + 267, 379, 271, 379, 277, 379, 301, 379, 308, 379, 339, 379, 369, 379, 374, 379, 3, 380, 379, 381, + 186, 382, 211, 382, 19, 385, 31, 385, 48, 385, 93, 385, 101, 385, 103, 385, 153, 385, 159, 385, + 173, 385, 185, 385, 192, 385, 207, 385, 384, 210, 384, 254, 263, 385, 267, 385, 271, 385, 279, 385, + 308, 385, 339, 385, 343, 385, 374, 385, 41, 387, 178, 388, 388, 220, 390, 248, 243, 393, 126, 396, + 398, 128, 400, 8, 400, 148, 379, 403, 385, 403, 404, 302, 379, 405, 385, 405, 143, 407, 149, 407, + 243, 407, 249, 407, 9, 409, 408, 8, 61, 408, 68, 408, 83, 408, 143, 409, 149, 409, 177, 408, + 243, 409, 249, 409, 295, 408, 379, 409, 385, 409, 19, 414, 28, 414, 414, 134, 414, 236, 414, 370, + 413, 415, 418, 2, 143, 418, 243, 418, 422, 48, 51, 425, 41, 426, 48, 426, 9, 429, 379, 429, + 411, 429, 418, 429, 385, 431, 9, 432, 9, 435, 41, 434, 48, 434, 51, 434, 58, 434, 61, 434, + 68, 434, 436, 48, 41, 440, 48, 440, 51, 443, 9, 445, 411, 445, 418, 445, 9, 446, 385, 449, + 9, 451, 41, 450, 48, 450, 51, 450, 58, 450, 61, 450, 68, 450, 68, 453, 158, 453, 262, 453, + 408, 453, 454, 270, 454, 338, 454, 344, 158, 457, 262, 457, 408, 457, 458, 134, 458, 154, 458, 236, + 458, 370, 334, 460, 373, 460, 397, 460, 35, 462, 39, 462, 239, 462, 313, 462, 360, 462, 25, 464, + 29, 464, 35, 464, 39, 464, 464, 78, 354, 466, 387, 466, 19, 468, 39, 468, 307, 468, 328, 468, + 385, 468, 19, 470, 39, 470, 379, 472, 385, 472, 75, 474, 224, 474, 251, 474, 15, 476, 19, 476, + 171, 476, 191, 476, 208, 476, 478, 218, 478, 300, 15, 484, 19, 484, 25, 484, 29, 484, 486, 134, + 486, 236, 486, 370, 224, 489, 239, 489, 274, 489, 313, 489, 360, 489, 379, 491, 385, 491, 307, 493, + 328, 493, 35, 497, 39, 497, 39, 499, 385, 499, 379, 503, 385, 503, 61, 511, 512, 298, 512, 414, + 514, 416, 13, 517, 287, 517, 367, 517, 518, 298, 379, 521, 51, 525, 61, 525, 385, 525, 528, 134, + 528, 154, 528, 236, 528, 370, 517, 535, 517, 541, 379, 543, 501, 545, 7, 550, 51, 553, 462, 553, + 464, 553, 497, 553, 507, 553, 549, 553, 5, 554, 51, 557, 385, 557, 462, 557, 464, 557, 497, 557, + 564, 478, 385, 567, 462, 573, 497, 573, 578, 300, 580, 300, 472, 584, 51, 587, 61, 587, 143, 588, + 243, 588, 68, 591, 545, 591, 592, 6, 68, 593, 545, 595, 596, 4, 39, 598, 600, 154, 151, 603, + 39, 604, 537, 611, 379, 613, 75, 614, 171, 616, 379, 619, 15, 620, 25, 620, 51, 620, 61, 620, + 61, 623, 259, 623, 395, 623, 561, 623, 68, 625, 158, 625, 262, 625, 408, 625, 626, 420, 626, 438, + 158, 629, 262, 629, 408, 629, 630, 46, 630, 56, 630, 162, 632, 134, 632, 154, 632, 236, 632, 370, + 636, 78, 553, 636, 557, 636, 574, 640, 535, 642, 243, 648, 67, 650, 77, 650, 195, 650, 317, 650, + 533, 650, 562, 650, 650, 608, 652, 42, 48, 652, 103, 652, 201, 652, 491, 652, 495, 652, 313, 654, + 360, 654, 39, 656, 334, 659, 373, 659, 397, 659, 662, 512, 33, 665, 191, 665, 208, 665, 569, 665, + 614, 665, 670, 36, 670, 218, 15, 677, 171, 677, 570, 677, 67, 679, 77, 679, 195, 679, 317, 679, + 533, 679, 562, 679, 669, 679, 334, 681, 397, 681, 243, 683, 484, 683, 665, 685, 686, 134, 686, 154, + 686, 236, 686, 370, 476, 691, 677, 691, 694, 522, 61, 701, 468, 701, 499, 701, 700, 606, 313, 703, + 360, 703, 704, 6, 379, 707, 385, 707, 652, 707, 7, 708, 51, 711, 143, 711, 464, 711, 636, 711, + 3, 712, 51, 715, 143, 715, 149, 715, 464, 715, 636, 715, 623, 721, 722, 644, 379, 725, 517, 726, + 143, 729, 379, 731, 474, 732, 736, 630, 584, 739, 583, 740, 638, 740, 61, 742, 468, 742, 499, 742, + 334, 744, 397, 744, 249, 749, 750, 6, 68, 751, 754, 46, 753, 755, 468, 757, 499, 757, 557, 757, + 758, 2, 39, 763, 151, 765, 61, 766, 61, 769, 243, 771, 39, 773, 61, 775, 259, 775, 395, 775, + 561, 775, 721, 775, 776, 508, 776, 564, 647, 781, 782, 532, 784, 468, 39, 789, 557, 789, 158, 791, + 262, 791, 408, 791, 158, 793, 262, 793, 408, 793, 796, 66, 796, 178, 796, 264, 798, 504, 800, 134, + 800, 154, 800, 236, 800, 370, 802, 68, 804, 66, 135, 804, 237, 804, 371, 804, 804, 416, 577, 804, + 623, 804, 735, 804, 775, 804, 570, 806, 691, 806, 808, 98, 535, 808, 808, 684, 726, 808, 810, 66, + 814, 66, 689, 818, 718, 818, 820, 568, 822, 42, 57, 822, 822, 502, 531, 822, 558, 822, 822, 674, + 737, 822, 824, 42, 48, 824, 103, 824, 201, 824, 491, 824, 495, 824, 707, 824, 826, 32, 307, 828, + 328, 828, 39, 830, 832, 570, 33, 834, 191, 834, 208, 834, 836, 508, 354, 839, 387, 839, 840, 512, + 35, 843, 224, 843, 251, 843, 616, 843, 642, 845, 846, 126, 75, 849, 574, 849, 732, 849, 151, 851, + 689, 853, 718, 853, 57, 855, 531, 855, 558, 855, 737, 855, 354, 857, 387, 857, 35, 859, 224, 859, + 251, 859, 143, 861, 484, 861, 843, 863, 864, 134, 864, 154, 864, 236, 864, 370, 474, 867, 640, 867, + 849, 867, 143, 871, 483, 873, 647, 873, 851, 873, 634, 875, 667, 875, 51, 877, 462, 877, 497, 877, + 307, 879, 328, 879, 33, 881, 191, 881, 208, 881, 882, 570, 884, 4, 652, 887, 824, 887, 808, 889, + 816, 889, 5, 890, 892, 480, 808, 895, 816, 895, 61, 897, 243, 897, 816, 897, 3, 898, 61, 901, + 243, 901, 468, 901, 499, 901, 816, 901, 904, 810, 904, 814, 61, 907, 517, 908, 642, 908, 243, 911, + 476, 912, 677, 912, 804, 915, 650, 917, 679, 917, 916, 796, 916, 810, 61, 919, 249, 919, 557, 919, + 584, 919, 740, 919, 61, 920, 583, 920, 739, 920, 804, 920, 804, 921, 924, 814, 61, 927, 61, 928, + 634, 930, 667, 930, 51, 932, 462, 932, 497, 932, 354, 934, 387, 934, 35, 936, 224, 936, 251, 936, + 938, 582, 149, 941, 919, 941, 942, 4, 947, 949, 950, 2, 763, 953, 769, 953, 789, 953, 919, 953, + 143, 955, 39, 959, 161, 965, 157, 969, 195, 969, 763, 969, 769, 969, 789, 969, 957, 969, 39, 971, + 591, 971, 157, 975, 157, 979, 763, 979, 769, 979, 789, 979, 957, 979, 39, 981, 591, 981, 157, 983, + 988, 78, 135, 988, 237, 988, 371, 988, 407, 988, 988, 538, 988, 696, 988, 868, 41, 991, 48, 991, + 379, 992, 379, 995, 243, 996, 243, 999, 143, 1000, 143, 1003, 1004, 488, 1006, 658, 83, 1009, 143, 1011, + 243, 1011, 379, 1011, 41, 1012, 48, 1012, 33, 1014, 685, 1014, 385, 1017, 789, 1017, 33, 1018, 527, 1020, + 1022, 46, 143, 1024, 1026, 6, 1028, 962, 1030, 132, 9, 1033, 1032, 8, 243, 1033, 249, 1033, 379, 1033, + 385, 1033, 51, 1034, 35, 1036, 645, 1038, 957, 1043, 143, 1046, 379, 1046, 67, 1049, 283, 1049, 435, 1049, + 451, 1049, 1050, 810, 133, 1052, 1052, 810, 73, 1055, 1054, 132, 763, 1055, 769, 1055, 789, 1055, 919, 1055, + 9, 1057, 1056, 8, 9, 1058, 143, 1059, 51, 1061, 645, 1063, 1064, 154, 161, 1067, 1068, 796, 1070, 178, + 1072, 18, 1072, 716, 1074, 88, 1074, 126, 1076, 74, 1080, 16, 61, 1083, 1084, 812, 137, 1087, 553, 1089, + 557, 1089, 711, 1089, 715, 1089, 33, 1091, 685, 1091, 1090, 854, 23, 1093, 527, 1093, 1092, 852, 1094, 156, + 137, 1097, 1098, 582, 957, 1101, 143, 1103, 761, 1103, 787, 1103, 1104, 810, 83, 1107, 149, 1107, 919, 1107, + 379, 1108, 1110, 794, 379, 1113, 243, 1114, 1114, 672, 137, 1116, 1120, 36, 243, 1122, 1124, 126, 1126, 124, + 1126, 546, 1126, 660, 1128, 58, 9, 1131, 1132, 58, 31, 1134, 143, 1136, 379, 1136, 9, 1138, 493, 1140, + 1142, 796, 61, 1145, 143, 1147, 379, 1147, 9, 1149, 61, 1151, 489, 1153, 489, 1154, 897, 1154, 945, 1157, + 1158, 794, 665, 1160, 843, 1162, 1164, 8, 51, 1166, 61, 1166, 39, 1168, 39, 1170, 507, 1170, 462, 1173, + 497, 1173, 1174, 796, 31, 1176, 197, 1179, 197, 1180, 897, 1180, 27, 1182, 381, 1185, 39, 1189, 39, 1191, + 507, 1191, 945, 1193, 41, 1197, 48, 1197, 1198, 48, 1200, 48, 761, 1203, 787, 1203, 747, 1204, 1206, 8, + 379, 1208, 51, 1210, 1212, 336, 1214, 154, 659, 1217, 659, 1218, 889, 1218, 747, 1221, 9, 1223, 61, 1225, + 61, 1227, 1228, 98, 61, 1231, 39, 1232, 1234, 324, 693, 1236, 143, 1239, 157, 1239, 77, 1241, 83, 1241, + 195, 1241, 317, 1241, 453, 1241, 625, 1241, 37, 1242, 51, 1245, 121, 1245, 21, 1246, 319, 1249, 319, 1250, + 889, 1250, 693, 1253, 1254, 6, 9, 1261, 1260, 8, 11, 1262, 79, 1262, 1262, 134, 9, 1264, 161, 1267, + 9, 1268, 1270, 134, 31, 1273, 47, 1273, 73, 1273, 111, 1273, 133, 1273, 145, 1273, 169, 1273, 211, 1273, + 215, 1273, 285, 1273, 289, 1273, 565, 1273, 915, 1273, 804, 1275, 73, 1276, 169, 1276, 285, 1276, 31, 1279, + 41, 1279, 51, 1279, 61, 1279, 133, 1279, 143, 1279, 211, 1279, 61, 1280, 301, 1283, 179, 1287, 11, 1288, + 903, 1288, 89, 1291, 9, 1292, 67, 1295, 133, 1298, 9, 1301, 1302, 8, 650, 1305, 679, 1305, 1304, 796, + 1306, 814, 1308, 6, 261, 1309, 591, 1309, 665, 1310, 493, 1313, 665, 1315, 493, 1316, 89, 1318, 11, 1321, + 903, 1321, 11, 1323, 9, 1327, 61, 1328, 261, 1331, 405, 1333, 11, 1334, 83, 1339, 9, 1340, 143, 1341, + 665, 1345, 493, 1347, 39, 1349, 75, 1351, 61, 1353, 1354, 802, 1356, 56, 1356, 144, 97, 1358, 199, 1358, + 301, 1358, 367, 1358, 381, 1358, 713, 1358, 899, 1358, 1360, 16, 179, 1362, 47, 1364, 39, 1366, 75, 1368, + 67, 1370, 1372, 144, 1374, 16, 25, 1378, 245, 1382, 555, 1382, 891, 1382, 143, 1385, 497, 1385, 804, 1387, + 1388, 816, 143, 1391, 497, 1391, 379, 1392, 143, 1394, 1396, 78, 39, 1399, 650, 1401, 679, 1401, 1400, 796, + 39, 1403, 1404, 796, 233, 1407, 699, 1407, 11, 1409, 245, 1411, 555, 1411, 891, 1411, 61, 1413}; + // clang-format off +}; // namespace mockturtle + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp b/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp new file mode 100644 index 0000000..c14b780 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp @@ -0,0 +1,347 @@ +/* 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 xmg3_npn.hpp + \brief Replace with size-optimum xmg3s and AIGs from NPN (from ABC rewrite) + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../../algorithms/simulation.hpp" +#include "../../io/write_bench.hpp" +#include "../../networks/xmg.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +struct xmg3_npn_resynthesis_params +{ + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +struct xmg3_npn_resynthesis_stats +{ + stopwatch<>::duration time_classes{ 0 }; + stopwatch<>::duration time_db{ 0 }; + + uint32_t db_size; + uint32_t covered_classes; + + void report() const + { + std::cout << fmt::format( "[i] build classes time = {:>5.2f} secs\n", to_seconds( time_classes ) ); + std::cout << fmt::format( "[i] build db time = {:>5.2f} secs\n", to_seconds( time_db ) ); + } +}; + +/*! \brief Resynthesis function based on pre-computed AIGs. + * + * This resynthesis function can be passed to ``cut_rewriting``. It will + * produce a network based on pre-computed xmg3s with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const aig_network aig = ...; + xmg3_npn_resynthesis resyn; + aig = cut_rewriting( aig, resyn ); + + .. note:: + + The implementation of this algorithm was heavily inspired by the rewrite + command in AIG. It uses the same underlying database of subcircuits. + \endverbatim + */ +template +class xmg3_npn_resynthesis +{ +public: + xmg3_npn_resynthesis( xmg3_npn_resynthesis_params const& ps = {}, xmg3_npn_resynthesis_stats* pst = nullptr ) + : ps( ps ), + pst( pst ), + _classes( 1 << 16 ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "DatabaseNtk is not a network type" ); + static_assert( has_get_node_v, "DatabaseNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "DatabaseNtk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "DatabaseNtk does not implement the is_xor method" ); + static_assert( has_size_v, "DatabaseNtk does not implement the size method" ); + static_assert( has_create_pi_v, "DatabaseNtk does not implement the create_pi method" ); + static_assert( has_create_and_v, "DatabaseNtk does not implement the create_and method" ); + static_assert( has_create_xor_v, "DatabaseNtk does not implement the create_xor method" ); + static_assert( has_foreach_fanin_v, "DatabaseNtk does not implement the foreach_fanin method" ); + static_assert( has_foreach_node_v, "DatabaseNtk does not implement the foreach_node method" ); + static_assert( has_make_signal_v, "DatabaseNtk does not implement the make_signal method" ); + + _repr.reserve( 222u ); + build_classes(); + build_db(); + } + + virtual ~xmg3_npn_resynthesis() + { + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + + template + void operator()( Ntk& ntk, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + kitty::static_truth_table<4u> tt = kitty::extend_to<4u>( function ); + + /* get representative of function */ + const auto repr = _repr[_classes[*tt.cbegin()]]; + + /* check if representative has circuits */ + const auto it = _repr_to_signal.find( repr ); + if ( it == _repr_to_signal.end() ) + { + return; + } + + const auto config = kitty::exact_npn_canonization( tt ); + + assert( repr == std::get<0>( config ) ); + + std::vector> pis( 4, ntk.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector> pis_perm; + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm.push_back( pis[perm[i]] ); + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = ntk.create_not( pis_perm[i] ); + } + } + + for ( auto const& cand : it->second ) + { + std::unordered_map, signal> db_to_ntk; + + db_to_ntk.insert( { 0, ntk.get_constant( false ) } ); + for ( auto i = 0; i < 4; ++i ) + { + db_to_ntk.insert( { i + 1, pis_perm[i] } ); + } + auto f = copy_db_entry( ntk, _db.get_node( cand ), db_to_ntk ); + if ( _db.is_complemented( cand ) != ( ( phase >> 4 ) & 1 ) ) + { + f = ntk.create_not( f ); + } + if ( !fn( f ) ) + { + return; + } + } + } + +private: + signal + copy_db_entry( Ntk& ntk, node const& n, std::unordered_map, signal>& db_to_ntk ) const + { + if ( const auto it = db_to_ntk.find( n ); it != db_to_ntk.end() ) + { + return it->second; + } + + std::vector> fanin; + // std::array, 2> fanin; + _db.foreach_fanin( n, [&]( auto const& f ) { + auto ntk_f = copy_db_entry( ntk, _db.get_node( f ), db_to_ntk ); + if ( _db.is_complemented( f ) ) + { + ntk_f = ntk.create_not( ntk_f ); + } + fanin.push_back( ntk_f ); + } ); + + const auto f = _db.is_xor3( n ) ? ntk.create_xor3( fanin[0], fanin[1], fanin[2] ) : ntk.create_maj( fanin[0], fanin[1], fanin[2] ); + db_to_ntk.insert( { n, f } ); + return f; + } + + void build_classes() + { + stopwatch t( st.time_classes ); + + kitty::dynamic_truth_table map( 16u ); + std::transform( map.cbegin(), map.cend(), map.begin(), []( auto word ) { return ~word; } ); + + int64_t index = 0; + kitty::static_truth_table<4u> tt; + while ( index != -1 ) + { + kitty::create_from_words( tt, &index, &index + 1 ); + const auto res = kitty::exact_npn_canonization( tt, [&]( const auto& tt ) { + _classes[*tt.cbegin()] = _repr.size(); + kitty::clear_bit( map, *tt.cbegin() ); + } ); + _repr.push_back( std::get<0>( res ) ); + + /* find next non-classified truth table */ + index = find_first_one_bit( map ); + } + } + + void build_db() + { + stopwatch t( st.time_db ); + + _db.get_constant( false ); + /* four primary inputs */ + _db.create_pi(); + _db.create_pi(); + _db.create_pi(); + _db.create_pi(); + + auto* p = subgraphs; + while ( true ) + { + auto entry0 = *p++; + auto entry1 = *p++; + auto entry2 = *p++; + + if ( entry0 == 0 && entry1 == 0 && entry2 == 0 ) + break; + + auto is_xor = entry0 & 1; + entry0 >>= 1; + + const auto child0 = _db.make_signal( entry0 >> 1 ) ^ ( entry0 & 1 ); + const auto child1 = _db.make_signal( entry1 >> 1 ) ^ ( entry1 & 1 ); + const auto child2 = _db.make_signal( entry2 >> 1 ) ^ ( entry2 & 1 ); + + if ( is_xor ) + { + _db.create_xor3( child0, child1, child2 ); + } + else + { + _db.create_maj( child0, child1, child2 ); + } + } + + const auto sim_res = simulate_nodes>( _db ); + + _db.foreach_node( [&]( auto n ) { + if ( _repr[_classes[*sim_res[n].cbegin()]] == sim_res[n] ) + { + if ( _repr_to_signal.count( sim_res[n] ) == 0 ) + { + _repr_to_signal.insert( { sim_res[n], { _db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[sim_res[n]].push_back( _db.make_signal( n ) ); + } + } + else + { + const auto f = ~sim_res[n]; + if ( _repr[_classes[*f.cbegin()]] == f ) + { + if ( _repr_to_signal.count( f ) == 0 ) + { + _repr_to_signal.insert( { f, { !_db.make_signal( n ) } } ); + } + else + { + _repr_to_signal[f].push_back( !_db.make_signal( n ) ); + } + } + } + } ); + + st.db_size = _db.size(); + st.covered_classes = static_cast( _repr_to_signal.size() ); + } + + xmg3_npn_resynthesis_params ps; + xmg3_npn_resynthesis_stats st; + xmg3_npn_resynthesis_stats* pst{ nullptr }; + + std::vector> _repr; + std::vector _classes; + std::unordered_map, std::vector>, kitty::hash>> _repr_to_signal; + + DatabaseNtk _db; + + // clang-format off + inline static const uint16_t subgraphs[] + { + 0x2,0x2,0x4,0xc,0x8,0xb,0x2,0xa,0xc,0x8,0x7,0x8,0x2,0x6,0x10,0x2,0x6,0x8,0x29,0x4,0x2,0x0,0x15,0x16,0x0,0x3,0x4,0x8,0x14,0x1b,0x8,0x7,0x1a,0x3d,0x6,0x2,0x0,0x9,0x20,0x4,0x4,0x6,0x2,0x8,0x24,0x49,0x6,0x0,0x0,0x9,0x28,0xa,0x6,0xa,0x0,0x2,0x2d,0x10,0xa,0x2f,0x6,0x4,0x8,0x4,0x6,0x8,0x2,0x32,0x34,0x15,0x6,0x0,0x0,0x9,0x38,0x4,0x4,0x9,0x0,0x7,0x3c,0x10,0x3c,0x3f,0x11,0x6,0x4,0x0,0x9,0x42,0x0,0x6,0x9,0x8d,0x4,0x0,0x10,0xa,0x49,0x0,0x4,0x7,0x8,0x8,0x4d,0xd,0x4,0x2,0x2,0x8,0x50,0x0,0x2,0x15,0x10,0x16,0x55,0x11,0x4,0x2,0x0,0x6,0x59,0x2,0x8,0x5a,0x4,0x4,0x7,0xbd,0x8,0x6,0x0,0x9,0x60,0x4,0x4,0x8,0x0,0x6,0x65,0xc,0x8,0x67,0x4,0x6,0x51,0xd5,0x50,0x8,0x0,0x6b,0x6c,0x2,0x4,0x6,0x8,0x8,0x70,0x4,0x14,0x72,0x6,0x4,0x6,0x4,0x8,0x76,0xed,0x8,0x4,0x0,0x79,0x7a,0x69,0x4,0x2,0x2,0x34,0x7e,0x11,0x6,0x2,0x8,0x6,0x8,0x0,0x82,0x85,0x0,0x2,0x35,0x8,0x34,0x89,0x0,0x5,0x82,0xc,0x8,0x8d,0x8,0x8c,0x8f,0xc,0x8,0xa,0x85,0x6,0x2,0xc,0x8,0x95,0x0,0x42,0x97,0x0,0x2,0x5,0x135,0x6,0x0,0x2,0x84,0x9c,0x0,0x2,0x9,0xc,0x64,0xa1,0x4,0x7,0x42,0x6,0x44,0xa4,0x10,0xa,0x43,0x85,0x4,0x2,0x0,0x7,0xaa,0x12,0x42,0xac,0x0,0x2,0x8,0x2,0x84,0xb0,0x0,0x3,0x6,0x169,0x8,0x2,0x0,0x51,0xb6,0x4,0x6,0x25,0x0,0x9,0x24,0x8,0xba,0xbd,0x109,0x8,0x2,0x0,0x8,0x84,0x0,0xc0,0xc3,0x10,0x50,0x70,0x0,0x7,0x42,0x12,0x58,0xc8,0xc,0x8,0x59,0x0,0x3,0x58,0x10,0xcc,0xcf,0x4,0x5,0x8,0x0,0x4,0xd3,0x4,0x7,0xd4,0x1ad,0x8,0x6,0xc,0x8,0x64,0x8,0x7,0x32,0x1b9,0x4,0x0,0x0,0x4,0x8,0x0,0x2,0xe1,0xa,0x6,0xe2,0x1c9,0x4,0x0,0x0,0x8,0xd2,0x4,0x6,0xe8,0x1d5,0xd2,0x0,0x0,0x2,0x59,0x8,0x6,0xef,0x1e1,0xee,0x0,0x0,0x2,0x4,0xc,0x58,0xf5,0x1ed,0x6,0x0,0x0,0x8,0x64,0x0,0x6,0xfa,0x1f9,0x64,0x6,0x2,0x2,0x6,0xa,0xa0,0x100,0x205,0x4,0x0,0x65,0x4,0x0,0x2,0x8,0x32,0xc,0x107,0x108,0xc9,0x8,0x0,0xc,0x8,0x10d,0x11,0x4,0x0,0x221,0x6,0x2,0x4,0x8,0x113,0x10,0x111,0x114,0x0,0x112,0x117,0xb1,0x6,0x0,0x10,0x100,0x11b,0x8,0x6,0x9b,0x23d,0x32,0x0,0x0,0x9,0x100,0x9,0x2,0x0,0x200,0x123,0x124,0x0,0x5,0x6,0x251,0x8,0x2,0x8,0x8,0x128,0x0,0x12a,0x12d,0x10,0x70,0x124,0x1a5,0x6,0x4,0x1a5,0x2,0x0,0x12,0x132,0x134,0x161,0x4,0x2,0xc,0x8,0x138,0x2,0xb0,0x13a,0x4,0x8,0x70,0x27d,0x8,0x0,0xe1,0x2,0x0,0x2,0x2,0x70,0x10,0x142,0x144,0x0,0x6,0x8,0x291,0x8,0x2,0x8,0x8,0x148,0x0,0x14a,0x14d,0x10,0x70,0x142,0x6,0x6,0x8,0x0,0x8,0x153,0x8,0x6,0x155,0x2ad,0x152,0x0,0x0,0x7,0xd2,0x2b5,0x152,0x0,0x11,0x2,0x0,0x12,0xc8,0x15e,0x0,0x6,0x1b,0x2c5,0x1a,0x2,0xc,0x8,0x164,0xc,0x9,0x32,0x2d1,0x4c,0x32,0x4,0x4,0x4d,0x99,0x6,0x2,0x10,0x16c,0x16e,0xc,0x9,0x128,0x4,0x4,0x173,0x2e9,0x128,0x8,0xa,0x6,0x15e,0xc,0x8,0x179,0x0,0x4,0x9,0x8,0x6,0x15e,0x2fd,0x8,0x0,0x2bc,0x17c,0x180,0xc9,0x50,0x0,0x10,0x50,0x184,0x0,0x3,0x84,0x311,0x8,0x2,0x6,0x6,0xa,0x10,0x38,0x18c,0x15,0x8,0x0,0x0,0x7,0x190,0x14,0x190,0x192,0xc,0x8,0x191,0x0,0x7,0xa,0x331,0x8,0x6,0x8,0x6,0x9,0x0,0x8,0x19d,0x33d,0x6,0x4,0x8,0x6,0x43,0x0,0x2,0x43,0x2,0x1a2,0x1a4,0x2,0x2,0x8,0x351,0x128,0x4,0x0,0x4,0x1a8,0xc,0x1a8,0x1ac,0x0,0x111,0x1a8,0x2,0x6,0x1b0,0x365,0x1a8,0x0,0x8,0x8,0x83,0x36d,0x9a,0x0,0x0,0x3,0x8,0x375,0x6,0x2,0x0,0x5,0x1bc,0x12,0x1bc,0x1be,0x289,0x6,0x2,0x10,0x70,0x1c2,0x8,0x8,0x1a9,0x2,0x6,0x1c6,0x391,0x1a8,0x0,0x0,0x2,0x6,0x10,0x70,0x1cc,0x4,0x6,0x10,0x0,0x9,0x1d0,0x3a5,0x10,0x4,0x141,0x6,0x4,0x2,0xe0,0x1d6,0xa,0x6,0x8,0x3b5,0xa0,0x6,0x8,0x9,0xa0,0x8,0x6,0x1df,0x3c1,0x1de,0xa0,0x399,0x8,0x4,0xe,0x46,0x1e4,0x2,0x6,0xa0,0x10,0x1d6,0x1e8,0x4,0x4,0x85,0x0,0x8,0x1ed,0x3dd,0x1ec,0x84,0x351,0x4,0x0,0xc,0x8,0x1f3,0x10,0x4c,0x65,0x3ed,0x64,0x6,0x86,0xe0,0x1a8,0xa,0x6,0xa0,0x3f9,0x8,0x4,0x21,0x4,0x0,0x4,0x6,0x11,0x20,0x201,0x202,0xd,0x4,0x0,0x2,0xe0,0x206,0x4,0x6,0x9,0x0,0x9,0x20a,0x419,0x84,0x0,0x4,0x4,0x111,0xc,0x8,0x210,0x425,0x210,0x110,0x1c0,0x100,0x206,0xc,0x8,0x111,0x8,0x6,0xa1,0x435,0x8,0x0,0xe,0xa0,0x1da,0x43d,0x8,0x0,0x4,0x7,0x128,0x2,0x8,0x222,0x449,0x128,0x4,0x10,0x24,0x206,0xc,0x64,0x9b,0x455,0x8,0x0,0x6,0x8,0x10,0x2,0xa,0x10,0x461,0x22e,0x4,0x0,0x2,0x207,0x469,0x4,0x2,0x10,0x206,0x236,0x251,0x8,0x4,0x0,0x3,0x23a,0x252,0x23a,0x23c,0x291,0x8,0x4,0x14,0x148,0x241,0x2,0x8,0xd2,0x489,0x70,0x0,0x99,0x8,0x6,0x4,0x4,0x82,0x495,0x2,0x0,0x4,0x4,0x83,0x0,0x5,0x8,0x104,0x24f,0x250,0x4a5,0x4,0x0,0x11,0x6,0x0,0x4ad,0x4,0x2,0xc,0x8,0x258,0x134,0x256,0x25b,0x8,0x7,0x46,0x4bd,0x152,0x0,0x0,0x7,0x8,0x12,0x124,0x262,0x4,0x8,0x47,0xc,0x125,0x266,0x2bd,0x6,0x4,0xc,0x47,0x26a,0x8,0x6,0x15f,0x2,0x6,0x26e,0x10,0x26a,0x270,0xc,0x8,0x125,0x2,0x4,0x8,0x248,0x256,0x277,0x2a5,0x4,0x0,0x8,0x6,0x153,0x10,0x27a,0x27c,0x4a,0x46,0x124,0x0,0x8,0x82,0x4,0x5,0x282,0x509,0x82,0x0,0x4,0x7,0x8,0xd,0x2,0x0,0x6,0x288,0x28a,0x8,0x289,0x28c,0x51d,0x28a,0x8,0x0,0x9,0x84,0x4,0x6,0x293,0x529,0x84,0x0,0x8,0x6,0x110,0x10,0x112,0x298,0x351,0x6,0x0,0x12,0x124,0x29c,0x0,0x59,0x256,0x48,0x47,0x58,0x4,0x6,0x262,0x549,0x262,0x4,0x10,0x263,0x2a6,0x8d,0x4,0x2,0xe,0x46,0x2aa,0x8,0x8,0x1cd,0x55d,0x4,0x2,0xc,0x2ae,0x2b0,0xc,0x8,0x58,0xc,0x8,0x124,0x0,0x276,0x2b6,0x571,0x6,0x0,0x4,0xe0,0x207,0x579,0x8,0x4,0xa0,0x256,0x277,0x0,0x4,0x6,0x10,0x152,0x2c2,0x589,0x2c2,0x4,0x10,0x76,0x124,0x591,0x6,0x0,0xa,0x6,0xe0,0x1c1,0x2,0x0,0x2,0x2cc,0x2ce,0x5a1,0x8,0x6,0xa0,0x9a,0x149,0x8,0x6,0x125,0x249,0x6,0x0,0x10,0x2d6,0x2d9,0x4,0x6,0xe0,0x5b9,0x4,0x2,0x0,0x2dc,0x2de,0x5c1,0x2de,0x8,0x351,0x6,0x2,0x4,0x4,0x2e4,0x5cd,0x8,0x2,0x0,0x6,0x125,0x248,0x256,0x2ea,0x2,0x2,0x256,0x5dd,0x4,0x2,0x10,0x257,0x2ee,0x4ae,0x2f0,0x2f2,0x12,0x256,0x278,0x12,0x124,0x2d8,0x0,0x6,0x124,0x5f5,0x124,0x8,0x6a,0x124,0x256,0x4,0x4,0x148,0x0,0x149,0x300,0x605,0x8,0x6,0x68,0x7e,0x257,0x0,0x2,0x1db,0x611,0x8,0x6,0x4,0x4,0x257,0x12,0x256,0x30c,0x61d,0x256,0x0,0x61a,0x30e,0x310,0x515,0x8,0x2,0x0,0x5,0x314,0x516,0x314,0x316,0x64,0x100,0x257,0x6,0x6,0x32,0x0,0x8,0x31d,0x63d,0x32,0x6,0x4,0x6,0x33,0x645,0xb0,0x6,0x611,0x276,0x6,0xa,0x6,0x1a,0x0,0x8,0x1b,0x655,0x328,0x4,0xc,0x8,0x3c,0x65d,0x8,0x0,0xe,0x8,0x3c,0x0,0x6,0x333,0x669,0x3c,0x0,0x0,0x4,0x3c,0xe,0x8,0x338,0x675,0x3c,0x0,0x79,0x6,0x0,0x10,0x19c,0x33e,0xc,0xa,0x58,0x685,0x8,0x0,0x4,0x6,0x9b,0x8,0x9,0x9a,0x691,0x346,0x0,0x8,0x6,0x1b,0xc,0x256,0x34d,0x8,0x8,0x257,0x4,0x257,0x350,0x140,0xf5,0x256,0x0,0x6,0xf5,0x6ad,0xf4,0x8,0x4,0x5,0x6,0x8,0x43,0x35a,0x6b9,0x2,0x0,0x0,0x2,0x7,0x4,0x9,0x360,0x4,0x5,0x362,0x6c9,0x42,0x0,0x0,0x8,0x19c,0x6d1,0x4,0x2,0x0,0x369,0x36a,0x6d9,0x19c,0x6,0x85,0x2,0x0,0xc,0x8,0x370,0x84,0x370,0x372,0xc,0x9,0xf4,0x6ed,0x28a,0x4,0x0,0x9,0xa,0xc,0xf4,0x37b,0x6f9,0xa,0x0,0x65,0x6,0x4,0x201,0x8,0x4,0x2,0x380,0x382,0x6,0x8,0x2c2,0x70d,0x6,0x4,0x1a5,0x6,0x2,0x1a5,0x4,0x0,0x0,0x38b,0x38c,0x0,0x3,0x32,0x64,0x380,0x391,0x0,0x6,0x3d,0x729,0x2,0x0,0x72d,0x8,0x4,0xc,0x8,0x71,0x4,0x4,0x39a,0x739,0x70,0x8,0x2,0x4,0x32,0x65,0x6,0x0,0x4,0x3a1,0x3a2,0x749,0x6,0x4,0x0,0x8,0x3c,0x0,0x6,0x3a9,0x755,0x4,0x2,0x8,0xd2,0x38a,0x639,0x262,0x4,0x6,0x8,0x262,0x8,0x9,0x3b2,0x769,0x262,0x2,0x4,0x8,0x262,0xa,0x6,0x3b8,0x775,0x262,0x2,0x0,0x2,0x58,0xc,0x8,0x3be,0x781,0x3be,0x58,0x515,0x4,0x0,0x12,0x28a,0x3c4,0xe,0x3c4,0x3c6,0x0,0x4,0x1bb,0x0,0x6,0x3cb,0x799,0x58,0x0,0x8,0x8,0x71,0xc,0x15f,0x3d0,0x7a5,0x70,0x0,0x2,0x6,0xf4,0xa,0x8,0xf4,0x7b1,0x3d6,0x2,0x585,0x8,0x2,0x0,0x2,0x3dd,0x7bd,0x6,0x4,0xa,0x6,0xb4,0x7c5,0x58,0x0,0x0,0x4,0x207,0x6,0x8,0x206,0x7d1,0x3e6,0x2,0xe,0x110,0x15e,0x4,0x7,0x15e,0x7dd,0x6,0x4,0x6,0x3ee,0x3f0,0x8,0x8,0x15,0x4,0x6,0x3f4,0x7e9,0x14,0x0,0x7f1,0x3f6,0x4,0xa,0x6,0x1ba,0x7f9,0xe0,0x2,0x1c1,0x6,0x4,0x4,0x4,0x400,0x805,0x8,0x0,0x49,0x8,0x0,0x49,0x8,0x6,0xc,0x14,0xf5,0x815,0xf4,0x2,0x819,0x8,0x4,0x8,0x6,0xb5,0x4,0x8,0xb4,0x825,0x410,0x6,0x0,0x4,0x1cd,0x82d,0x8,0x2,0x4,0x8,0x42,0x835,0x6,0x0,0x0,0x2,0x206,0x83d,0x8,0x4,0x15,0x8,0x6,0x515,0x8,0x4,0x0,0x0,0x0 + + }; + // clang-format on +}; // namespace mockturtle + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp b/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp new file mode 100644 index 0000000..28a5284 --- /dev/null +++ b/include/mockturtle/algorithms/node_resynthesis/xmg_npn.hpp @@ -0,0 +1,292 @@ +/* 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 xmg_npn.hpp + \brief Replace with size-optimum XMGs from NPN + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee + \author Zhufei Chu +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../algorithms/cleanup.hpp" +#include "../../io/write_bench.hpp" +#include "../../networks/xmg.hpp" +#include "../../traits.hpp" +#include "../../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Resynthesis function based on pre-computed size-optimum XMGs. + * + * This resynthesis function can be passed to ``node_resynthesis``, + * ``cut_rewriting``, and ``refactoring``. It will produce an XMG based on + * pre-computed size-optimum XMGs with up to at most 4 variables. + * Consequently, the nodes' fan-in sizes in the input network must not exceed + * 4. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const klut_network klut = ...; + xmg_npn_resynthesis resyn; + const auto xmg = node_resynthesis( klut, resyn ); + \endverbatim + */ +class xmg_npn_resynthesis +{ +public: + /*! \brief Default constructor. + * + */ + xmg_npn_resynthesis() + { + build_db(); + } + + template + void operator()( xmg_network& xmg, kitty::dynamic_truth_table const& function, LeavesIterator begin, LeavesIterator end, Fn&& fn ) const + { + assert( function.num_vars() <= 4 ); + const auto fe = kitty::extend_to( function, 4 ); + const auto config = kitty::exact_npn_canonization( fe ); + + auto func_str = "0x" + kitty::to_hex( std::get<0>( config ) ); + const auto it = class2signal.find( func_str ); + assert( it != class2signal.end() ); + + // const auto it = class2signal.find( static_cast( std::get<0>( config )._bits[0] ) ); + + std::vector pis( 4, xmg.get_constant( false ) ); + std::copy( begin, end, pis.begin() ); + + std::vector pis_perm( 4 ); + auto perm = std::get<2>( config ); + for ( auto i = 0; i < 4; ++i ) + { + pis_perm[i] = pis[perm[i]]; + } + + const auto& phase = std::get<1>( config ); + for ( auto i = 0; i < 4; ++i ) + { + if ( ( phase >> perm[i] ) & 1 ) + { + pis_perm[i] = !pis_perm[i]; + } + } + + for ( auto const& po : it->second ) + { + topo_view topo{ db, po }; + auto f = cleanup_dangling( topo, xmg, pis_perm.begin(), pis_perm.end() ).front(); + + if ( !fn( ( ( phase >> 4 ) & 1 ) ? !f : f ) ) + { + return; /* quit */ + } + } + } + +private: + std::unordered_map opt_xmgs; + + inline std::vector split( const std::string& str, const std::string& sep ) + { + std::vector result; + + size_t last = 0; + size_t next = 0; + while ( ( next = str.find( sep, last ) ) != std::string::npos ) + { + result.push_back( str.substr( last, next - last ) ); + last = next + 1; + } + result.push_back( str.substr( last ) ); + + return result; + } + + void load_optimal_xmgs( const unsigned& strategy ) + { + std::vector result; + + switch ( strategy ) + { + case 1: + result = split( npn4_s, "\n" ); + break; + + case 2: + result = split( npn4_sd, "\n" ); + break; + + case 3: + result = split( npn4_ds, "\n" ); + break; + + default: + break; + } + + for ( auto record : result ) + { + auto p = split( record, " " ); + assert( p.size() == 2u ); + opt_xmgs.insert( std::make_pair( p[0], p[1] ) ); + } + } + + std::vector create_xmg_from_str( const std::string& str, const std::vector& signals ) + { + auto sig = signals; + std::vector result; + + std::stack polar; + std::stack inputs; + + for ( auto i = 0ul; i < str.size(); i++ ) + { + // operators polarity + if ( str[i] == '[' || str[i] == '<' ) + { + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + + // input signals + if ( str[i] >= 'a' && str[i] <= 'd' ) + { + inputs.push( sig[str[i] - 'a' + 1] ); + + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + else if ( str[i] == '0' ) + { + inputs.push( sig[0] ); + + polar.push( i > 0 && str[i - 1] == '!' ? 1 : 0 ); + } + + // create signals + if ( str[i] == '>' ) + { + assert( inputs.size() >= 3u ); + auto x1 = inputs.top(); + inputs.pop(); + auto x2 = inputs.top(); + inputs.pop(); + auto x3 = inputs.top(); + inputs.pop(); + + assert( polar.size() >= 4u ); + auto p1 = polar.top(); + polar.pop(); + auto p2 = polar.top(); + polar.pop(); + auto p3 = polar.top(); + polar.pop(); + + auto p4 = polar.top(); + polar.pop(); + + inputs.push( db.create_maj( x1 ^ p1, x2 ^ p2, x3 ^ p3 ) ^ p4 ); + polar.push( 0 ); + } + + if ( str[i] == ']' ) + { + assert( inputs.size() >= 2u ); + auto x1 = inputs.top(); + inputs.pop(); + auto x2 = inputs.top(); + inputs.pop(); + + assert( polar.size() >= 3u ); + auto p1 = polar.top(); + polar.pop(); + auto p2 = polar.top(); + polar.pop(); + + auto p3 = polar.top(); + polar.pop(); + + inputs.push( db.create_xor( x1 ^ p1, x2 ^ p2 ) ^ p3 ); + polar.push( 0 ); + } + } + + assert( !polar.empty() ); + auto po = polar.top(); + polar.pop(); + db.create_po( inputs.top() ^ po ); + result.push_back( inputs.top() ^ po ); + return result; + } + + void build_db() + { + std::vector signals; + signals.push_back( db.get_constant( false ) ); + + for ( auto i = 0u; i < 4; ++i ) + { + signals.push_back( db.create_pi() ); + } + + load_optimal_xmgs( 1 ); // size optimization + + for ( const auto& e : opt_xmgs ) + { + class2signal.insert( std::make_pair( e.first, create_xmg_from_str( e.second, signals ) ) ); + } + } + + xmg_network db; + std::unordered_map> class2signal; + + std::string npn4_s = "0x3cc3 [d[!bc]]\n0x1bd8 [!!]\n0x19e3 [[!>]>]\n0x19e1 [>>]\n0x17e8 [d]\n0x179a <[!a][ad]!>\n0x178e [!d]\n0x16e9 [![!d]!>]\n0x16bc [[!c<0ad>]>]\n0x16ad >!>>\n0x16ac [!>>]\n0x16a9 !![ac]>>![ac]>>\n0x169e <[bc][a[bc]]>\n0x169b [>]\n0x169a [a]>\n0x1699 <>>!>>>\n0x1687 [!a!>![c]>]\n0x167e []>\n0x166e [>!>]\n0x03cf \n0x166b ]>\n0x01be >>]>>\n0x07f2 >\n0x07e9 [>>]\n0x6996 [b[a[cd]]]\n0x01af >!>\n0x033c [c]![!bd]>\n0x07e3 ]]>>\n0x1681 <!>![[!ab]]>\n0x01ae [!d<0!>]\n0x07e2 [!>>]\n0x01ad [>]\n0x07e1 [!!<0c!>>]\n0x001f <0!c!<0!a!b>>>\n0x01ac [>]\n0x07e0 [d>]\n0x07bc [>>]\n0x03dc ]!>>\n0x06f6 \n0x06bd [!>>]\n0x077e >[a]>\n0x06b9 ><0!cd>>\n0x06b7 ![b[!ac]]>\n0x06b6 !<0c!<0!d>>>\n0x077a [!<0!a>]\n0x06b5 [!>>]\n0x0ff0 [cd]\n0x0779 [>>]\n0x06b4 [c>]\n0x0778 >[c>]>\n0x007f !>>\n0x06b3 <[cd][!bd]>>\n0x007e <0!d![a]>\n0x06b2 [d]>\n0x0776 <[ab]![cd]>\n0x06b1 [!d!![ab]>]\n0x06b0 [d>>]\n0x037c ]!>>\n0x01ef ]>\n0x0696 >!>\n0x035e [!<0!a!d>!<0!c!>>]\n0x0678 [c]!>\n0x0676 ![ab]>\n0x0669 [!d!>!>]\n0x01bc [<0!b!c>>]\n0x0663 [!<0b>]\n0x07f0 <0[cd][cd]>>\n0x01bf >\n0x0666 <0[ab]!<0cd>>\n0x0661 [>>]\n0x1689 [!<0d![ab]>>]\n0x0660 <0![!cd][ab]>\n0x0182 [!>>]\n0x07b6 [c!>]\n0x03d7 >\n0x06f1 >![d>]>>\n0x03dd !![!d]>\n0x0180 [!!>]\n0x07b4 [!<0c!<0d>>]\n0x03db [a]>\n0x07b0 [cd]>\n0x03d4 [d>]\n0x03c7 <[!bc]<0!a!c>!<0bd>>\n0x03c6 [b>]>\n0x03c5 [!!>]\n0x03c3 ![bc]>\n0x03c1 ]!![b]>>\n0x03c0 [d]\n0x1798 [><0!d>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 <0!d>>>\n0x177e [!<0!a!c>]\n0x066f \n0x035b !<0b!c><0a!c>>\n0x1697 [>[!bc]]\n0x066b !]>>\n0x07f8 [d>]\n0x035a [c]>\n0x1696 <0>![a[!bc]]>\n0x003f <0!d!>\n0x0673 ]!>>\n0x0359 [!<0b!c>]\n0x0672 <<0!b!d>[ab][cd]>\n0x0358 [!>!>![c<0a!d>]>]\n0x003d <0>>>\n0x0357 !>\n0x003c <0!d[bc]>\n0x0356 [<0!a!d><0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d [<0!d>>>]\n0x0690 [c]\n0x01e9 [!>!<0d>>>]\n0x1686 [!a>]\n0x07f1 >\n0x01bd ]<0!c>>\n0x1683 [[!bc]>]\n0x001e <0!d[c]>\n0x01ab [ad]>\n0x01aa >>\n0x01a9 ]<0!ad>>\n0x001b <0!!>\n0x01a8 [<0!b!c>>]\n0x000f <0!c!d>\n0x0199 [!ab]<0!d[!ab]>>\n0x0198 [b!]>]\n0x0197 [!a<0bc>]>\n0x06f9 [d]\n0x0196 >!>\n0x03d8 [d!>]\n0x06f2 <[cd]<0a![cd]>>\n0x03de ]>\n0x018f ]>\n0x0691 <<0c>>>\n0x01ea [!d!>]\n0x07b1 <[cd]<0!a!c>!>\n0x0189 <<0b!d>[a]>\n0x036b ]>>\n0x03d5 ]>\n0x0186 [>]\n0x0119 <0[!ab]!>\n0x0368 [![c<0a!d>]!]>>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 ![ac][cd]>\n0x0181 [>>>]\n0x036e ]!>\n0x011f >\n0x016f >!>>\n0x016e >\n0x013f !>\n0x019f <0!<0bd>>\n0x013e [[!bd]!>]\n0x019e [d>>]\n0x013d <0!>>\n0x016b >\n0x01fe [d!<0!a!>]\n0x166a [!<0b!c>>>]\n0x0019 [<0b>>]\n0x006b <0!a<0bc>>>\n0x069f !>\n0x013c <0!!>>>\n0x037e [>]\n0x012f <0!a!c>>\n0x0693 [c!![!b>]>]\n0x012d <0!>\n0x01ee [!d<0!a!b>]>\n0x012c [!<0!b!c>>]\n0x017f >\n0x1796 ![!d]>\n0x036d <!>\n0x011e >>!!<0!d!<0!c<0!a!b>>>>>\n0x0679 !>>\n0x035f >\n0x067b >![b]!>\n0x0118 <0[!d[a]]![a]>>\n0x067a [>>]\n0x0117 <0!!>>\n0x1ee1 [!d![!c]]\n0x016a >!!>>>\n0x0169 ><0!d>>\n0x037d ]>\n0x0697 <<0ac>!!!>>\n0x006f ![!ab]>\n0x17ac [>]\n0x0069 <0!d![a[bc]]>\n0x0667 <0!<0ab>!>>\n0x0168 >>>\n0x067e ]>\n0x011b !>\n0x036a []\n0x018b <0a!d>>\n0x0001 <0<0!b!d>>\n0x1669 <!<[bd]!>>>\n0x0018 <0!d[a]>\n0x168b [![d]>>]\n0x0662 [>>]\n0x00ff !d\n0x168e [>>]\n0x0007 <0<0!c!d>!>\n0x19e6 [[ad]>]\n0x0116 <0!![!c>]>\n0x036c [[bd]<0c!>]\n0x01e8 <<0b!d>>\n0x06f0 <<0!ab>![!cd]>\n0x03d6 [[c]!>>]\n0x1be4 [!d!]\n0x0187 <0!c>>\n0x0003 <0!d<0!b!c>>\n0x017e [[!ad]>>]\n0x01eb ]>>\n0x0006 <0!d>\n0x011a [a[cd]>]\n0x0369 <0[!c<0!d![!ab]>]!<0bd>>\n0x03d9 [!b>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [d[!b]]\n0x0017 <0!d!>\n0x019a ]<0!c>>\n0x0016 <0!>!>>"; + std::string npn4_sd = "0x3cc3 [b[!cd]]\n0x1bd8 [!]\n0x19e3 [>]\n0x19e1 [>>]\n0x17e8 [d]\n0x179a [>[!bd]>]\n0x178e \n0x16e9 [d<!<0a!b>>]\n0x16bc [<0!b!c>[ad]>]\n0x16ad [![ac]><0d>]\n0x16ac [!>>]\n0x16a9 <0>[!<0!b!c>[!ad]]>\n0x169e >\n0x169b <>!>!>\n0x169a [a>]\n0x1699 >!>>\n0x1687 [[ac]!<0bc>>]\n0x167e <>[a]!>>\n0x166e [>>]\n0x03cf \n0x166b ]>\n0x01be [ad]!>>\n0x07f2 <[cd]<0a!b>!<0ad>>\n0x07e9 [>>]\n0x6996 [[bc][ad]]\n0x01af <[!ac]<0a!d>!>\n0x033c <<0c!d>!>\n0x07e3 [c!<0b!c>>]\n0x1681 <0[![ab]]!>>\n0x01ae ![!ad]<0b!d>>\n0x07e2 [!>>]\n0x01ad <0b!d>[!ac]>\n0x07e1 [c!>]\n0x001f <0!c!d>>\n0x01ac [d!<0!b!c>>]\n0x07e0 [d>]\n0x07bc [>!<0!d>]\n0x03dc ]!>>\n0x06f6 \n0x06bd <>!>>\n0x077e <<0!ab>!>\n0x06b9 ![!cd]>>\n0x06b7 >\n0x06b6 [!c!<!<0c!d>[ab]>]\n0x077a [!<0a!>![cd]]\n0x06b5 ![!c]>\n0x0ff0 [cd]\n0x0779 <>!>\n0x06b4 [!c![ab]>]\n0x0778 [[cd]<0<0ab>>]\n0x007f <0!b!d>>\n0x06b3 [![!bd]!>]\n0x007e <0!d![a]>\n0x06b2 [!<0!c!>>]\n0x0776 <[ab][cd]!>\n0x06b1 [d[!ab]>]\n0x06b0 [>!<0cd>>]\n0x037c [[!bd]!>]\n0x01ef ]>\n0x0696 <<0c!d>!>\n0x035e [!<0!c!>>]\n0x0678 <<0a!b>[c]>\n0x0676 [ab]<0c!d>>\n0x0669 [<0!c!d><[cd]<0!c!d>[ab]>]\n0x01bc [>!<0!b!c>]\n0x0663 <0[b]!<0cd>>\n0x07f0 <[cd]!!<0ab>>\n0x01bf <0!a!b>>\n0x0666 <0[ab]!<0cd>>\n0x0661 <!>[]>\n0x1689 [[d]<0!c>]\n0x0660 [!]\n0x0182 [>>]\n0x07b6 [>[!c]]\n0x03d7 >\n0x06f1 [><0!ad>>]\n0x03dd <[bd]<0!a!d>!<0cd>>\n0x0180 [!>]\n0x07b4 [>>]\n0x03db <[!bc]!>\n0x07b0 <<0ac>[cd]!>\n0x03d4 [!d>]\n0x03c7 <0!a!c>[!bc]>\n0x03c6 <0!<0cd>[!b]>\n0x03c5 <[bd]<0!a!c>![bc]>\n0x03c3 [!bc]>\n0x03c1 [b]>>\n0x03c0 [d]\n0x1798 [>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 [b<[ac][!ad]>]\n0x177e [<0!b!d>!]\n0x066f \n0x035b <<0!b!c>!<0ac>>\n0x1697 >\n0x066b [>!<0!cd>>]\n0x07f8 [!d!>]\n0x035a [!><0!a!d>]\n0x1696 >>>\n0x003f <0!d!>\n0x0673 ]>>\n0x0359 []\n0x0672 <[cd]<0!b!d>[ab]>\n0x0358 <<0a!c>[!d<0!b!c>]>\n0x003d [bc]<0!a!d>>\n0x0357 <0!a!d>>\n0x003c <0!d![!bc]>\n0x0356 [!<0!a!d>!<0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d <0!b>>\n0x0690 [!d]\n0x01e9 ![b]>\n0x1686 [[bc]>]\n0x07f1 [cd]>\n0x01bd ]<0!b>>\n0x1683 [[bc]!>]\n0x001e <0!d![!c]>\n0x01ab >\n0x01aa <[ad]<0!b!c><0a!d>>\n0x01a9 [a>]\n0x001b <0!>\n0x01a8 [a>]\n0x000f <0!c!d>\n0x0199 <[!ab]<0!a!c>>\n0x0198 [!>]\n0x0197 ]>>\n0x06f9 [d]\n0x0196 <<0!d!>[ad]!>\n0x03d8 [>>]\n0x06f2 >\n0x03de ][bd]>\n0x018f ]>\n0x0691 <0>>>\n0x01ea [d>]\n0x07b1 <<0!a!c>[cd]>\n0x0189 <[!ab]!<0a!d>>\n0x036b !>\n0x03d5 [!<0a!d>>]\n0x0186 [>!]\n0x0119 <0[!ab]!>\n0x0368 [>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 [cd]![ac]>\n0x0181 <[b]<0!d!>>\n0x036e [!b<0!a!d>]>\n0x011f >\n0x016f <<0a!d>!>\n0x016e [a[bd]]>\n0x013f <0!<0ad>!>\n0x019f <0!a!b>>\n0x013e [[!cd]>]\n0x019e <0!b>>\n0x013d <!!>\n0x016b ![!b[!ac]]>\n0x01fe [d>]\n0x166a ]!>>\n0x0019 <0!a!b><0b!c>>\n0x006b >!>\n0x069f !>\n0x013c <<0!ad>>!>>\n0x037e [!>]\n0x012f !>\n0x0693 [![bd]>]\n0x012d <<0ac>!>\n0x01ee [!!>]\n0x012c [<0!a!b>>]\n0x017f >\n0x1796 !>>\n0x036d <!>\n0x011e >!!<0c!d>>>\n0x0679 <0!b!c>>[c]>\n0x035f !>\n0x067b [![ad]<0b!c>]>\n0x0118 <<0!a!b>>>\n0x067a [>>]\n0x0117 ><0!c!>>\n0x1ee1 [[cd]<0!a!b>]\n0x016a !>!>>\n0x0169 <0!d>>\n0x037d [!d<0!b!c>]>\n0x0697 <>!>\n0x006f [ab]>\n0x17ac [>]\n0x0069 <0!d[a[!bc]]>\n0x0667 !>\n0x0168 [>>]\n0x067e ][ab]>\n0x011b !>\n0x036a [!<0!a!d>]\n0x018b <0!b!c><0ab>>\n0x0001 <0<0!a!b><0!c!d>>\n0x1669 <>!>\n0x0018 <0!d[a]>\n0x168b [[!a]>>]\n0x0662 [ab][d]>\n0x00ff !d\n0x168e [!>>]\n0x0007 <0!!<0ab>>\n0x19e6 [[ad]<0b!<0ac>>]\n0x0116 [<0[!ac]!>]\n0x036c [[bd]!>]\n0x01e8 <0!ad>>\n0x06f0 <[cd]!>\n0x03d6 [!<0bc>>>]\n0x1be4 [d!]\n0x0187 !>\n0x0003 <0!d<0!b!c>>\n0x017e [[!bd]<[!bd]>]\n0x01eb ]!>>\n0x0006 <0[ab]<0!c!d>>\n0x011a <0[d[ac]]!>\n0x0369 >>>\n0x03d9 [d<>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [[!cd]]\n0x0017 <0!d!>\n0x019a ]!>>\n0x0016 <0>!>"; + std::string npn4_ds = "0x3cc3 [b[!cd]]\n0x1bd8 [!]\n0x19e3 [>]\n0x19e1 [bc]]![bc]>>\n0x17e8 [d]\n0x179a [>[!bd]>]\n0x178e \n0x16e9 [d<!<0a!b>>]\n0x16bc [<0!b!c>[ad]>]\n0x16ad [![ac]><0d>]\n0x16ac [<[bc]<0ab>>>]\n0x16a9 <0>[!<0!b!c>[!ad]]>\n0x169e >\n0x169b <>!>!>\n0x169a [a>]\n0x1699 >!>>\n0x1687 [[ac]!<0bc>>]\n0x167e <>[a]!>>\n0x166e [>>]\n0x03cf \n0x166b ]>\n0x01be [ad]!>>\n0x07f2 <[cd]<0a!b>!<0ad>>\n0x07e9 [>>]\n0x6996 [[bc][ad]]\n0x01af <[!ac]<0a!d>!>\n0x033c <<0c!d>!>\n0x07e3 [c!<0b!c>>]\n0x1681 <0[![ab]]!>>\n0x01ae ![!ad]<0b!d>>\n0x07e2 [>]\n0x01ad <0b!d>[!ac]>\n0x07e1 [c!>]\n0x001f <0!c!d>>\n0x01ac [d!<0!b!c>>]\n0x07e0 [d>]\n0x07bc [>!<0!d>]\n0x03dc ]!>>\n0x06f6 \n0x06bd <>!>>\n0x077e <<0!ab>!>\n0x06b9 ![!cd]>>\n0x06b7 >\n0x06b6 [!c!<!<0c!d>[ab]>]\n0x077a [!<0a!>![cd]]\n0x06b5 ![!c]>\n0x0ff0 [cd]\n0x0779 <>!>\n0x06b4 [!c![ab]>]\n0x0778 [[cd]<0<0ab>>]\n0x007f <0!b!d>>\n0x06b3 [![!bd]!>]\n0x007e <0a!c>>\n0x06b2 [!<0!c!>>]\n0x0776 <[ab][cd]!>\n0x06b1 [d[!ab]>]\n0x06b0 [>!<0cd>>]\n0x037c [[!bd]!>]\n0x01ef <<0b!d>!<0ad>>\n0x0696 <<0c!d>!>\n0x035e <<0b!d>!>\n0x0678 <<0a!b>[c]>\n0x0676 [ab]<0c!d>>\n0x0669 [<0!c!d><[cd]<0!c!d>[ab]>]\n0x01bc [>!<0!b!c>]\n0x0663 <0[b]!<0cd>>\n0x07f0 <[cd]!!<0ab>>\n0x01bf <0!a!b>>\n0x0666 <0[ab]!<0cd>>\n0x0661 <!>[]>\n0x1689 [[d]<0!c>]\n0x0660 [!]\n0x0182 <0>!>\n0x07b6 [>[!c]]\n0x03d7 >\n0x06f1 [><0!ad>>]\n0x03dd <[bd]<0!a!d>!<0cd>>\n0x0180 [!>]\n0x07b4 [>>]\n0x03db <[!bc]!>\n0x07b0 <<0ac>[cd]!>\n0x03d4 <[cd]<0!a!d>[bd]>\n0x03c7 <0!a!c>[!bc]>\n0x03c6 <0!<0cd>[!b]>\n0x03c5 <[bd]<0!a!c>![bc]>\n0x03c3 [!bc]>\n0x03c1 [b]>>\n0x03c0 [d]\n0x1798 [>>]\n0x036f ]>\n0x03fc [d!<0!b!c>]\n0x1698 [b<[ac][!ad]>]\n0x177e [<0!b!d>!]\n0x066f \n0x035b <<0!b!c>!<0ac>>\n0x1697 >\n0x066b [>!<0!cd>>]\n0x07f8 [!d!>]\n0x035a [!><0!a!d>]\n0x1696 >>>\n0x003f <0!d!>\n0x0673 ]>>\n0x0359 []\n0x0672 <[cd]<0!b!d>[ab]>\n0x0358 <<0a!c>[!d<0!b!c>]>\n0x003d [bc]<0!a!d>>\n0x0357 <0!a!d>>\n0x003c <0!d![!bc]>\n0x0356 [!<0!a!d>!<0!b!c>]\n0x07e6 [>]\n0x033f \n0x033d <0!b>>\n0x0690 <<0c!d>>\n0x01e9 ![b]>\n0x1686 [[bc]>]\n0x07f1 [cd]>\n0x01bd ]<0!b>>\n0x1683 [[bc]!>]\n0x001e <0!d![!c]>\n0x01ab >\n0x01aa <[ad]<0!b!c><0a!d>>\n0x01a9 [a>]\n0x001b <0!>\n0x01a8 [a>]\n0x000f <0!c!d>\n0x0199 <[!ab]<0!a!c>>\n0x0198 [!>]\n0x0197 ]>>\n0x06f9 <[cd]>\n0x0196 <<0!d!>[ad]!>\n0x03d8 [>>]\n0x06f2 >\n0x03de ][bd]>\n0x018f ]>\n0x0691 <0>>>\n0x01ea [d>]\n0x07b1 <<0!a!c>[cd]>\n0x0189 <[!ab]!<0a!d>>\n0x036b !>\n0x03d5 [!<0a!d>>]\n0x0186 [>!]\n0x0119 <0[!ab]!>\n0x0368 [>]\n0x0183 <[!bc]<0a!d><0!a!b>>\n0x07b5 [cd]![ac]>\n0x0181 <[b]<0!d!>>\n0x036e [!b<0!a!d>]>\n0x011f >\n0x016f <<0a!d>!>\n0x016e [a[bd]]>\n0x013f <0!<0ad>!>\n0x019f <0!a!b>>\n0x013e [!>![!d]]\n0x019e <0!b>>\n0x013d <!!>\n0x016b ![!b[!ac]]>\n0x01fe [d>]\n0x166a ]!>>\n0x0019 <0!a!b><0b!c>>\n0x006b >!>\n0x069f !>\n0x013c <<0!ad>>!>>\n0x037e [!>]\n0x012f !>\n0x0693 [![bd]>]\n0x012d <<0ac>!>\n0x01ee [!!>]\n0x012c [<0!a!b>>]\n0x017f >\n0x1796 !>>\n0x036d <!>\n0x011e >!!<0c!d>>>\n0x0679 <0!b!c>>[c]>\n0x035f !>\n0x067b [![ad]<0b!c>]>\n0x0118 <<0!a!b>>>\n0x067a <[c]<0a!b>!>\n0x0117 ><0!c!>>\n0x1ee1 [[cd]<0!a!b>]\n0x016a !>!>>\n0x0169 <0!d>>\n0x037d [!d<0!b!c>]>\n0x0697 <>!>\n0x006f [ab]>\n0x17ac [>]\n0x0069 <0!d[a[!bc]]>\n0x0667 !>\n0x0168 [>>]\n0x067e ][ab]>\n0x011b !>\n0x036a [!<0!a!d>]\n0x018b <0!b!c><0ab>>\n0x0001 <0<0!a!b><0!c!d>>\n0x1669 <>!>\n0x0018 <0ab>!>\n0x168b <<0!b!c>!<0!b!c>>!!>>\n0x0662 [ab][d]>\n0x00ff !d\n0x168e [!>>]\n0x0007 <0!!<0ab>>\n0x19e6 [[ad]<0b!<0ac>>]\n0x0116 [<0[!ac]!>]\n0x036c [>!>]\n0x01e8 <0!ad>>\n0x06f0 <[cd]!>\n0x03d6 [!<0bc>>>]\n0x1be4 [d!]\n0x0187 !>\n0x0003 <0!d<0!b!c>>\n0x017e [[!bd]<[!bd]>]\n0x01eb ]!>>\n0x0006 <0[ab]<0!c!d>>\n0x011a <0[d[ac]]!>\n0x0369 >>>\n0x03d9 [d<>]\n0x0000 0\n0x019b ![ab]>\n0x1668 [>]\n0x18e7 [[!cd]]\n0x0017 <0!d!>\n0x019a ]!>>\n0x0016 <0>!>"; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/pattern_generation.hpp b/include/mockturtle/algorithms/pattern_generation.hpp new file mode 100644 index 0000000..dd721c3 --- /dev/null +++ b/include/mockturtle/algorithms/pattern_generation.hpp @@ -0,0 +1,603 @@ +/* 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 pattern_generation.hpp + \brief Expressive Simulation Pattern Generation + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "circuit_validator.hpp" +#include "dont_cares.hpp" +#include "simulation.hpp" +#include +#include +#include +#include + +namespace mockturtle +{ + +struct pattern_generation_params +{ + /*! \brief Number of patterns each node should have for both values. + * + * When this parameter is set to greater than 1, and if the network has more + * than 2048 PIs, the `BUFFER_SIZE` in `lib/bill/sat/interface/abc_bsat2.hpp` + * has to be increased to at least `ntk.num_pis()`. + */ + uint32_t num_stuck_at{ 1 }; + + /*! \brief Whether to consider observability, and how many levels. 0 = no. -1 = Consider TFO until PO. */ + int32_t odc_levels{ 0 }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. Note that it will take more time to do extra ODC computation if this is turned on. */ + bool verbose{ false }; + + /*! \brief Random seed. */ + std::default_random_engine::result_type random_seed{ 1 }; + + /*! \brief Conflict limit of the SAT solver. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Maximum number of clauses of the SAT solver. (incremental CNF construction) */ + uint32_t max_clauses{ 1000 }; +}; + +struct pattern_generation_stats +{ + /*! \brief Total time. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Time for ODC computation */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Number of constant nodes. */ + uint32_t num_constant{ 0 }; + + /*! \brief Number of generated patterns. */ + uint32_t num_generated_patterns{ 0 }; + + /*! \brief Number of stuck-at patterns that is re-generated because the original one was unobservable. */ + uint32_t unobservable_type1{ 0 }; + + /*! \brief Number of additional patterns generated because the node was unobservable with one value. */ + uint32_t unobservable_type2{ 0 }; + + /*! \brief Number of unobservable nodes (node for which an observable pattern can not be found). */ + uint32_t unobservable_node{ 0 }; +}; + +namespace detail +{ + +template +class patgen_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = incomplete_node_map; + + explicit patgen_impl( Ntk& ntk, Simulator& sim, pattern_generation_params const& ps, validator_params& vps, pattern_generation_stats& st ) + : ntk( ntk ), ps( ps ), st( st ), vps( vps ), validator( ntk, vps ), + tts( ntk ), sim( sim ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + + if ( ps.num_stuck_at > 0 ) + { + stuck_at_check(); + if constexpr ( std::is_same_v ) + { + sim.pack_bits(); + call_with_stopwatch( st.time_sim, [&]() { + tts.reset(); + simulate_nodes( ntk, tts, sim, true ); + } ); + } + if constexpr ( substitute_const ) + { + for ( auto n : const_nodes ) + { + if ( !ntk.is_dead( ntk.get_node( n ) ) ) + { + ntk.substitute_node( ntk.get_node( n ), ntk.get_constant( ntk.is_complemented( n ) ) ); + } + } + } + } + + if constexpr ( use_odc ) + { + observability_check(); + if constexpr ( std::is_same_v ) + { + sim.pack_bits(); + call_with_stopwatch( st.time_sim, [&]() { + tts.reset(); + simulate_nodes( ntk, tts, sim, true ); + } ); + } + } + + if constexpr ( std::is_same_v ) + { + sim.randomize_dont_care_bits( ps.random_seed ); + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + } + } + +private: + void stuck_at_check() + { + progress_bar pbar{ ntk.size(), "patgen-sa |{0}| node = {1:>4} #pat = {2:>4}", ps.progress }; + + kitty::partial_truth_table zero = sim.compute_constant( false ); + + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, sim.num_bits() ); + + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + assert( zero.num_bits() == sim.num_bits() ); + + if ( ( tts[n] == zero ) || ( tts[n] == ~zero ) ) + { + bool value = ( tts[n] == zero ); /* wanted value of n */ + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( 0 ); + return validator.validate( n, !value ); + } ); + if ( !res ) + { + return true; /* timeout, next node */ + } + else if ( !( *res ) ) /* SAT, pattern found */ + { + if constexpr ( use_odc ) + { + /* check if the found pattern is observable */ + bool observable = call_with_stopwatch( st.time_odc, [&]() { + return pattern_is_observable( ntk, n, validator.cex, ps.odc_levels ); + } ); + if ( !observable ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] generated pattern is not observable (type 1). node: " << n << ", with value " << value << "\n"; + } + + const auto res2 = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, !value ); + } ); + if ( res2 ) + { + if ( !( *res2 ) ) + { + ++st.unobservable_type1; + if ( ps.verbose ) + { + assert( pattern_is_observable( ntk, n, validator.cex, ps.odc_levels ) ); + std::cout << "\t\t[i] unobservable pattern resolved.\n"; + } + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + } + + new_pattern( validator.cex, n ); + + if ( ps.num_stuck_at > 1 ) + { + auto generated = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.generate_pattern( n, value, { validator.cex }, ps.num_stuck_at - 1 ); + } ); + for ( auto& pattern : generated ) + { + new_pattern( pattern, n ); + } + } + + zero = sim.compute_constant( false ); + } + else /* UNSAT, constant node */ + { + ++st.num_constant; + const_nodes.emplace_back( value ? ntk.make_signal( n ) : !ntk.make_signal( n ) ); + return true; /* next gate */ + } + } + else if ( ps.num_stuck_at > 1 ) + { + auto const& tt = tts[n]; + if ( kitty::count_ones( tt ) < ps.num_stuck_at ) + { + generate_more_patterns( n, tt, true, zero ); + } + else if ( kitty::count_zeros( tt ) < ps.num_stuck_at ) + { + generate_more_patterns( n, tt, false, zero ); + } + } + return true; /* next gate */ + } ); + } + + void observability_check() + { + progress_bar pbar{ ntk.size(), "patgen-obs |{0}| node = {1:>4} #pat = {2:>4}", ps.progress }; + + kitty::partial_truth_table zero = sim.compute_constant( false ); + + ntk.foreach_gate( [&]( auto const& n, auto i ) { + pbar( i, i, sim.num_bits() ); + + for ( auto& f : const_nodes ) + { + if ( ntk.get_node( f ) == n ) + { + return true; /* skip constant nodes */ + } + } + + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + assert( zero.num_bits() == sim.num_bits() ); + + /* compute ODC */ + auto odc = call_with_stopwatch( st.time_odc, [&]() { + return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); + } ); + + /* check if under non-ODCs n is always the same value */ + if ( ( tts[n] & ~odc ) == zero ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] under all observable patterns, node " << n << " is always 0 (type 2).\n"; + } + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, false ); + } ); + if ( res ) + { + if ( !( *res ) ) + { + new_pattern( validator.cex, n ); + ++st.unobservable_type2; + + if ( ps.verbose ) + { + auto odc2 = call_with_stopwatch( st.time_odc, [&]() { return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); } ); + assert( ( tts[n] & ~odc2 ) != sim.compute_constant( false ) ); + std::cout << "\t\t[i] added generated pattern to resolve unobservability.\n"; + } + + zero = sim.compute_constant( false ); + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + else if ( ( tts[n] | odc ) == ~zero ) + { + if ( ps.verbose ) + { + std::cout << "\t[i] under all observable patterns, node " << n << " is always 1 (type 2).\n"; + } + + const auto res = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.validate( n, true ); + } ); + if ( res ) + { + if ( !( *res ) ) + { + new_pattern( validator.cex, n ); + ++st.unobservable_type2; + + if ( ps.verbose ) + { + auto odc2 = call_with_stopwatch( st.time_odc, [&]() { return observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); } ); + assert( ( tts[n] | odc2 ) != sim.compute_constant( true ) ); + std::cout << "\t\t[i] added generated pattern to resolve unobservability.\n"; + } + + zero = sim.compute_constant( false ); + } + else + { + ++st.unobservable_node; + if ( ps.verbose ) + { + std::cout << "\t\t[i] unobservable node " << n << "\n"; + } + } + } + } + + return true; /* next gate */ + } ); + } + +private: + void new_pattern( std::vector const& pattern, node const& n ) + { + if constexpr ( std::is_same_v ) + { + sim.add_pattern( pattern, compute_support( n ) ); + } + else + { + (void)n; + sim.add_pattern( pattern ); + } + + if constexpr ( has_EXCDC_interface_v ) + { + assert( !ntk.pattern_is_EXCDC( pattern ) ); + } + ++st.num_generated_patterns; + + /* re-simulate */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void generate_more_patterns( node const& n, kitty::partial_truth_table const& tt, bool value, kitty::partial_truth_table& zero ) + { + /* collect the `value` patterns */ + std::vector> patterns; + for ( auto i = 0u; i < tt.num_bits(); ++i ) + { + if ( kitty::get_bit( tt, i ) == value ) + { + patterns.emplace_back(); + ntk.foreach_pi( [&]( auto const& pi ) { + patterns.back().emplace_back( kitty::get_bit( tts[pi], i ) ); + } ); + } + } + + auto generated = call_with_stopwatch( st.time_sat, [&]() { + validator.set_odc_levels( ps.odc_levels ); + return validator.generate_pattern( n, value, patterns, ps.num_stuck_at - patterns.size() ); + } ); + for ( auto& pattern : generated ) + { + new_pattern( pattern, n ); + } + zero = sim.compute_constant( false ); + } + + std::vector compute_support( node const& n ) + { + ntk.incr_trav_id(); + if constexpr ( use_odc ) + { + if ( ps.odc_levels != 0 ) + { + std::vector leaves; + mark_fanout_leaves_rec( n, 1, leaves ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + leaves.emplace_back( ntk.get_node( f ) ); + } + } ); + + ntk.incr_trav_id(); + for ( auto& l : leaves ) + { + mark_support_rec( l ); + } + } + } + mark_support_rec( n ); + + std::vector care( ntk.num_pis(), false ); + ntk.foreach_pi( [&]( auto const& f, uint32_t i ) { + if ( ntk.visited( f ) == ntk.trav_id() ) + { + care[i] = true; + } + } ); + return care; + } + + void mark_support_rec( node const& n ) + { + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.visited( ntk.get_node( f ) ) == ntk.trav_id() ) + { + return true; + } + mark_support_rec( ntk.get_node( f ) ); + return true; + } ); + } + + void mark_fanout_leaves_rec( node const& n, int32_t level, std::vector& leaves ) + { + ntk.foreach_fanout( n, [&]( auto const& fo ) { + if ( ntk.visited( fo ) == ntk.trav_id() ) + { + return true; + } + ntk.set_visited( fo, ntk.trav_id() ); + + if ( level == ps.odc_levels ) + { + leaves.emplace_back( fo ); + return true; + } + + mark_fanout_leaves_rec( fo, level + 1, leaves ); + return true; + } ); + } + +private: + Ntk& ntk; + + pattern_generation_params const& ps; + pattern_generation_stats& st; + + validator_params& vps; + circuit_validator validator; + + TT tts; + std::vector const_nodes; + + Simulator& sim; +}; + +} /* namespace detail */ + +/*! \brief Expressive simulation pattern generation. + * + * This function implements two simulation pattern generation methods: + * stuck-at value checking and observability checking. Please refer to + * [1] for details of the algorithm and its purpose. + * + * [1] Simulation-Guided Boolean Resubstitution. IWLS 2020 (arXiv:2007.02579). + * + * \param sim Reference of a `partial_simulator` or `bit_packed_simulator` + * object where the generated patterns will be stored. + * It can be empty (`Simulator( ntk.num_pis(), 0 )`) + * or already containing some patterns generated from previous runs + * (`Simulator( filename )`) or randomly generated + * (`Simulator( ntk.num_pis(), num_random_patterns )`). The generated + * patterns can then be written out with `write_patterns` + * or directly be used by passing the simulator to another algorithm. + */ +template +void pattern_generation( Ntk& ntk, Simulator& sim, pattern_generation_params const& ps = {}, pattern_generation_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( std::is_same_v || std::is_same_v, "Simulator should be either partial_simulator or bit_packed_simulator" ); + + pattern_generation_stats st; + validator_params vps; + vps.conflict_limit = ps.conflict_limit; + vps.max_clauses = ps.max_clauses; + vps.random_seed = ps.random_seed; + + if ( ps.odc_levels != 0 ) + { + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::patgen_impl p( fanout_view, sim, ps, vps, st ); + p.run(); + } + else + { + detail::patgen_impl p( ntk, sim, ps, vps, st ); + p.run(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/reconv_cut.hpp b/include/mockturtle/algorithms/reconv_cut.hpp new file mode 100644 index 0000000..3c7e5fe --- /dev/null +++ b/include/mockturtle/algorithms/reconv_cut.hpp @@ -0,0 +1,461 @@ +/* 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 reconv_cut.hpp + \brief Implements reconvergence-driven cuts (based on ABC's + implementation in `abcReconv.c` by Alan Mishchenko). + + \author Heinz Riener +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for reconvergence-driven cut computation + * + * The data structure `reconvergence_driven_cut_parameters` holds configurable parameters + * with default arguments for `reconvergence_driven_cut_impl*`. + */ +struct reconvergence_driven_cut_parameters +{ + /* Maximum number of leaves */ + uint64_t max_leaves{ 8u }; + + /* Skip nodes with many fanouts */ + uint64_t max_fanouts{ 100000u }; + + /* Initially reserve memory for a fixed number of nodes */ + uint64_t reserve_memory_for_nodes{ 300u }; +}; + +/*! \brief Statistics for reconvergence-driven cut computation + * + * The data structure `reconvergence_driven_cut_statistics` holds data + * collected when running a reconvergence-driven cut computation + * algorithm. + */ +struct reconvergence_driven_cut_statistics +{ + /* Total number of calls */ + uint64_t num_calls{ 0 }; + + /* Total number of leaves */ + uint64_t num_leaves{ 0 }; + + /* Total number of nodes */ + uint64_t num_nodes{ 0 }; +}; + +/*! \cond PRIVATE */ +namespace detail +{ + +template +class reconvergence_driven_cut_impl +{ +public: + using parameters_type = reconvergence_driven_cut_parameters; + using statistics_type = reconvergence_driven_cut_statistics; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit reconvergence_driven_cut_impl( Ntk const& ntk, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + leaves.reserve( ps.max_leaves ); + if constexpr ( compute_nodes ) + { + nodes.reserve( ps.reserve_memory_for_nodes ); + } + } + + std::pair, std::vector> run( std::vector const& pivots ) + { + assert( pivots.size() > 0u ); + + /* prepare for traversal and clean internal state */ + ntk.incr_trav_id(); + nodes.clear(); + leaves.clear(); + + /* collect and mark all pivots */ + for ( const auto& pivot : pivots ) + { + if constexpr ( compute_nodes ) + { + nodes.emplace_back( pivot ); + } + ntk.set_visited( pivot, ntk.trav_id() ); + } + + leaves = pivots; + + if ( leaves.size() > ps.max_leaves ) + { + /* special case: cut already overflows at the current node because the cut size limit is very low */ + leaves.clear(); + nodes.clear(); + return { leaves, nodes }; + } + + /* compute the cut */ + while ( construct_cut() ) + ; + assert( leaves.size() <= ps.max_leaves ); + + /* update statistics */ + ++st.num_calls; + st.num_leaves += leaves.size(); + st.num_nodes += nodes.size(); + + return { leaves, nodes }; + } + +private: + bool construct_cut() + { + uint64_t best_cost{ std::numeric_limits::max() }; + std::optional best_fanin; + uint64_t best_position; + + /* evaluate fanins of the cut */ + uint64_t position{ 0 }; + for ( const auto& l : leaves ) + { + uint64_t const current_cost{ cost( l ) }; + if constexpr ( sort_equal_cost_by_level ) + { + if ( best_cost > current_cost || + ( best_cost == current_cost && best_fanin && ntk.level( l ) > ntk.level( *best_fanin ) ) ) + { + best_cost = current_cost; + best_fanin = std::make_optional( l ); + best_position = position; + } + } + else + { + if ( best_cost > current_cost ) + { + best_cost = current_cost; + best_fanin = std::make_optional( l ); + best_position = position; + } + } + + if ( best_cost == 0u ) + { + break; + } + + ++position; + } + + if ( !best_fanin ) + { + return false; + } + + if ( leaves.size() - 1 + best_cost > ps.max_leaves ) + { + return false; + } + + /* remove the best node from the array */ + leaves.erase( std::begin( leaves ) + best_position ); + + /* add the fanins of best to leaves and nodes */ + ntk.foreach_fanin( *best_fanin, [&]( signal const& fi ) { + node const& n = ntk.get_node( fi ); + if ( n != 0 && ( ntk.visited( n ) != ntk.trav_id() ) ) + { + ntk.set_visited( n, ntk.trav_id() ); + if constexpr ( compute_nodes ) + { + nodes.emplace_back( n ); + } + leaves.emplace_back( n ); + } + } ); + + assert( leaves.size() <= ps.max_leaves ); + return true; + } + + uint64_t cost( node const& n ) const + { + /* make sure the node is in the construction zone */ + assert( ntk.visited( n ) == ntk.trav_id() ); + + /* cannot expand over a constant or CI node */ + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + return std::numeric_limits::max(); + } + + /* count the number of leaves that we haven't visited */ + uint64_t cost{ 0 }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + cost += ntk.visited( ntk.get_node( fi ) ) != ntk.trav_id(); + } ); + + /* always accept if the number of leaves does not increase */ + if ( cost < ntk.fanin_size( n ) ) + { + return cost; + } + + /* skip nodes with many fanouts */ + if ( ntk.fanout_size( n ) > ps.max_fanouts ) + { + return std::numeric_limits::max(); + } + + /* return the number of nodes that will be on the leaves if this node is removed */ + return cost; + } + +private: + Ntk const& ntk; + reconvergence_driven_cut_parameters ps; + reconvergence_driven_cut_statistics& st; + + std::vector leaves; + std::vector nodes; +}; /* reconvergence_drive_cut_impl */ + +template +class reconvergence_driven_cut_impl2 +{ +public: + using parameters_type = reconvergence_driven_cut_parameters; + using statistics_type = reconvergence_driven_cut_statistics; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit reconvergence_driven_cut_impl2( Ntk const& ntk, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) + : ntk( ntk ), ps( ps ), st( st ) + { + } + + std::pair, std::vector> run( std::vector const& pivots ) + { + assert( pivots.size() > 0u ); + + /* prepare for traversal and clean internal state */ + ntk.incr_trav_id(); + nodes.clear(); + leaves.clear(); + assert( nodes.empty() ); + + for ( const auto& pivot : pivots ) + { + ntk.set_visited( pivot, ntk.trav_id() ); + } + + while ( construct_cut() ) + ; + assert( leaves.size() <= ps.max_leaves ); + + /* update statistics */ + ++st.num_calls; + st.num_leaves += leaves.size(); + st.num_nodes += nodes.size(); + + return { leaves, nodes }; + } + + bool construct_cut() + { + assert( leaves.size() <= ps.max_leaves && "cut-size overflow" ); + std::stable_sort( std::begin( leaves ), std::end( leaves ), + [this]( node const& a, node const& b ) { + return cost( a ) < cost( b ); + } ); + + /* find the first non-pi node to extend the cut (because the vector is sorted, this non-pi is cost-minimal) */ + auto const it = std::find_if( std::begin( leaves ), std::end( leaves ), + [&]( node const& n ) { + return !ntk.is_ci( n ); + } ); + if ( std::end( leaves ) == it ) + { + /* if all nodes are pis, then the cut cannot be extended */ + return false; + } + + /* the cost is identical to the number of nodes added to `leaves` if *it is used to expand leaves */ + int64_t const c = cost( *it ); + if ( leaves.size() + c > ps.max_leaves ) + { + /* if the expansion exceeds the cut_size, then the cut cannot be extended */ + return false; + } + + /* otherwise expand the cut with the children of *it and mark *it visited */ + node const n = *it; + leaves.erase( it ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const& child = ntk.get_node( fi ); + if ( !ntk.is_constant( child ) && std::find( std::begin( leaves ), std::end( leaves ), child ) == std::end( leaves ) && ntk.visited( child ) != ntk.trav_id() ) + { + leaves.emplace_back( child ); + ntk.set_visited( child, ntk.trav_id() ); + } + } ); + + assert( leaves.size() <= ps.max_leaves ); + return true; + } + + /* counts the number of non-constant leaves */ + int64_t cost( node const& n ) const + { + int32_t current_cost = -1; + ntk.foreach_fanin( n, [&]( signal const& s ) { + auto const& child = ntk.get_node( s ); + if ( !ntk.is_constant( child ) ) + { + ++current_cost; + } + } ); + return current_cost; + } + +private: + Ntk const& ntk; + reconvergence_driven_cut_parameters ps; + reconvergence_driven_cut_statistics& st; + + std::vector leaves; + std::vector nodes; +}; /* reconvergence_drive_cut_impl2 */ + +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, std::vector> const& pivots, reconvergence_driven_cut_parameters const& ps, reconvergence_driven_cut_statistics& st ) +{ + return Impl( ntk, ps, st ).run( pivots ); +} + +} // namespace detail +/*! \endcond */ + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a set of pivot nodes. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, std::vector> const& pivots, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + if constexpr ( sort_equal_cost_by_level ) + { + static_assert( has_level_v, "Ntk does not implement the level method" ); + } + + using Impl = detail::reconvergence_driven_cut_impl; + + reconvergence_driven_cut_statistics st; + auto const result = detail::reconvergence_driven_cut( ntk, pivots, ps, st ); + if ( pst ) + { + *pst = st; + } + return result; +} + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a single pivot node. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, node const& pivot, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + return reconvergence_driven_cut( ntk, std::vector>{ pivot }, ps, pst ); +} + +/*! \brief Reconvergence-driven cut towards inputs. + * + * This class implements a generation algorithm for + * reconvergence-driven cuts. The cut grows towards the primary + * inputs starting from a single pivot signal. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `get_node` + * - `visited` + * - `has_visited` + * - `foreach_fanin` + * + */ +template +std::pair>, std::vector>> reconvergence_driven_cut( Ntk const& ntk, signal const& pivot, reconvergence_driven_cut_parameters const& ps = {}, reconvergence_driven_cut_statistics* pst = nullptr ) +{ + return reconvergence_driven_cut( ntk, std::vector>{ ntk.get_node( pivot ) }, ps, pst ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/refactoring.hpp b/include/mockturtle/algorithms/refactoring.hpp new file mode 100644 index 0000000..dbdef72 --- /dev/null +++ b/include/mockturtle/algorithms/refactoring.hpp @@ -0,0 +1,420 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file refactoring.hpp + \brief Refactoring + + \author Alessandro Tempia Calvino + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ +#pragma once + +#include "../networks/mig.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/cut_view.hpp" +#include "../views/mffc_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "../views/color_view.hpp" +#include "cleanup.hpp" +#include "detail/mffc_utils.hpp" +#include "dont_cares.hpp" +#include "simulation.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for refactoring. + * + * The data structure `refactoring_params` holds configurable parameters with + * default arguments for `refactoring`. + */ +struct refactoring_params +{ + /*! \brief Maximum number of PIs of the MFFC or window. */ + uint32_t max_pis{ 6 }; + + /*! \brief Allow zero-gain substitutions */ + bool allow_zero_gain{ false }; + + /*! \brief Extract a reconvergence-driven cut for large MFFcs */ + bool use_reconvergence_cut{ true }; + + /*! \brief Use don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for refactoring. + * + * The data structure `refactoring_stats` provides data collected by running + * `refactoring`. + */ +struct refactoring_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime for computing MFFCs. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for rewriting. */ + stopwatch<>::duration time_refactoring{ 0 }; + + /*! \brief Accumulated runtime for simulating MFFCs. */ + stopwatch<>::duration time_simulation{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + std::cout << fmt::format( "[i] MFFC time = {:>5.2f} secs\n", to_seconds( time_mffc ) ); + std::cout << fmt::format( "[i] refactoring time = {:>5.2f} secs\n", to_seconds( time_refactoring ) ); + std::cout << fmt::format( "[i] simulation time = {:>5.2f} secs\n", to_seconds( time_simulation ) ); + } +}; + +namespace detail +{ + +template +struct has_refactoring_with_dont_cares : std::false_type +{ +}; + +template +struct has_refactoring_with_dont_cares()( std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval(), + std::declval )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_refactoring_with_dont_cares_v = has_refactoring_with_dont_cares::value; + +template +class refactoring_impl +{ +public: + refactoring_impl( Ntk& ntk, RefactoringFn&& refactoring_fn, refactoring_params const& ps, refactoring_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), refactoring_fn( refactoring_fn ), ps( ps ), st( st ), cost_fn( cost_fn ) {} + + void run() + { + progress_bar pbar{ ntk.size(), "refactoring |{0}| node = {1:>4} cand = {2:>4} est. reduction = {3:>5}", ps.progress }; + + stopwatch t( st.time_total ); + + ntk.clear_visited(); + + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.max_pis; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl reconv_cuts( ntk, rps, rst ); + + color_view color_ntk{ ntk }; + + const auto size = ntk.num_gates(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( i >= size ) + { + return false; + } + if ( ntk.fanout_size( n ) == 0u ) + { + return true; + } + + ntk.foreach_node( [&]( auto n ){ + ntk.set_value( n, ntk.fanout_size( n ) ); + }); + + const auto mffc = make_with_stopwatch>( st.time_mffc, ntk, n ); + + pbar( i, i, _candidates, _estimated_gain ); + + if ( mffc.num_pos() == 0 || ( !ps.use_reconvergence_cut && mffc.num_pis() > ps.max_pis ) || mffc.size() < 4 ) + { + return true; + } + + kitty::dynamic_truth_table tt; + std::vector> leaves( ps.max_pis ); + uint32_t num_leaves = 0; + + if ( mffc.num_pis() <= ps.max_pis ) + { + /* use MFFC */ + mffc.foreach_pi( [&]( auto const& m, auto j ) { + leaves[j] = ntk.make_signal( m ); + } ); + + num_leaves = mffc.num_pis(); + + default_simulator sim( mffc.num_pis() ); + tt = call_with_stopwatch( st.time_simulation, + [&]() { return simulate( mffc, sim )[0]; } ); + } + else + { + /* compute a reconvergent-driven cut */ + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + + num_leaves = extended_leaves.size(); + assert( num_leaves <= ps.max_pis ); + + for ( auto j = 0u; j < num_leaves; ++j ) + { + leaves[j] = ntk.make_signal( extended_leaves[j] ); + } + + cut_view cut( ntk, extended_leaves, ntk.make_signal( n ) ); + default_simulator sim( num_leaves ); + tt = call_with_stopwatch( st.time_simulation, + [&]() { return simulate( cut, sim )[0]; } ); + } + + signal new_f; + bool resynthesized{ false }; + + ntk.incr_trav_id(); + int32_t gain = recursive_deref_mark( n ); + + { + if ( ps.use_dont_cares ) + { + if constexpr ( has_refactoring_with_dont_cares_v ) + { + std::vector> pivots; + for ( auto const& c : leaves ) + { + pivots.push_back( ntk.get_node( c ) ); + } + stopwatch t( st.time_refactoring ); + + refactoring_fn( ntk, tt, satisfiability_dont_cares( ntk, pivots, 16u ), leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + else + { + stopwatch t( st.time_refactoring ); + refactoring_fn( ntk, tt, leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + } + else + { + stopwatch t( st.time_refactoring ); + refactoring_fn( ntk, tt, leaves.begin(), leaves.begin() + num_leaves, [&]( auto const& f ) { new_f = f; resynthesized = true; return false; } ); + } + } + + if ( !resynthesized || n == ntk.get_node( new_f ) ) + { + recursive_ref( n ); + return true; + } + + /* ref only if it is a new node */ + if ( ntk.fanout_size( ntk.get_node( new_f ) ) == 0 ) + { + recursive_deref_check_mark( ntk.get_node( new_f ) ); + gain -= recursive_ref( ntk.get_node( new_f ) ); + } + + recursive_ref( n ); + + if ( gain > 0 || ( ps.allow_zero_gain && gain == 0 ) ) + { + ++_candidates; + _estimated_gain += gain; + ntk.substitute_node( n, new_f ); + } + else + { + /* remove */ + if ( ntk.fanout_size( ntk.get_node( new_f ) ) == 0 ) + ntk.take_out_node( ntk.get_node( new_f ) ); + } + return true; + } ); + } + +private: + uint32_t recursive_deref_mark( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + ntk.set_visited( n, ntk.trav_id() ); + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref_mark( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_deref_check_mark( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + if ( ntk.visited( n ) == ntk.trav_id() ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref_check_mark( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_ref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk.get_node( s ) ); + } + } ); + return value; + } + +private: + Ntk& ntk; + RefactoringFn&& refactoring_fn; + refactoring_params const& ps; + refactoring_stats& st; + NodeCostFn cost_fn; + + uint32_t _candidates{ 0 }; + uint32_t _estimated_gain{ 0 }; +}; + +} /* namespace detail */ + +/*! \brief Boolean refactoring. + * + * This algorithm performs refactoring by collapsing maximal fanout-free cones + * (MFFCs) into truth tables and recreating a new network structure from it. + * If the MFFC is too large a reconvergence-driven cut is extracted. + * The algorithm performs changes directly in the input network and keeps the + * substituted structures dangling in the network. They can be cleaned up using + * the `cleanup_dangling` algorithm. + * + * The refactoring function must be of type `NtkDest::signal(NtkDest&, + * kitty::dynamic_truth_table const&, LeavesIterator, LeavesIterator)` where + * `LeavesIterator` can be dereferenced to a `NtkDest::signal`. The last two + * parameters compose an iterator pair where the distance matches the number of + * variables of the truth table that is passed as second parameter. There are + * some refactoring algorithms in the folder + * `mockturtle/algorithms/node_resyntesis`, since the resynthesis functions + * have the same signature. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `make_signal` + * - `foreach_gate` + * - `substitute_node` + * - `clear_visited` + * - `clear_values` + * - `fanout_size` + * - `set_value` + * - `foreach_node` + * + * \param ntk Input network (will be changed in-place) + * \param refactoring_fn Refactoring function + * \param ps Refactoring params + * \param pst Refactoring statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void refactoring( Ntk& ntk, RefactoringFn&& refactoring_fn, refactoring_params const& ps = {}, refactoring_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + 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_size_v, "Ntk does not implement the size method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + fanout_view f_ntk{ ntk }; + + refactoring_stats st; + detail::refactoring_impl, RefactoringFn, NodeCostFn> p( f_ntk, refactoring_fn, ps, st, cost_fn ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/resubstitution.hpp b/include/mockturtle/algorithms/resubstitution.hpp new file mode 100644 index 0000000..d8ed920 --- /dev/null +++ b/include/mockturtle/algorithms/resubstitution.hpp @@ -0,0 +1,859 @@ +/* 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 resubstitution.hpp + \brief Generic resubstitution framework + + \author Eleonora Testa + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +#include "detail/resub_utils.hpp" +#include "dont_cares.hpp" +#include "reconv_cut.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for resubstitution. + * + * The data structure `resubstitution_params` holds configurable parameters with + * default arguments for `resubstitution`. + */ +struct resubstitution_params +{ + /*! \brief Maximum number of PIs of reconvergence-driven cuts. */ + uint32_t max_pis{ 8 }; + + /*! \brief Maximum number of divisors to consider. */ + uint32_t max_divisors{ 150 }; + + /*! \brief Maximum number of nodes added by resubstitution. */ + uint32_t max_inserts{ 2 }; + + /*! \brief Maximum fanout of a node to be considered as root. */ + uint32_t skip_fanout_limit_for_roots{ 1000 }; + + /*! \brief Maximum fanout of a node to be considered as divisor. */ + uint32_t skip_fanout_limit_for_divisors{ 100 }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /****** window-based resub engine ******/ + + /*! \brief Use don't cares for optimization. Only used by window-based resub engine. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. Only used by window-based resub engine. */ + uint32_t window_size{ 12u }; + + /*! \brief Whether to prevent from increasing depth. Currently only used by window-based resub engine. */ + bool preserve_depth{ false }; + + /****** simulation-based resub engine ******/ + + /*! \brief Whether to use pre-generated patterns stored in a file. + * If not, by default, 1024 random pattern + 1x stuck-at patterns will be generated. Only used by simulation-based resub engine. + */ + std::optional pattern_filename{}; + + /*! \brief Whether to save the appended patterns (with CEXs) into file. Only used by simulation-based resub engine. */ + std::optional save_patterns{}; + + /*! \brief Maximum number of clauses of the SAT solver. Only used by simulation-based resub engine. */ + uint32_t max_clauses{ 1000 }; + + /*! \brief Conflict limit for the SAT solver. Only used by simulation-based resub engine. */ + uint32_t conflict_limit{ 1000 }; + + /*! \brief Random seed for the SAT solver (influences the randomness of counter-examples). Only used by simulation-based resub engine. */ + uint32_t random_seed{ 1 }; + + /*! \brief Whether to utilize ODC, and how many levels. 0 = no. -1 = Consider TFO until PO. Only used by simulation-based resub engine. */ + int32_t odc_levels{ 0 }; + + /*! \brief Maximum number of trials to call the resub functor. Only used by simulation-based resub engine. */ + uint32_t max_trials{ 100 }; + + /* k-resub engine specific */ + /*! \brief Maximum number of divisors to consider in k-resub engine. Only used by `abc_resub_functor` with simulation-based resub engine. */ + uint32_t max_divisors_k{ 50 }; +}; + +/*! \brief Statistics for resubstitution. + * + * The data structure `resubstitution_stats` provides data collected by running + * `resubstitution`. + */ +struct resubstitution_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Accumulated runtime of the divisor collector. */ + stopwatch<>::duration time_divs{ 0 }; + + /*! \brief Accumulated runtime of the resub engine. */ + stopwatch<>::duration time_resub{ 0 }; + + /*! \brief Accumulated runtime of the callback function. */ + stopwatch<>::duration time_callback{ 0 }; + + /*! \brief Total number of divisors. */ + uint64_t num_total_divisors{ 0 }; + + /*! \brief Total number of gain. */ + uint64_t estimated_gain{ 0 }; + + /*! \brief Initial network size (before resubstitution). */ + uint64_t initial_size{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] ======== Stats ========\n" ); + fmt::print( "[i] #divisors = {:8d}\n", num_total_divisors ); + fmt::print( "[i] est. gain = {:8d} ({:>5.2f}%)\n", estimated_gain, ( 100.0 * estimated_gain ) / initial_size ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] total : {:>5.2f} secs\n", to_seconds( time_total ) ); + fmt::print( "[i] DivCollector: {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( "[i] ResubEngine : {:>5.2f} secs\n", to_seconds( time_resub ) ); + fmt::print( "[i] callback : {:>5.2f} secs\n", to_seconds( time_callback ) ); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +namespace detail +{ + +template +bool substitute_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + ntk.substitute_node( n, g ); + return true; +} + +template +bool report_fn( Ntk& ntk, typename Ntk::node const& n, typename Ntk::signal const& g ) +{ + fmt::print( "[i] Substitute node {} with signal {}{}\n", n, ntk.is_complemented( g ) ? "!" : "", ntk.get_node( g ) ); + return false; +} + +struct default_collector_stats +{ + /*! \brief Total number of leaves. */ + uint64_t num_total_leaves{ 0 }; + + /*! \brief Accumulated runtime for cut computation. */ + stopwatch<>::duration time_cuts{ 0 }; + + /*! \brief Accumulated runtime for mffc computation. */ + stopwatch<>::duration time_mffc{ 0 }; + + /*! \brief Accumulated runtime for divisor computation. */ + stopwatch<>::duration time_divs{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] #leaves = {:6d}\n", num_total_leaves ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] reconv. cut : {:>5.2f} secs\n", to_seconds( time_cuts ) ); + fmt::print( "[i] MFFC : {:>5.2f} secs\n", to_seconds( time_mffc ) ); + fmt::print( "[i] divs collect: {:>5.2f} secs\n", to_seconds( time_divs ) ); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +/*! \brief Prepare the three public data members `leaves`, `divs` and `mffc` + * to be ready for usage. + * + * `leaves`: sufficient support for all divisors + * `divs`: divisor nodes that can be used for resubstitution + * `mffc`: MFFC nodes which are needed to do simulation from + * `leaves`, through `divs` and `mffc` until the root node, + * but should be excluded from resubstitution. + * The last element of `mffc` is always the root node. + * + * `divs` and `mffc` are in topological order. + * + * \param MffcMgr Manager class to compute the potential gain if a + * resubstitution exists (number of MFFC nodes when the cost function is circuit size). + * \param MffcRes Typename of the return value of `MffcMgr`. + * \param cut_comp Manager class to compute reconvergence-driven cuts. + */ +template, typename MffcRes = uint32_t, typename cut_comp = detail::reconvergence_driven_cut_impl> +class default_divisor_collector +{ +public: + using stats = default_collector_stats; + using mffc_result_t = MffcRes; + using node = typename Ntk::node; + + using cut_comp_parameters_type = typename cut_comp::parameters_type; + using cut_comp_statistics_type = typename cut_comp::statistics_type; + +public: + explicit default_divisor_collector( Ntk const& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), cuts( ntk, cut_comp_parameters_type{ ps.max_pis }, cuts_st ) + { + } + + bool run( node const& n, mffc_result_t& potential_gain ) + { + /* skip nodes with many fanouts */ + if ( ntk.fanout_size( n ) > ps.skip_fanout_limit_for_roots ) + { + return false; + } + + /* compute a reconvergence-driven cut */ + leaves = call_with_stopwatch( st.time_cuts, [&]() { + return cuts.run( { n } ).first; + } ); + st.num_total_leaves += leaves.size(); + + /* collect the MFFC */ + MffcMgr mffc_mgr( ntk ); + potential_gain = call_with_stopwatch( st.time_mffc, [&]() { + return mffc_mgr.run( n, leaves, mffc ); + } ); + + /* collect the divisor nodes in the cut */ + bool div_comp_success = call_with_stopwatch( st.time_divs, [&]() { + return collect_divisors( n ); + } ); + + if ( !div_comp_success ) + { + return false; + } + + return true; + } + +private: + void collect_divisors_rec( node const& n ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + return; + } + ntk.set_visited( n, ntk.trav_id() ); + + ntk.foreach_fanin( n, [&]( const auto& f ) { + collect_divisors_rec( ntk.get_node( f ) ); + } ); + + /* collect the internal nodes */ + if ( ntk.value( n ) == 0 && n != 0 ) /* ntk.fanout_size( n ) */ + { + divs.emplace_back( n ); + } + } + + bool collect_divisors( node const& root ) + { + auto max_depth = std::numeric_limits::max(); + if ( ps.preserve_depth ) + { + max_depth = ntk.level( root ); + } + /* add the leaves of the cuts to the divisors */ + divs.clear(); + + ntk.incr_trav_id(); + for ( const auto& l : leaves ) + { + divs.emplace_back( l ); + ntk.set_visited( l, ntk.trav_id() ); + } + + /* mark nodes in the MFFC */ + for ( const auto& t : mffc ) + { + ntk.set_value( t, 1 ); + } + + /* collect the cone (without MFFC) */ + collect_divisors_rec( root ); + + /* unmark the current MFFC */ + for ( const auto& t : mffc ) + { + ntk.set_value( t, 0 ); + } + + /* check if the number of divisors is not exceeded */ + if ( divs.size() + mffc.size() - leaves.size() > ps.max_divisors - ps.max_pis ) + { + return false; + } + uint32_t limit = ps.max_divisors - ps.max_pis - mffc.size() + leaves.size(); + + /* explore the fanouts, which are not in the MFFC */ + bool quit = false; + for ( auto i = 0u; i < divs.size(); ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.fanout_size( d ) > ps.skip_fanout_limit_for_divisors ) + { + continue; + } + if ( divs.size() >= limit ) + { + break; + } + + /* if the fanout has all fanins in the set, add it */ + ntk.foreach_fanout( d, [&]( node const& p ) { + if ( ntk.visited( p ) == ntk.trav_id() || ntk.level( p ) > max_depth ) + { + return true; /* next fanout */ + } + + bool all_fanins_visited = true; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.visited( ntk.get_node( g ) ) != ntk.trav_id() ) + { + all_fanins_visited = false; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( !all_fanins_visited ) + return true; /* next fanout */ + + bool has_root_as_child = false; + ntk.foreach_fanin( p, [&]( const auto& g ) { + if ( ntk.get_node( g ) == root ) + { + has_root_as_child = true; + return false; /* terminate fanin-loop */ + } + return true; /* next fanin */ + } ); + + if ( has_root_as_child ) + { + return true; /* next fanout */ + } + + divs.emplace_back( p ); + ntk.set_visited( p, ntk.trav_id() ); + + /* quit computing divisors if there are too many of them */ + if ( divs.size() >= limit ) + { + quit = true; + return false; /* terminate fanout-loop */ + } + + return true; /* next fanout */ + } ); + + if ( quit ) + { + break; + } + } + + /* note: different from the previous version, now we do not add MFFC nodes into divs */ + assert( root == mffc.at( mffc.size() - 1u ) ); + /* note: this assertion makes sure window_simulator does not go out of bounds */ + assert( divs.size() + mffc.size() - leaves.size() <= ps.max_divisors - ps.max_pis ); + + return true; + } + +private: + Ntk const& ntk; + resubstitution_params ps; + stats& st; + + cut_comp cuts; + cut_comp_statistics_type cuts_st; + +public: + std::vector leaves; + std::vector divs; + std::vector mffc; +}; + +template +struct window_resub_stats +{ + /*! \brief Number of successful resubstitutions. */ + uint32_t num_resub{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for don't-care computation. */ + stopwatch<>::duration time_dont_care{ 0 }; + + /*! \brief Time of the resub functor. */ + stopwatch<>::duration time_compute_function{ 0 }; + + ResubFnSt functor_st; + + void report() const + { + // clang-format off + fmt::print( "[i] \n" ); + fmt::print( "[i] #resub = {:6d}\n", num_resub ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] simulation: {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( "[i] don't care: {:>5.2f} secs\n", to_seconds( time_dont_care ) ); + fmt::print( "[i] functor : {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + fmt::print( "[i] ======== Details ========\n" ); + functor_st.report(); + fmt::print( "[i] =========================\n\n" ); + // clang-format on + } +}; + +/*! \brief Window-based resubstitution engine. + * + * This engine computes the complete truth tables of nodes within a window + * with the leaves as inputs. It does not verify the resubstitution candidates + * given by the resubstitution functor. This engine requires the divisor + * collector to prepare three data members: `leaves`, `divs` and `mffc`. + * + * Required interfaces of the resubstitution functor: + * - Constructor: `resub_fn( Ntk const& ntk, Simulator const& sim,` + * `std::vector const& divs, uint32_t num_divs, ResubFnSt& st )` + * - A public `operator()`: `std::optional operator()` + * `( node const& root, TTdc care, uint32_t required, uint32_t max_inserts,` + * `MffcRes potential_gain, uint32_t& last_gain ) const` + * + * Compatible resubstitution functors implemented: + * - `default_resub_functor` + * - `aig_resub_functor` + * - `mig_resub_functor` + * - `xmg_resub_functor` + * - `xag_resub_functor` + * - `mig_resyn_functor` + * + * \param TTsim Truth table type for simulation. + * \param TTdc Truth table type for don't-care computation. + * \param ResubFn Resubstitution functor to compute the resubstitution. + * \param MffcRes Typename of `potential_gain` needed by the resubstitution functor. + */ +template, TTdc>, typename MffcRes = uint32_t> +class window_based_resub_engine +{ +public: + static constexpr bool require_leaves_and_mffc = true; + using stats = window_resub_stats; + using mffc_result_t = MffcRes; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit window_based_resub_engine( Ntk& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), sim( ntk, ps.max_divisors, ps.max_pis ) + { + } + + void init() {} + void update() {} + + std::optional run( node const& n, std::vector const& leaves, std::vector const& divs, std::vector const& mffc, mffc_result_t potential_gain, uint32_t& last_gain ) + { + /* simulate the collected divisors */ + call_with_stopwatch( st.time_sim, [&]() { + simulate( leaves, divs, mffc ); + } ); + + auto care = kitty::create( ps.max_pis ); + call_with_stopwatch( st.time_dont_care, [&]() { + if ( ps.use_dont_cares ) + { + care = ~satisfiability_dont_cares( ntk, leaves, ps.window_size ); + } + else + { + care = ~care; + } + } ); + + ResubFn resub_fn( ntk, sim, divs, divs.size(), st.functor_st ); + auto res = call_with_stopwatch( st.time_compute_function, [&]() { + auto max_depth = std::numeric_limits::max(); + if ( ps.preserve_depth ) + { + max_depth = ntk.level( n ); + } + return resub_fn( n, care, max_depth, ps.max_inserts, potential_gain, last_gain ); + } ); + if ( res ) + { + ++st.num_resub; + } + return res; + } + +private: + void simulate( std::vector const& leaves, std::vector const& divs, std::vector const& mffc ) + { + sim.resize(); + for ( auto i = 0u; i < divs.size() + mffc.size(); ++i ) + { + const auto d = i < divs.size() ? divs.at( i ) : mffc.at( i - divs.size() ); + + /* skip constant 0 */ + if ( d == 0 ) + continue; + + /* assign leaves to variables */ + if ( i < leaves.size() ) + { + sim.assign( d, i + 1 ); + continue; + } + + /* compute truth tables of inner nodes */ + sim.assign( d, i - uint32_t( leaves.size() ) + ps.max_pis + 1 ); + std::vector tts; + ntk.foreach_fanin( d, [&]( const auto& s ) { + tts.emplace_back( sim.get_tt( ntk.make_signal( ntk.get_node( s ) ) ) ); /* ignore sign */ + } ); + + auto const tt = ntk.compute( d, tts.begin(), tts.end() ); + sim.set_tt( i - uint32_t( leaves.size() ) + ps.max_pis + 1, tt ); + } + + /* normalize truth tables */ + sim.normalize( divs ); + sim.normalize( mffc ); + } + +private: + Ntk& ntk; + resubstitution_params const& ps; + stats& st; + + window_simulator sim; +}; /* window_based_resub_engine */ + +/*! \brief The top-level resubstitution framework. + * + * \param ResubEngine The engine that computes the resubtitution for a given root + * node and divisors. One can choose from `window_based_resub_engine` which + * does complete simulation within small windows, or `simulation_based_resub_engine` + * which does partial simulation on the whole circuit. + * + * \param DivCollector Collects divisors near a given root node, and compute + * the potential gain (MFFC size or its variants). + * Currently only `default_divisor_collector` is implemented, but + * a frontier-based approach may be integrated in the future. + * When using `window_based_resub_engine`, the `DivCollector` should prepare + * three public data members: `leaves`, `divs`, and `mffc` (see documentation + * of `default_divisor_collector` for details). When using `simulation_based_resub_engine`, + * only `divs` is needed. + */ +template, class DivCollector = default_divisor_collector> +class resubstitution_impl +{ +public: + using engine_st_t = typename ResubEngine::stats; + using collector_st_t = typename DivCollector::stats; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using resub_callback_t = std::function; + using mffc_result_t = typename ResubEngine::mffc_result_t; + + /*! \brief Constructor of the top-level resubstitution framework. + * + * \param ntk The network to be optimized. + * \param ps Resubstitution parameters. + * \param st Top-level resubstitution statistics. + * \param engine_st Statistics of the resubstitution engine. + * \param collector_st Statistics of the divisor collector. + * \param callback Callback function when a resubstitution is found. + */ + explicit resubstitution_impl( Ntk& ntk, resubstitution_params const& ps, resubstitution_stats& st, engine_st_t& engine_st, collector_st_t& collector_st ) + : ntk( ntk ), ps( ps ), st( st ), engine_st( engine_st ), collector_st( collector_st ) + { + static_assert( std::is_same_v, "MFFC result type of the engine and the collector are different" ); + + st.initial_size = ntk.num_gates(); + + register_events(); + } + + ~resubstitution_impl() + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + + void run( resub_callback_t const& callback = substitute_fn ) + { + stopwatch t( st.time_total ); + + /* start the managers */ + DivCollector collector( ntk, ps, collector_st ); + ResubEngine resub_engine( ntk, ps, engine_st ); + call_with_stopwatch( st.time_resub, [&]() { + resub_engine.init(); + } ); + + progress_bar pbar{ ntk.size(), "resub |{0}| node = {1:>4} cand = {2:>4} est. gain = {3:>5}", ps.progress }; + + auto const size = ntk.num_gates(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( i >= size ) + { + return false; /* terminate */ + } + + pbar( i, i, candidates, st.estimated_gain ); + + /* compute cut, collect divisors, compute MFFC */ + mffc_result_t potential_gain; + const auto collector_success = call_with_stopwatch( st.time_divs, [&]() { + return collector.run( n, potential_gain ); + } ); + if ( !collector_success ) + { + return true; /* next */ + } + + /* update statistics */ + last_gain = 0; + st.num_total_divisors += collector.divs.size(); + + /* try to find a resubstitution with the divisors */ + auto g = call_with_stopwatch( st.time_resub, [&]() { + if constexpr ( ResubEngine::require_leaves_and_mffc ) /* window-based */ + { + return resub_engine.run( n, collector.leaves, collector.divs, collector.mffc, potential_gain, last_gain ); + } + else /* simulation-based */ + { + return resub_engine.run( n, collector.divs, potential_gain, last_gain ); + } + } ); + if ( !g ) + { + return true; /* next */ + } + + /* update progress bar */ + candidates++; + st.estimated_gain += last_gain; + + /* update network */ + bool updated = call_with_stopwatch( st.time_callback, [&]() { + return callback( ntk, n, *g ); + } ); + if ( updated ) + { + resub_engine.update(); + } + + return true; /* next */ + } ); + } + +private: + void register_events() + { + auto const update_level_of_new_node = [&]( const auto& n ) { + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_deleted_node = [&]( const auto& n ) { + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + + /* maybe should move to depth_view */ + void update_node_level( node const& n, bool top_most = true ) + { + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( top_most ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); + } + } + } + +private: + Ntk& ntk; + + resubstitution_params const& ps; + resubstitution_stats& st; + engine_st_t& engine_st; + collector_st_t& collector_st; + + /* temporary statistics for progress bar */ + uint32_t candidates{ 0 }; + uint32_t last_gain{ 0 }; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +} /* namespace detail */ + +/*! \brief Window-based Boolean resubstitution with default resub functor (only div0). */ +template +void default_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if ( ps.max_pis == 8 ) + { + using truthtable_t = kitty::static_truth_table<8>; + using truthtable_dc_t = kitty::dynamic_truth_table; + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } + else + { + using resub_impl_t = detail::resubstitution_impl; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp b/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp new file mode 100644 index 0000000..51c7e9c --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/aig_enumerative.hpp @@ -0,0 +1,472 @@ +/* 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 aig_enumerative.hpp + \brief AIG enumerative resynthesis + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee + + Based on previous implementation of AIG resubstitution by + Eleonora Testa, Heinz Riener, and Mathias Soeken +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include +#include +#include + +namespace mockturtle +{ + +struct aig_enumerative_resyn_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted single AND-resubsitutions */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted single OR-resubsitutions */ + uint64_t num_div1_or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + /*! \brief Number of accepted single 2AND-resubsitutions */ + uint64_t num_div12_2and_accepts{ 0 }; + + /*! \brief Number of accepted single 2OR-resubsitutions */ + uint64_t num_div12_2or_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted double AND-OR-resubsitutions */ + uint64_t num_div2_and_or_accepts{ 0 }; + + /*! \brief Number of accepted double OR-AND-resubsitutions */ + uint64_t num_div2_or_and_accepts{ 0 }; + + /*! \brief Number of accepted three resubsitutions */ + uint64_t num_div3_accepts{ 0 }; + + /*! \brief Number of accepted AND-2OR-resubsitutions */ + uint64_t num_div3_and_2or_accepts{ 0 }; + + /*! \brief Number of accepted OR-2AND-resubsitutions */ + uint64_t num_div3_or_2and_accepts{ 0 }; + + void report() const + { + // clang-format off + fmt::print( "[i] aig_enumerative_resyn_stats\n" ); + fmt::print( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + fmt::print( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + fmt::print( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + fmt::print( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + fmt::print( "[i] 2-resub {:6d} = {:6d} 2AND + {:6d} 2OR ({:>5.2f} secs)\n", + num_div12_accepts, num_div12_2and_accepts, num_div12_2or_accepts, to_seconds( time_resub12 ) ); + fmt::print( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + fmt::print( "[i] 2-resub {:6d} = {:6d} AND-OR + {:6d} OR-AND ({:>5.2f} secs)\n", + num_div2_accepts, num_div2_and_or_accepts, num_div2_or_and_accepts, to_seconds( time_resub2 ) ); + fmt::print( "[i] 3-resub {:6d} = {:6d} AND-2OR + {:6d} OR-2AND ({:>5.2f} secs)\n", + num_div3_accepts, num_div3_and_2or_accepts, num_div3_or_2and_accepts, to_seconds( time_resub3 ) ); + fmt::print( "[i] total {:6d}\n", + (num_const_accepts + num_div0_accepts + num_div1_accepts + num_div12_accepts + num_div2_accepts + num_div3_accepts) ); + // clang-format on + } +}; /* aig_enumerative_resyn_stats */ + +template +struct aig_enumerative_resyn +{ +public: + using stats = aig_enumerative_resyn_stats; + using index_list_t = xag_index_list; + using truth_table_t = TT; + +public: + explicit aig_enumerative_resyn( stats& st ) noexcept + : st( st ) + {} + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + (void)care; + assert( is_const0( ~care ) && "enumerative resynthesis does not support don't cares" ); + + index_list_t il( std::distance( begin, end ) ); + const TT ntarget = ~target; + uint32_t i, j, k, l; + iterator_type it = begin; + + /* C-resub */ + if ( kitty::is_const0( target ) ) + { + il.add_output( 0 ); + return il; + } + if constexpr ( !normalized ) + { + if ( kitty::is_const0( ntarget ) ) + { + il.add_output( 1 ); + return il; + } + } + + /* 0-resub */ + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( target == tts[*it] ) + { + il.add_output( make_lit( i ) ); + return il; + } + if constexpr ( !normalized ) + { + if ( ntarget == tts[*it] ) + { + assert( !normalized ); + il.add_output( make_lit( i, true ) ); + return il; + } + } + } + + if ( max_size == 0 ) + { + return std::nullopt; + } + + /* collect unate literals */ + std::vector pos_unate, neg_unate, binate; + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( kitty::implies( tts[*it], target ) ) + { + pos_unate.emplace_back( make_lit( i ) ); + } + else if ( kitty::implies( target, tts[*it] ) ) + { + neg_unate.emplace_back( make_lit( i ) ); + } + else if ( kitty::implies( target, ~tts[*it] ) ) + { + neg_unate.emplace_back( make_lit( i, true ) ); + } + else + { + if constexpr ( !normalized ) + { + if ( kitty::implies( ~tts[*it], target ) ) + { + pos_unate.emplace_back( make_lit( i, true ) ); + } + else + { + binate.emplace_back( make_lit( i ) ); + } + } + else + { + binate.emplace_back( make_lit( i ) ); + } + } + } + + /* 1-resub */ + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) ) ) + { + il.add_output( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ) ^ 0x1 ); // OR + return il; + } + } + } + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) ) ) + { + il.add_output( il.add_and( neg_unate[i], neg_unate[j] ) ); // AND + return il; + } + } + } + + if ( max_size == 1 ) + { + return std::nullopt; + } + + /* 2-resub */ + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + for ( k = j + 1; k < pos_unate.size(); ++k ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) | get_tt_from_lit( pos_unate[k], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ), pos_unate[k] ^ 0x1 ) ^ 0x1 ); // OR-OR + return il; + } + } + } + } + + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + for ( k = j + 1; k < neg_unate.size(); ++k ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) & get_tt_from_lit( neg_unate[k], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( neg_unate[i], neg_unate[j] ), neg_unate[k] ) ); // AND-AND + return il; + } + } + } + } + + /* collect binate divisors */ + std::vector> neg_binates, pos_binates; + + for ( i = 0u; i < binate.size(); ++i ) + { + if ( neg_binates.size() >= 500 && pos_binates.size() >= 500 ) + { + break; + } + for ( j = i + 1; j < binate.size(); ++j ) + { + auto const& tt_s0 = get_tt_from_lit( binate[i], tts, begin ); + auto const& tt_s1 = get_tt_from_lit( binate[j], tts, begin ); + if ( pos_binates.size() < 500 ) + { + if ( kitty::implies( tt_s0 & tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i], binate[j] ) ); + } + if ( kitty::implies( ~tt_s0 & tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ) ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i], binate[j] ^ 0x1 ) ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, target ) ) + { + pos_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ^ 0x1 ) ); + } + } + if ( neg_binates.size() < 500 ) + { + if ( kitty::implies( target, tt_s0 | tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i], binate[j] ) ); + } + if ( kitty::implies( target, ~tt_s0 | tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ) ); + } + + if ( kitty::implies( target, tt_s0 | ~tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i], binate[j] ^ 0x1 ) ); + } + + if ( kitty::implies( target, ~tt_s0 | ~tt_s1 ) ) + { + neg_binates.emplace_back( std::make_pair( binate[i] ^ 0x1, binate[j] ^ 0x1 ) ); + } + } + } + } + for ( i = 0u; i < pos_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( pos_binates[i].first, tts, begin ) & get_tt_from_lit( pos_binates[i].second, tts, begin ); + for ( j = 0u; j < pos_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( pos_unate[j], tts, begin ) | tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( pos_binates[i].first, pos_binates[i].second ) ^ 0x1, pos_unate[j] ^ 0x1 ) ^ 0x1 ); // AND-OR + return il; + } + } + } + for ( i = 0u; i < neg_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( neg_binates[i].first, tts, begin ) | get_tt_from_lit( neg_binates[i].second, tts, begin ); + for ( j = 0u; j < neg_unate.size(); ++j ) + { + if ( target == ( get_tt_from_lit( neg_unate[j], tts, begin ) & tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( neg_binates[i].first ^ 0x1, neg_binates[i].second ^ 0x1 ) ^ 0x1, neg_unate[j] ) ); // OR-AND + return il; + } + } + } + + if ( max_size == 2 ) + { + return std::nullopt; + } + + /* 3-resub */ + for ( i = 0u; i < neg_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( neg_binates[i].first, tts, begin ) | get_tt_from_lit( neg_binates[i].second, tts, begin ); + for ( j = i + 1; j < neg_binates.size(); ++j ) + { + if ( target == ( ( get_tt_from_lit( neg_binates[j].first, tts, begin ) | get_tt_from_lit( neg_binates[j].second, tts, begin ) ) & tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( neg_binates[i].first ^ 0x1, neg_binates[i].second ^ 0x1 ) ^ 0x1, il.add_and( neg_binates[j].first ^ 0x1, neg_binates[j].second ^ 0x1 ) ^ 0x1 ) ); // AND-2OR + return il; + } + } + } + for ( i = 0u; i < pos_binates.size(); ++i ) + { + auto const& tt_binate = get_tt_from_lit( pos_binates[i].first, tts, begin ) & get_tt_from_lit( pos_binates[i].second, tts, begin ); + for ( j = i + 1; j < pos_binates.size(); ++j ) + { + if ( target == ( ( get_tt_from_lit( pos_binates[j].first, tts, begin ) & get_tt_from_lit( pos_binates[j].second, tts, begin ) ) | tt_binate ) ) + { + il.add_output( il.add_and( il.add_and( pos_binates[i].first, pos_binates[i].second ) ^ 0x1, il.add_and( pos_binates[j].first, pos_binates[j].second ) ^ 0x1 ) ^ 0x1 ); // OR-2AND + return il; + } + } + } + + for ( i = 0u; i < pos_unate.size(); ++i ) + { + for ( j = i + 1; j < pos_unate.size(); ++j ) + { + for ( k = j + 1; k < pos_unate.size(); ++k ) + { + for ( l = k + 1; l < pos_unate.size(); ++l ) + { + if ( target == ( get_tt_from_lit( pos_unate[i], tts, begin ) | get_tt_from_lit( pos_unate[j], tts, begin ) | get_tt_from_lit( pos_unate[k], tts, begin ) | get_tt_from_lit( pos_unate[l], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( pos_unate[i] ^ 0x1, pos_unate[j] ^ 0x1 ), il.add_and( pos_unate[k] ^ 0x1, pos_unate[l] ^ 0x1 ) ) ^ 0x1 ); // OR-2OR + return il; + } + } + } + } + } + + for ( i = 0u; i < neg_unate.size(); ++i ) + { + for ( j = i + 1; j < neg_unate.size(); ++j ) + { + for ( k = j + 1; k < neg_unate.size(); ++k ) + { + for ( l = k + 1; l < neg_unate.size(); ++l ) + { + if ( target == ( get_tt_from_lit( neg_unate[i], tts, begin ) & get_tt_from_lit( neg_unate[j], tts, begin ) & get_tt_from_lit( neg_unate[k], tts, begin ) & get_tt_from_lit( neg_unate[l], tts, begin ) ) ) + { + il.add_output( il.add_and( il.add_and( neg_unate[i], neg_unate[j] ), il.add_and( neg_unate[k], neg_unate[l] ) ) ); // AND-2AND + return il; + } + } + } + } + } + + if ( max_size == 3 ) + { + return std::nullopt; + } + + return std::nullopt; + } + +private: + uint32_t make_lit( uint32_t const& var, bool const& inv = false ) + { + return ( var + 1 ) * 2 + (uint32_t)inv; + } + + template + TT get_tt_from_lit( uint32_t const& lit, truth_table_storage_type const& tts, iterator_type const& begin ) + { + return ( lit % 2 ) ? ~tts[*( begin + ( lit / 2 ) - 1 )] : tts[*( begin + ( lit / 2 ) - 1 )]; + } + +private: + stats& st; +}; /* aig_enumerative_resyn */ + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp b/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp new file mode 100644 index 0000000..228f0f3 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/dump_resyn.hpp @@ -0,0 +1,120 @@ +/* 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 dump_resyn.hpp + \brief Dumps out resynthesis problems. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/null_utils.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +template +class resyn_dumper +{ +public: + using stats = null_stats; + using index_list_t = IndexList; + using truth_table_t = TT; + + explicit resyn_dumper( stats& st ) + { + (void)st; + } + + ~resyn_dumper() + { + std::cout << "avg. size = " << float( total_size ) / float( num_calls ) << "\n"; + } + + void reset_filename( std::string const& new_prefix ) + { + filename_prefix = new_prefix; + id_counter = 0; + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)max_level; + + std::string const filename = fmt::format( "{}{:0>{}}.resyn", filename_prefix, id_counter++, id_width ); + std::ofstream os( filename.c_str(), std::ofstream::out ); + + /* header */ + const uint32_t I = 0; + const uint32_t N = std::distance( begin, end ); + const uint32_t T = 1; + const uint32_t L = target.num_bits(); + os << fmt::format( "resyn {} {} {} {}\n", I, N, T, L ); + + /* simulation signatures */ + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + kitty::print_binary( tt, os ); + os << "\n"; + ++begin; + } + + /* target offset */ + kitty::print_binary( ~target & care, os ); + os << "\n"; + /* target onset */ + kitty::print_binary( target & care, os ); + os << "\n"; + + /* comment */ + os << fmt::format( "c\nmax size = {}\n", max_size ); + + total_size += max_size; + num_calls++; + os.close(); + return std::nullopt; + } + +private: + std::string filename_prefix = "resyn"; + uint32_t id_counter{0}; + const uint32_t id_width{3}; // width = 4 means max id = 9999 + uint32_t total_size{0}; + uint32_t num_calls{0}; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp b/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp new file mode 100644 index 0000000..0e48a11 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/mig_enumerative.hpp @@ -0,0 +1,284 @@ +/* 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_enumerative.hpp + \brief MIG enumerative resynthesis + + \author Hanyu Wang + \author Siang-Yun (Sonia) Lee + + Based on previous implementation of MIG resubstitution by + Eleonora Testa, Heinz Riener, and Mathias Soeken +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" +#include +#include +#include + +namespace mockturtle +{ + +template +struct mig_enumerative_resyn +{ +public: + using stats = null_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + +public: + explicit mig_enumerative_resyn( stats& st ) noexcept + : st( st ) + { + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + (void)care; + assert( is_const0( ~care ) && "enumerative resynthesis does not support don't cares" ); + + index_list_t il( std::distance( begin, end ) ); + const TT ntarget = ~target; + uint32_t i, j, k, l; + iterator_type it = begin, jt = begin; + + /* C-resub */ + if ( kitty::is_const0( target ) ) + { + il.add_output( 0 ); + return il; + } + if ( kitty::is_const0( ntarget ) ) // unreachable if normalized + { + il.add_output( 1 ); + return il; + } + + /* 0-resub */ + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + if ( target == tts[*it] ) + { + il.add_output( make_lit( i ) ); + return il; + } + if ( ntarget == tts[*it] ) // unreachable if normalized + { + il.add_output( make_lit( i, true ) ); + return il; + } + } + + /* R-resub: doesn't work with this problem definition (need fanins of root) */ + + if ( max_size == 0 ) + { + return std::nullopt; + } + + /* collect candidate pairs using MAJ filtering rule */ + std::vector> maj1pairs; + std::vector binate; + for ( it = begin, i = 0u; it != end; ++it, ++i ) + { + for ( jt = it + 1, j = i + 1; jt != end; ++jt, ++j ) + { + if ( kitty::ternary_majority( tts[*it], tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i ), make_lit( j ) ); + } + else if ( kitty::ternary_majority( ~tts[*it], tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i, true ), make_lit( j ) ); + } + else if ( kitty::ternary_majority( tts[*it], ~tts[*jt], target ) == target ) + { + maj1pairs.emplace_back( make_lit( i ), make_lit( j, true ) ); + } + else if ( kitty::ternary_majority( ~tts[*it], ~tts[*jt], target ) == target ) // unreachable if normalized + { + maj1pairs.emplace_back( make_lit( i, true ), make_lit( j, true ) ); + } + else if ( std::find( binate.begin(), binate.end(), make_lit( i ) ) == binate.end() ) + { + binate.emplace_back( make_lit( i ) ); + binate.emplace_back( make_lit( i, true ) ); /* 2x redundant memory*/ + } + } + if ( kitty::implies( tts[*it], target ) ) + { + maj1pairs.emplace_back( make_lit( i ), 1 ); + } + else if ( kitty::implies( ~tts[*it], target ) ) + { + maj1pairs.emplace_back( make_lit( i, true ), 1 ); + } + else if ( kitty::implies( target, tts[*it] ) ) + { + maj1pairs.emplace_back( make_lit( i ), 0 ); + } + else if ( kitty::implies( target, ~tts[*it] ) ) + { + maj1pairs.emplace_back( make_lit( i, true ), 0 ); + } + else if ( std::find( binate.begin(), binate.end(), make_lit( i ) ) == binate.end() ) + { + binate.emplace_back( make_lit( i ) ); + binate.emplace_back( make_lit( i, true ) ); /* 2x redundant memory*/ + } + } + + /* 1-resub */ + for ( i = 0u; i < maj1pairs.size(); ++i ) + { + TT const& x = get_tt_from_lit( maj1pairs[i].first, tts, begin ); + if ( maj1pairs[i].second < 2 ) + { + for ( j = i + 1; j < maj1pairs.size(); ++j ) + { + if ( maj1pairs[i].second == 0 && target == ( x & get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + else if ( maj1pairs[i].second == 1 && target == ( x | get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + + if ( maj1pairs[j].second < 2 ) + { + continue; + } + if ( maj1pairs[i].second == 0 && target == ( x & get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + else if ( maj1pairs[i].second == 1 && target == ( x | get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + } + else + { + TT const& y = get_tt_from_lit( maj1pairs[i].second, tts, begin ); + for ( j = i + 1; j < maj1pairs.size(); ++j ) + { + if ( target == kitty::ternary_majority( x, y, get_tt_from_lit( maj1pairs[j].first, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].first ) ); + return il; + } + if ( maj1pairs[j].second < 2 ) + { + if ( maj1pairs[j].second == 0 && target == ( x & y ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + else if ( maj1pairs[j].second == 1 && target == ( x | y ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + else if ( target == kitty::ternary_majority( x, y, get_tt_from_lit( maj1pairs[j].second, tts, begin ) ) ) + { + il.add_output( il.add_maj( maj1pairs[i].first, maj1pairs[i].second, maj1pairs[j].second ) ); + return il; + } + } + } + } + + if ( max_size == 1 ) + { + return std::nullopt; + } + + /* 2-resub */ + for ( i = 0u; i < binate.size(); ++i ) + { + auto const& x = get_tt_from_lit( binate[i], tts, begin ); + for ( j = i + 2u; j < binate.size(); ++j ) + { + auto const& y = get_tt_from_lit( binate[j], tts, begin ); + for ( k = j + 2u; k < binate.size(); ++k ) + { + auto const& z = get_tt_from_lit( binate[k], tts, begin ); + auto tt_binate = kitty::ternary_majority( x, y, z ); + if ( kitty::implies( tt_binate, target ) ) /* Boolean Over-Filtering */ + { + for ( l = 0u; l < maj1pairs.size(); ++l ) + { + auto const& a = get_tt_from_lit( maj1pairs[l].first, tts, begin ); + auto tt = maj1pairs[l].second >= 2 ? kitty::ternary_majority( a, get_tt_from_lit( maj1pairs[l].second, tts, begin ), tt_binate ) : maj1pairs[l].second ? a | tt_binate + : a & tt_binate; + if ( tt == target ) + { + il.add_output( il.add_maj( maj1pairs[l].first, maj1pairs[l].second, il.add_maj( binate[i], binate[j], binate[k] ) ) ); + return il; + } + } + } + } + } + } + + if ( max_size == 2 ) + { + return std::nullopt; + } + + return std::nullopt; + } + +private: + uint32_t make_lit( uint32_t const& var, bool const& inv = false ) + { + return ( var + 1 ) * 2 + (uint32_t)inv; + } + + template + TT get_tt_from_lit( uint32_t const& lit, truth_table_storage_type const& tts, iterator_type const& begin ) + { + return ( lit % 2 ) ? ~tts[*( begin + ( lit / 2 ) - 1 )] : tts[*( begin + ( lit / 2 ) - 1 )]; + } + +private: + stats& st; +}; /* mig_enumerative_resyn */ + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp b/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp new file mode 100644 index 0000000..6684e51 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/mig_resyn.hpp @@ -0,0 +1,1344 @@ +/* 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_resyn.hpp + \brief Implements resynthesis methods for MIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list.hpp" + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +struct mig_resyn_static_params +{ + using base_type = mig_resyn_static_params; + + /*! \brief Reserved capacity for divisor truth tables (number of divisors). */ + static constexpr uint32_t reserve{ 200u }; + + /*! \brief Whether to preserve depth. */ + static constexpr bool preserve_depth{ false }; + + /*! \brief Whether the divisors have uniform costs (size and depth, whenever relevant). */ + static constexpr bool uniform_div_cost{ true }; + + /*! \brief Size cost of each MAJ gate. */ + static constexpr uint32_t size_cost_of_maj{ 1u }; + + /*! \brief Depth cost of each MAJ gate (only relevant when `preserve_depth = true`). */ + static constexpr uint32_t depth_cost_of_maj{ 1u }; + + // Future work: Consider cost for inverter / MAJ with constant input; Add XOR support (XMG) +}; + +struct mig_resyn_stats +{ + void report() const {} +}; + +/*! \brief Logic resynthesis engine for MIGs with a bottom-up approach. + * + * This algorithm resynthesizes the target function with divisor functions + * by building a chain of majority gates from bottom to top. Divisors are + * chosen as side fanins based on some scoring functions aiming at covering + * more uncovered bits. + * + */ +template +class mig_resyn_bottomup +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + + explicit mig_resyn_bottomup( stats& st ) + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !static_params::preserve_depth && static_params::uniform_div_cost, "Advanced resynthesis is not implemented for this solver" ); + divisors.reserve( static_params::reserve ); + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)care; + (void)max_level; + divisors.clear(); + index_list.clear(); + + num_bits = target.num_bits(); + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + divisors.emplace_back( tt ^ divisors[0] ); // tt XNOR target = tt XOR ~target + divisors.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + index_list.add_inputs(); + ++begin; + } + + return compute_function( max_size ); + } + +private: + std::optional compute_function( uint32_t num_inserts ) + { + uint64_t max_score = 0u; + max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + uint32_t score = kitty::count_ones( divisors.at( i ) ); + if ( score > max_score ) + { + max_score = score; + max_i = i; + if ( max_score == num_bits ) + { + break; + } + } + } + /* 0-resub (including constants) */ + if ( max_score == num_bits ) + { + index_list.add_output( max_i ); + return index_list; + } + + if ( num_inserts == 0u ) + { + return std::nullopt; + } + size_limit = divisors.size() + num_inserts * 2; + + return bottom_up_approach(); + } + + std::optional bottom_up_approach() + { + // maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i}} ); + TT const& function_i = divisors.at( max_i ); + current_lit = divisors.size(); + return bottom_up_approach_rec( function_i ); + } + + std::optional bottom_up_approach_rec( TT const& function_i ) + { + /* THINK: Should we consider reusing newly-built nodes (nodes in maj_nodes) in addition to divisors? */ + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + uint64_t max_score = 0u; + max_j = 0u; + auto const not_covered_by_i = ~function_i; + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ); + uint32_t score = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( score > max_score && ( j >> 1 ) != ( max_i >> 1 ) ) + { + max_score = score; + max_j = j; + } + } + // maj_nodes.back().fanins.emplace_back( max_j ); + + /* the third fanin: only care about the disagreed bits */ + max_score = 0u; + max_k = 0u; + auto const disagree_in_ij = function_i ^ divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + uint32_t score = kitty::count_ones( divisors.at( k ) & disagree_in_ij ); + if ( score > max_score && ( k >> 1 ) != ( max_i >> 1 ) && ( k >> 1 ) != ( max_j >> 1 ) ) + { + max_score = score; + max_k = k; + } + } + // maj_nodes.back().fanins.emplace_back( max_k ); + index_list.add_maj( max_i, max_j, max_k ); + + auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); + if ( kitty::is_const0( ~current_function ) ) + { + index_list.add_output( current_lit ); + return index_list; + } + else if ( current_lit + 2 < size_limit ) + { + // maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, {maj_nodes.back().id}} ); + max_i = current_lit; + current_lit += 2; + return bottom_up_approach_rec( current_function ); + } + else + { + return std::nullopt; + } + } + +private: + uint32_t size_limit; + uint32_t num_bits; + uint32_t current_lit; /* literal of the current topmost node */ + + uint32_t max_i, max_j, max_k; + + std::vector divisors; + index_list_t index_list; + + stats& st; +}; /* mig_resyn_bottomup */ + +/*! \brief Logic resynthesis engine for MIGs with top-down decomposition. + * + * This algorithm resynthesizes the target function with divisor functions + * by first building the topmost node, and then iteratively refining its + * output function by expanding a leaf with a new node. The three fanins + * of the newly-created node are chosen from the divisors based on some + * scoring functions aiming at covering more *care* bits. + * + */ +template +class mig_resyn_topdown +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using truth_table_t = TT; + +private: + /*! \brief Internal data structure */ + struct expansion_position + { + int32_t parent_position = -1; // maj_nodes.at( ... ) + int32_t fanin_num = -1; // 0, 1, 2 + + bool operator==( expansion_position const& e ) const + { + return parent_position == e.parent_position && fanin_num == e.fanin_num; + } + }; + + struct maj_node + { + uint32_t id; /* maj_nodes.at( id - divisors.size() ) */ + std::vector fanins; /* ids of its three fanins */ + + std::vector fanin_functions = std::vector(); + TT care = TT(); + expansion_position parent = expansion_position(); + }; + + struct simple_maj + { + std::vector fanins; /* ids of divisors */ + TT function = TT(); /* resulting function */ + }; + +public: + explicit mig_resyn_topdown( stats& st ) + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !( static_params::uniform_div_cost && static_params::preserve_depth ), "If depth is to be preserved, divisor depth cost must be provided (usually not uniform)" ); + divisors.reserve( static_params::reserve ); + } + + /*! \brief Perform MIG resynthesis. + * + * `*pTTs[*begin]` must be of type `TT`. + * + * \param target Truth table of the target function. + * \param care Truth table of the care set. + * \param begin Begin iterator to divisor nodes. + * \param end End iterator to divisor nodes. + * \param tts A data structure (e.g. std::vector) that stores the truth tables of the divisor functions. + * \param max_size Maximum number of nodes allowed in the dependency circuit. + */ + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + divisors.clear(); + maj_nodes.clear(); + computed_table.clear(); + leaves.clear(); + + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + divisors.emplace_back( tt ^ divisors[0] ); // tt XNOR target = tt XOR ~target + divisors.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + ++begin; + } + scores.resize( divisors.size() ); + size_limit = max_size; + num_bits = kitty::count_ones( care ); + + return compute_function( care ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, Fn&& size_cost, uint32_t max_size = std::numeric_limits::max() ) + { + static_assert( !static_params::uniform_div_cost && !static_params::preserve_depth, "" ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, Fn&& size_cost, Fn&& depth_cost, uint32_t max_size = std::numeric_limits::max(), uint32_t max_depth = std::numeric_limits::max() ) + {} + +private: + std::optional compute_function( TT const& care ) + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors.at( i ) & care ) ) + { + /* 0-resub (including constants) */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_output( i ); + return index_list; + } + } + + if ( size_limit == 0u ) + { + return std::nullopt; + } + + return top_down_approach( care ); + } + + std::optional top_down_approach( TT const& top_care ) + { + maj_nodes.reserve( size_limit ); + std::vector top_node_choices = construct_top( top_care ); + + if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function & top_care ) ) + { + /* 1-resub */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_maj( top_node_choices[0].fanins[0], top_node_choices[0].fanins[1], top_node_choices[0].fanins[2] ); + index_list.add_output( divisors.size() ); + return index_list; + } + // for ( simple_maj& top_node : top_node_choices ) + //{ + // if ( easy_refine( top_node, top_care, 0 ) || easy_refine( top_node, top_care, 1 ) ) + // { + // /* 1-resub */ + // mig_index_list index_list( divisors.size() / 2 - 1 ); + // index_list.add_maj( top_node.fanins[0], top_node.fanins[1], top_node.fanins[2] ); + // index_list.add_output( divisors.size() ); + // return index_list; + // } + // } + if ( size_limit == 1u ) + { + return std::nullopt; + } + + std::vector maj_nodes_best; + for ( simple_maj const& top_node : top_node_choices ) + { + // for ( int32_t i = 0; i < 3; ++i ) + { + maj_nodes.clear(); + maj_nodes.emplace_back( maj_node{ uint32_t( divisors.size() ), top_node.fanins, { divisors.at( top_node.fanins[0] ), divisors.at( top_node.fanins[1] ), divisors.at( top_node.fanins[2] ) }, top_care } ); + + 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 )} ); + leaves.emplace_back( expansion_position{ 0, 0 } ); + leaves.emplace_back( expansion_position{ 0, 1 } ); + leaves.emplace_back( expansion_position{ 0, 2 } ); + + // TT const care = top_care & ~( divisors.at( top_node.fanins[sibling_index( i, 1 )] ) & divisors.at( top_node.fanins[sibling_index( i, 2 )] ) ); + // if ( evaluate_one( care, divisors.at( top_node.fanins[i] ), expansion_position{0, i} ) ) + //{ + // /* 2-resub */ + // maj_nodes_best = maj_nodes; + // return translate( maj_nodes_best ); + // } + + if ( !refine() ) + { + continue; + } + + if ( maj_nodes_best.size() == 0u || maj_nodes.size() < maj_nodes_best.size() ) + { + maj_nodes_best = maj_nodes; + } + } + } + + if ( maj_nodes_best.size() == 0u ) + { + return std::nullopt; + } + return translate( maj_nodes_best ); + } + + bool refine() + { + // while ( ( leaves.size() != 0u || improve_in_parent.size() != 0u || shuffle.size() != 0u ) && maj_nodes.size() < size_limit ) + while ( leaves.size() != 0u && maj_nodes.size() < size_limit ) + { + // if ( leaves.size() == 0u ) + //{ + // if ( improve_in_parent.size() != 0u ) + // { + // leaves = improve_in_parent; + // improve_in_parent.clear(); + // } + // else + // { + // leaves = shuffle; + // shuffle.clear(); + // } + // first_round = false; + // } + + uint32_t min_mismatch = num_bits + 1; + uint32_t pos = 0u; + for ( int32_t i = 0; (unsigned)i < leaves.size(); ++i ) + { + maj_node& parent_node = maj_nodes.at( leaves[i].parent_position ); + uint32_t const& fi = leaves[i].fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + + if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + if ( fulfilled( original_function, care ) /* already fulfilled */ + || care == parent_node.care /* probably cannot improve */ + ) + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + uint32_t const mismatch = count_ones( care & ~original_function ); + if ( mismatch < min_mismatch ) + { + pos = i; + min_mismatch = mismatch; + } + } + if ( leaves.size() == 0u ) + { + break; + } + expansion_position node_position = leaves.at( pos ); + leaves.erase( leaves.begin() + pos ); + + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + + if ( evaluate_one( care, original_function, node_position ) ) + { + return true; + } + } + return false; + } + + bool evaluate_one( TT const& care, TT const& original_function, expansion_position const& node_position ) + { + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + + simple_maj const new_node = expand_one( care ); + uint64_t const original_score = score( original_function, care ); + uint64_t const new_score = score( new_node.function, care ); + if ( new_score <= original_score ) + { + return false; + } + + // if ( new_score == original_score ) + //{ + // if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) + // { + // if ( first_round ) + // { + // /* We put it into a back-up queue for now */ + // improve_in_parent.emplace_back( node_position ); + // return false; + // } + // else + // { + // /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + // first_round = true; + // } + // } + // else if ( kitty::count_ones( new_node.function & parent_node.care ) == kitty::count_ones( original_function & parent_node.care ) && new_node.function != original_function ) + // { + // if ( first_round ) + // { + // /* We put it into a back-up queue for now */ + // shuffle.emplace_back( node_position ); + // return false; + // } + // else + // { + // /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + // first_round = true; + // } + // } + // else + // { + // return false; + // } + // } + + /* construct the new node */ + uint32_t const new_id = maj_nodes.size() + divisors.size(); + maj_nodes.emplace_back( maj_node{ new_id, new_node.fanins, { divisors.at( new_node.fanins[0] ), divisors.at( new_node.fanins[1] ), divisors.at( new_node.fanins[2] ) }, care, node_position } ); + update_fanin( parent_node, fi, new_id, new_node.function ); + + if ( kitty::is_const0( ~new_node.function & care ) ) + { + if ( node_fulfilled( maj_nodes.at( 0u ) ) ) + { + return true; + } + // TODO: add all the fulfilled nodes (trace upwards) to the divisor list, determining their indices in topological order + // This also has to be taken care of in the different runs. + } + else + { + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 0 } ); + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 1 } ); + leaves.emplace_back( expansion_position{ int32_t( maj_nodes.size() - 1 ), 2 } ); + } + return false; + } + + simple_maj expand_one( TT const& care ) + { + /* look up in computed_table */ + auto computed = computed_table.find( care ); + if ( computed != computed_table.end() ) + { + // std::cout<<"cache hit!\n"; + return computed->second; + } + + /* the first fanin: cover most care bits */ + uint64_t max_score = 0u; + uint32_t max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + max_i = i; + } + } + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + max_score = 0u; + uint32_t max_j = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ) & care; + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + max_j = j; + } + } + + /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ + max_score = 0u; + uint32_t max_k = 0u; + auto const not_covered_by_j = ~divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = divisors.at( k ) & care; + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + max_k = k; + } + } + + computed_table[care] = simple_maj( { { max_i, max_j, max_k }, kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ) } ); + return computed_table[care]; + } + + std::vector construct_top( TT const& care ) + { + 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 ) & care ); + 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( care, i, res ) ) + { + break; + } + } + } + return res; + } + + bool construct_top( TT const& care, 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 ) & care; + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + } + } + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( scores.at( j ) == max_score && !same_divisor( j, max_i ) ) + { + if ( construct_top( care, max_i, j, res ) ) + { + break; + } + } + } + return false; + } + + bool construct_top( TT const& care, 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 ) & care; + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + } + } + + for ( auto k = 0u; k < divisors.size(); ++k ) + { + if ( scores.at( k ) == max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + TT const func = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( k ) ); + if ( kitty::is_const0( ~func & care ) ) + { + res.clear(); + res.emplace_back( simple_maj( { { max_i, max_j, k }, func } ) ); + return true; + } + res.emplace_back( simple_maj( { { max_i, max_j, k }, func } ) ); + } + } + return false; + } + + /* try to replace the first (fi=0) or the second (fi=1) fanin with another divisor to improve coverage */ + // bool easy_refine( simple_maj& n, TT const& care, uint32_t fi ) + //{ + // uint64_t const original_coverage = kitty::count_ones( n.function & care ); + // 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 ) & care ); + // if ( coverage > current_coverage ) + // { + // current_coverage = coverage; + // n.fanins[fi] = i; + // } + // } + // if ( current_coverage > original_coverage ) + // { + // n.function = kitty::ternary_majority( divisors.at( n.fanins[0] ), divisors.at( n.fanins[1] ), divisors.at( n.fanins[2] ) ); + // if ( current_coverage == num_bits ) + // { + // return true; + // } + // } + // return false; + // } + + mig_index_list translate( std::vector const& maj_nodes_best ) const + { + mig_index_list index_list( divisors.size() / 2 - 1 ); + std::unordered_map id_map; + for ( auto i = 0u; i < maj_nodes_best.size(); ++i ) + { + auto& n = maj_nodes_best.at( maj_nodes_best.size() - i - 1u ); + uint32_t lits[3]; + for ( auto j = 0u; j < 3u; ++j ) + { + if ( n.fanins[j] < divisors.size() ) + { + lits[j] = n.fanins[j]; + } + else + { + auto mapped = id_map.find( n.fanins[j] ); + assert( mapped != id_map.end() ); + lits[j] = mapped->second; + } + } + id_map[n.id] = divisors.size() + i * 2; + index_list.add_maj( lits[0], lits[1], lits[2] ); + } + index_list.add_output( ( id_map.find( maj_nodes_best.at( 0u ).id ) )->second ); + return index_list; + } + +private: + bool same_divisor( uint32_t const i, uint32_t const j ) + { + return ( i >> 1 ) == ( j >> 1 ); + } + + bool fulfilled( TT const& func, TT const& care ) + { + return kitty::is_const0( ~func & care ); + } + + bool node_fulfilled( maj_node const& node ) + { + return fulfilled( kitty::ternary_majority( node.fanin_functions.at( 0u ), node.fanin_functions.at( 1u ), node.fanin_functions.at( 2u ) ), node.care ); + } + + uint64_t score( TT const& func, TT const& care ) + { + return kitty::count_ones( func & care ); + } + + void update_fanin( maj_node& parent_node, uint32_t const fi, uint32_t const new_id, TT const& new_function ) + { + parent_node.fanins.at( fi ) = new_id; + TT const old_function = parent_node.fanin_functions.at( fi ); + parent_node.fanin_functions.at( fi ) = new_function; + + TT const& sibling_func1 = sibling_func( parent_node, fi, 1 ); + TT const& sibling_func2 = sibling_func( parent_node, fi, 2 ); + + update_sibling( parent_node, fi, 1, old_function, new_function, sibling_func1, sibling_func2 ); + update_sibling( parent_node, fi, 2, old_function, new_function, sibling_func2, sibling_func1 ); + + /* update grandparents */ + if ( parent_node.parent.parent_position != -1 ) /* not the topmost node */ + { + update_fanin( grandparent( parent_node ), parent_node.parent.fanin_num, parent_node.id, kitty::ternary_majority( new_function, sibling_func1, sibling_func2 ) ); + } + } + + /* Deal with the affects on siblings due to a change in one fanin function + * \param parent_node The node one of whose fanin functions is changed. + * \param fi The index of the changed fanin. (0 <= fi <= 2) + * \param sibling_num Which sibling we are updating. (1 or 2) + * \param old_function The original function of the changed fanin. + * \param new_function The new function of the changed fanin. + * \param sibling_func The function of the sibling being updated. + * \param other_sibling_func The function of the other sibling. + */ + void update_sibling( maj_node const& parent_node, uint32_t const fi, uint32_t const sibling_num, TT const& old_function, TT const& new_function, TT const& sibling_func, TT const& other_sibling_func ) + { + uint32_t index = sibling_index( fi, sibling_num ); + uint32_t id = parent_node.fanins.at( index ); + TT const old_care = care( parent_node.care, old_function, other_sibling_func ); + TT const new_care = care( parent_node.care, new_function, other_sibling_func ); + + if ( old_care != new_care ) + { + /* update care of the sibling (if it is not a divisor) */ + if ( id >= divisors.size() ) + { + update_node_care( id_to_node( id ), sibling_func, old_care, new_care ); + } + else /* add the position back to queue because there may be new opportunities */ + { + add_position( expansion_position( { int32_t( id_to_pos( parent_node.id ) ), int32_t( index ) } ) ); + } + } + } + + void update_node_care( maj_node& node, TT const& func, TT const& old_care, TT const& new_care ) + { + assert( node.care == old_care ); + /* check if it was fulfilled but becomes unfulfilled */ + if ( fulfilled( func, old_care ) && !fulfilled( func, new_care ) ) + { + /* add the fanin positions back to queue */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) < divisors.size() ) + { + add_position( expansion_position( { int32_t( id_to_pos( node.id ) ), int32_t( fi ) } ) ); + } + } + } + node.care = new_care; + + /* the update may propagate to its children */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) >= divisors.size() ) + { + TT const old_child_care = care( old_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + TT const new_child_care = care( new_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + if ( old_child_care != new_child_care ) + { + update_node_care( id_to_node( node.fanins.at( fi ) ), node.fanin_functions.at( fi ), old_child_care, new_child_care ); + } + } + } + } + + void add_position( expansion_position const& pos ) + { + for ( auto& l : leaves ) + { + if ( l == pos ) + { + return; + } + } + leaves.emplace_back( pos ); + } + + TT care( TT const& parent_care, TT const& sibling_func1, TT const& sibling_func2 ) + { + return parent_care & ~( sibling_func1 & sibling_func2 ); + } + + inline maj_node& grandparent( maj_node const& parent_node ) + { + return maj_nodes.at( parent_node.parent.parent_position ); + } + + inline uint32_t sibling_index( uint32_t const my_index, uint32_t const sibling_num ) + { + return ( my_index + sibling_num ) % 3; + } + + inline TT const& sibling_func( maj_node const& parent_node, uint32_t const my_index, uint32_t const sibling_num ) + { + return parent_node.fanin_functions.at( sibling_index( my_index, sibling_num ) ); + } + + inline uint32_t id_to_pos( uint32_t const id ) + { + assert( id >= divisors.size() ); + return ( id - divisors.size() ); + } + + inline maj_node& id_to_node( uint32_t const id ) + { + return maj_nodes.at( id_to_pos( id ) ); + } + +private: + uint32_t size_limit; + uint32_t num_bits; + + std::vector divisors; + std::vector scores; + std::vector maj_nodes; /* the really used nodes */ + std::unordered_map> computed_table; /* map from care to a simple_maj with divisors as fanins */ + + std::vector leaves; //, improve_in_parent, shuffle; + // bool first_round = true; + + stats& st; +}; /* mig_resyn_topdown */ + +/*! \brief Logic resynthesis engine for MIGs by Akers' majority synthesis algorithm. + * + * This engine is a re-implementation of Akers' algorithm based on the following paper: + * + * Akers, S. B. (1962, October). Synthesis of combinational logic using + * three-input majority gates. In 3rd Annual Symposium on Switching Circuit Theory + * and Logical Design (SWCT 1962) (pp. 149-158). IEEE. + * + */ +template +class mig_resyn_akers +{ +public: + using stats = mig_resyn_stats; + using index_list_t = mig_index_list; + using TT = kitty::partial_truth_table; + using truth_table_t = TT; + + explicit mig_resyn_akers( stats& st ) + : id_to_lit( { 0, 1 } ), st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !static_params::preserve_depth && static_params::uniform_div_cost, "Advanced resynthesis is not implemented for this solver" ); + divisors.reserve( static_params::reserve ); + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max(), uint32_t max_level = std::numeric_limits::max() ) + { + (void)care; + (void)max_level; + + divisors.clear(); + id_to_lit.resize( 2 ); + index_list.clear(); + + divisors.emplace_back( ~target ); + divisors.emplace_back( target ); + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == divisors[0].num_bits() ); + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( tt ^ divisors[0] ); + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( tt ^ target ); + index_list.add_inputs(); + ++begin; + } + + return compute_function( max_size ); + } + +private: + std::optional compute_function( uint32_t num_inserts ) + { + (void)st; + if ( !is_feasible() ) + { + return std::nullopt; + } + + /* search for 0-resub (including constants) */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors[i] ) ) + { + index_list.add_output( id_to_lit[i] ); + return index_list; + } + } + + reduce(); + + while ( divisors.size() > 1 ) + { + if ( index_list.num_gates() >= num_inserts ) + { + return std::nullopt; + } + find_gate(); + add_gate(); + if ( kitty::is_const0( ~divisors.back() ) ) + { + break; + } + reduce(); + } + + index_list.add_output( id_to_lit.back() ); + return index_list; + } + + void reduce() + { + uint32_t num_bits_before = 0u; + uint32_t num_divs_before = 0u; + while ( num_bits_before != divisors[0].num_bits() || num_divs_before != divisors.size() ) + { + num_bits_before = divisors[0].num_bits(); + num_divs_before = divisors.size(); + eliminate_divs(); /* reduce column */ + eliminate_bits(); /* reduce row */ + } + } + + void eliminate_divs() + { + for ( int32_t x = 0; x < (int32_t)divisors.size(); ++x ) /* try to remove divisors[x] */ + { + if ( is_feasible( x ) ) + { + divisors.erase( divisors.begin() + x ); + id_to_lit.erase( id_to_lit.begin() + x ); + --x; + } + } + } + + void eliminate_bits() + { + /* for each pair of bits, check if we can remove i or j */ + for ( int32_t i = 0; i < (int32_t)divisors[0].num_bits() - 1; ++i ) + { + for ( int32_t j = i + 1; j < (int32_t)divisors[0].num_bits(); ++j ) + { + bool can_remove_i = true, can_remove_j = true; + for ( auto x = 0u; x < divisors.size(); ++x ) + { + if ( !kitty::get_bit( divisors[x], i ) && kitty::get_bit( divisors[x], j ) ) + { + can_remove_i = false; + } + if ( kitty::get_bit( divisors[x], i ) && !kitty::get_bit( divisors[x], j ) ) + { + can_remove_j = false; + } + if ( !can_remove_i && !can_remove_j ) + { + break; + } + } + + if ( can_remove_i ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( i ); + } + --i; + break; /* break loop j */ + } + else if ( can_remove_j ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( j ); + } + --j; + } + } + } + } + + void find_gate() + { + /* 1. Try if there are some gates that can eliminate some columns */ + uint32_t best_num_eliminates = 0u; + /* for each column, find candidate gates that eliminates it */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + find_gate_to_eliminate( i, best_num_eliminates ); + if ( best_num_eliminates == 3 ) + { + /* cannot be better */ + break; + } + } + if ( best_num_eliminates > 0 ) + { + return; + } + + /* 2. No gate can eliminate any column. Choose a gate that misses the least essentials */ + uint32_t least_missed_essentials = divisors[0].num_bits() + 1; + /* for all possible gates (input combinations) */ + assert( divisors.size() >= 3 ); + for ( auto i = 0u; i < divisors.size() - 2; ++i ) + { + for ( auto j = i + 1; j < divisors.size() - 1; ++j ) + { + for ( auto k = j + 1; k < divisors.size(); ++k ) + { + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[i], divisors[j], divisors[k] ); + uint32_t missed_essentials = 0u; + /* for each bit */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( kitty::get_bit( gate_function, b ) ) + continue; + if ( is_essential( i, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( j, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( k, b ) ) + { + ++missed_essentials; + } + } + + if ( missed_essentials < least_missed_essentials ) + { + fanins[0] = i; + fanins[1] = j; + fanins[2] = k; + least_missed_essentials = missed_essentials; + } + } + } + } + } + + void find_gate_to_eliminate( uint32_t column, uint32_t& best_num_eliminates ) + { + std::vector> candidates; + /* for each of its essential bits */ + for ( auto b = 0u; b < divisors[0].num_bits(); ++b ) + { + if ( !is_essential( column, b ) ) + continue; + candidates.emplace_back(); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( column != j && kitty::get_bit( divisors[j], b ) ) + { + candidates.back().emplace_back( j ); + } + } + if ( candidates.back().size() == 0u ) + { + /* impossible to eliminate this column */ + return; + } + } + + assert( candidates.size() >= 2 ); // why must be? but what if not? + /* try all combinations of size 2 */ + for ( auto const& j : candidates[0] ) + { + for ( auto const& k : candidates[1] ) + { + if ( j == k ) + continue; + /* check if either j or k appears in all other sets */ + bool all_satisfied = true; + for ( auto s = 2u; s < candidates.size(); ++s ) + { + bool is_in_set = false; + for ( auto const& ele : candidates[s] ) + { + if ( ele == j || ele == k ) + { + is_in_set = true; + break; + } + } + if ( !is_in_set ) + { + all_satisfied = false; + break; + } + } + if ( all_satisfied ) + { + /* this gate eliminates column */ + uint32_t num_eliminates = 1u; + /* see if it also eliminates j and/or k */ + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[column], divisors[j], divisors[k] ); + if ( eliminates( gate_function, j ) ) + { + ++num_eliminates; + } + if ( eliminates( gate_function, k ) ) + { + ++num_eliminates; + } + if ( num_eliminates > best_num_eliminates ) + { + fanins[0] = column; + fanins[1] = j; + fanins[2] = k; + best_num_eliminates = num_eliminates; + if ( num_eliminates == 3 ) + { + /* cannot be better */ + return; + } + } + } + } + } + } + + void add_gate() + { + index_list.add_maj( id_to_lit[fanins[0]], id_to_lit[fanins[1]], id_to_lit[fanins[2]] ); + id_to_lit.emplace_back( ( index_list.num_pis() + index_list.num_gates() ) * 2 ); + divisors.emplace_back( kitty::ternary_majority( divisors[fanins[0]], divisors[fanins[1]], divisors[fanins[2]] ) ); + } + +private: + /* whether the table is feasible (lpsd) if divisors[x] is deleted */ + bool is_feasible( int32_t x = -1 ) const + { + if ( divisors.size() == 1 && x == 0 ) + { + /* x is the only remaining column */ + return false; + } + + /* for every pair of rows _bits[i], _bits[j] */ + for ( auto i = 0u; i < divisors[0].num_bits() - 1; ++i ) + { + for ( auto j = i + 1; j < divisors[0].num_bits(); ++j ) + { + /* check if there is another divisors[y] having both bits 1 */ + bool found = false; + for ( int32_t y = 0; y < (int32_t)divisors.size(); ++y ) + { + if ( y == x ) + continue; + if ( kitty::get_bit( divisors[y], i ) && kitty::get_bit( divisors[y], j ) ) + { + found = true; + break; + } + } + if ( !found ) + { + return false; + } + } + } + return true; + } + + /* whether divisors[x]._bits[i] is essential */ + bool is_essential( uint32_t x, uint32_t i ) const + { + if ( !kitty::get_bit( divisors[x], i ) ) + { + return false; + } + + kitty::partial_truth_table tt( divisors[0].num_bits() ); + for ( auto y = 0u; y < divisors.size(); ++y ) + { + if ( x == y ) + continue; + if ( !kitty::get_bit( divisors[y], i ) ) + continue; + tt |= divisors[y]; + } + + return !kitty::is_const0( ~tt ); + } + + /* whether the gate eliminates a given column */ + bool eliminates( kitty::partial_truth_table const& gate_function, uint32_t column ) const + { + /* for each of its essential bits */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( !kitty::get_bit( gate_function, b ) && is_essential( column, b ) ) + { + return false; + } + } + return true; + } + +private: + void print_table() const + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + std::cout << "[" << std::setw( 2 ) << id_to_lit[i] << "] "; + kitty::print_binary( divisors[i] ); + std::cout << "\n"; + } + } + +private: + std::vector divisors; + std::vector id_to_lit; + index_list_t index_list; + uint32_t fanins[3]; + + stats& st; +}; /* mig_resyn_akers */ +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp b/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp new file mode 100644 index 0000000..1bc55dc --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/mux_resyn.hpp @@ -0,0 +1,274 @@ +/* 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: + * + +A B Y +0 0 1 +0 1 1 +1 0 1 +1 1 0 + +A B Y +0 0 0 +0 1 + + * 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 mux_resyn.hpp + \brief Implements resynthesis methods for MuxIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Logic resynthesis engine for MuxIGs with top-down decomposition. + * + */ +template +class mux_resyn +{ +public: + using stats = null_stats; + using index_list_t = muxig_index_list; + using truth_table_t = TT; + +public: + explicit mux_resyn( stats& st ) + : st( st ) + { + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + divisors.clear(); + normalized.clear(); + + normalized.emplace_back( ~target ); // 0 XNOR target = ~target + normalized.emplace_back( target ); // 1 XNOR target = target + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + normalized.emplace_back( tt ^ normalized[0] ); // tt XNOR target = tt XOR ~target + normalized.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + divisors.emplace_back( tt ); + ++begin; + } + + num_bits = kitty::count_ones( care ); + remaining_size = max_size; + max_depth = std::min( 10u, max_size ); + index_list.clear(); + index_list.add_inputs( divisors.size() ); + + auto res = compute_function( care, 0 ); + if ( res ) + { + index_list.add_output( *res ); + return index_list; + } + else + { + return std::nullopt; + } + } + +private: + std::optional compute_function( TT const& care, uint32_t depth ) + { + if ( depth > max_depth ) + return std::nullopt; + + /* + std::cout << "In computation function with depth: " << depth << " and care: "; + kitty::print_binary(care); + std::cout << "\n"; + */ + uint32_t chosen_t{0}, chosen_s{0}, chosen_e{0}, score1{0}, score2{0}, max_score{0}, min_score1{num_bits}, min_score2{num_bits}; + for ( auto t = 0u; t < normalized.size(); ++t ) + { + score1 = kitty::count_ones( normalized.at( t ) & care ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_t = t; + if ( score1 == num_bits ) + { + /* 0-resub */ + return t; + } + } + } + + if ( remaining_size == 0 ) + return std::nullopt; + + TT uncovered = ~normalized.at( chosen_t ) & care; + + /*std::cout << "Chose T: " << chosen_t << " with uncovered: "; + kitty::print_binary(uncovered); + std::cout << "\n"; + */ + for ( auto s = 0u; s < divisors.size(); ++s ) + { + TT& tt_s = divisors.at( s ); + TT tt_not_s = ~divisors.at( s ); + + // try positive s + score1 = kitty::count_ones( uncovered & tt_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_not_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2; + } + } + + // try negative s + score1 = kitty::count_ones( uncovered & tt_not_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2 + 1; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2 + 1; + } + } + } + + /*if (chosen_s % 2 == 1){ + printf("inverted select\n"); + }*/ + TT tt_chosen_s = chosen_s % 2 ? ~divisors.at( chosen_s / 2 ) : divisors.at( chosen_s / 2 ); + + + + if ( min_score2 != 0 ) + { + TT to_cover = care & ~tt_chosen_s; + + /*std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + kitty::print_binary(to_cover); + std::cout << "\n"; + */ + + max_score = 0; + for ( auto e = 0u; e < normalized.size(); ++e ) + { + score1 = kitty::count_ones( normalized.at( e ) & to_cover ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_e = e; + if ( max_score == min_score2 ) // best e-child + break; + } + } + } + + if ( min_score1 != 0 ) /* expand on t-child */ + { + TT t_care = care & tt_chosen_s; + + /* + std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + kitty::print_binary(t_care); + std::cout << "\n"; + */ + + auto res = compute_function( t_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_t = *res; + else + return std::nullopt; + } + + if ( max_score != min_score2 ) /* expand on e-child */ + { + TT e_care = care & ~tt_chosen_s; + + /*std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + kitty::print_binary(e_care); + std::cout << "\n"; + */ + auto res = compute_function( e_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_e = *res; + else + return std::nullopt; + } + + assert( remaining_size >= 1 ); + --remaining_size; + return index_list.add_mux( chosen_s + 2, chosen_t, chosen_e ); + } + +private: + uint32_t num_bits; + uint32_t remaining_size; + uint32_t max_depth; + + std::vector divisors; + std::vector normalized; + + muxig_index_list index_list; + + stats& st; +}; /* mux_resyn */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/resyn_engines/mux_resyn_select_opt.hpp b/include/mockturtle/algorithms/resyn_engines/mux_resyn_select_opt.hpp new file mode 100644 index 0000000..019e815 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/mux_resyn_select_opt.hpp @@ -0,0 +1,268 @@ +/* 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 mux_resyn.hpp + \brief Implements resynthesis methods for MuxIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Logic resynthesis engine for MuxIGs with top-down decomposition. + * + */ +template +class mux_resyn_select_opt +{ +public: + using stats = null_stats; + using index_list_t = muxig_index_list; + using truth_table_t = TT; + +public: + explicit mux_resyn_select_opt( stats& st ) + : st( st ) + { + } + + template + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + divisors.clear(); + normalized.clear(); + + normalized.emplace_back( ~target ); // 0 XNOR target = ~target + normalized.emplace_back( target ); // 1 XNOR target = target + + while ( begin != end ) + { + auto const& tt = tts[*begin]; + assert( tt.num_bits() == target.num_bits() ); + normalized.emplace_back( tt ^ normalized[0] ); // tt XNOR target = tt XOR ~target + normalized.emplace_back( tt ^ target ); // ~tt XNOR target = tt XOR target + divisors.emplace_back( tt ); + ++begin; + } + + num_bits = kitty::count_ones( care ); + remaining_size = max_size; + max_depth = std::min( 10u, max_size ); + index_list.clear(); + index_list.add_inputs( divisors.size() ); + + auto res = compute_function( care, 0 ); + if ( res ) + { + index_list.add_output( *res ); + return index_list; + } + else + { + return std::nullopt; + } + } + +private: + std::optional compute_function( TT const& care, uint32_t depth ) + { + if ( depth > max_depth ) + return std::nullopt; + + /* + std::cout << "In computation function with depth: " << depth << " and care: "; + kitty::print_binary(care); + std::cout << "\n"; + */ + uint32_t chosen_t{0}, chosen_s{0}, chosen_e{0}, score1{0}, score2{0}, max_score{0}, min_score1{num_bits}, min_score2{num_bits}; + for ( auto t = 0u; t < normalized.size(); ++t ) + { + score1 = kitty::count_ones( normalized.at( t ) & care ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_t = t; + if ( score1 == num_bits ) + { + /* 0-resub */ + return t; + } + } + } + + if ( remaining_size == 0 ) + return std::nullopt; + + TT uncovered = ~normalized.at( chosen_t ) & care; + + //std::cout << "Chose T: " << chosen_t << " with uncovered: "; + //kitty::print_binary(uncovered); + //std::cout << "\n"; + + for ( auto s = 0u; s < divisors.size(); ++s ) + { + TT& tt_s = divisors.at( s ); + TT tt_not_s = ~divisors.at( s ); + + // try positive s + score1 = kitty::count_ones( uncovered & tt_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_not_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2; + } + } + + // try negative s + score1 = kitty::count_ones( uncovered & tt_not_s ); + if ( score1 < min_score1 ) + { + min_score1 = score1; + chosen_s = s * 2 + 1; + min_score2 = num_bits; + } + if ( score1 == min_score1 ) + { + score2 = kitty::count_ones( tt_s & care ); + if ( score2 < min_score2 ) + { + min_score2 = score2; + chosen_s = s * 2 + 1; + } + } + } + + bool inverted_s = false; + if (chosen_s % 2 == 1){ + inverted_s = true; + printf("Inverted Select\n"); + } else { + printf("Not Inverted Select\n"); + } + TT tt_chosen_s = chosen_s % 2 ? ~divisors.at( chosen_s / 2 ) : divisors.at( chosen_s / 2 ); + + if ( min_score2 != 0 ) + { + TT to_cover = care & ~tt_chosen_s; + + //std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + //kitty::print_binary(to_cover); + //std::cout << "\n"; + + + max_score = 0; + for ( auto e = 0u; e < normalized.size(); ++e ) + { + score1 = kitty::count_ones( normalized.at( e ) & to_cover ); + if ( score1 > max_score ) + { + max_score = score1; + chosen_e = e; + if ( max_score == min_score2 ) // best e-child + break; + } + } + } + + if ( min_score1 != 0 ) /* expand on t-child */ + { + TT t_care = care & tt_chosen_s; + + /* + std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + kitty::print_binary(t_care); + std::cout << "\n"; + */ + + auto res = compute_function( t_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_t = *res; + else + return std::nullopt; + } + + if ( max_score != min_score2 ) /* expand on e-child */ + { + TT e_care = care & ~tt_chosen_s; + + /*std::cout << "Chose S: " << chosen_s /2 << " complemented: "<< chosen_s%2 <<" with uncovered: "; + kitty::print_binary(e_care); + std::cout << "\n"; + */ + auto res = compute_function( e_care, depth + 1 ); + if ( res && remaining_size > 0 ) + chosen_e = *res; + else + return std::nullopt; + } + + assert( remaining_size >= 1 ); + --remaining_size; + if (inverted_s){ + return index_list.add_mux( chosen_s + 1, chosen_e, chosen_t ); + } else { + return index_list.add_mux( chosen_s + 2, chosen_t, chosen_e ); + } + } + +private: + uint32_t num_bits; + uint32_t remaining_size; + uint32_t max_depth; + + std::vector divisors; + std::vector normalized; + + muxig_index_list index_list; + + stats& st; +}; /* mux_resyn */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/resyn_engines/mux_resyn_xnor_sel.hpp b/include/mockturtle/algorithms/resyn_engines/mux_resyn_xnor_sel.hpp new file mode 100644 index 0000000..9cc4719 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/mux_resyn_xnor_sel.hpp @@ -0,0 +1,312 @@ +/* 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: + * + +A B Y +0 0 1 +0 1 1 +1 0 1 +1 1 0 + +A B Y +0 0 0 +0 1 + + * 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 mux_resyn.hpp + \brief Implements resynthesis methods for MuxIGs. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/null_utils.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mockturtle { + +/*! \brief Logic resynthesis engine for MuxIGs with top-down decomposition. + * + */ +template class mux_resyn_xnor_sel { +public: + using stats = null_stats; + using index_list_t = muxig_index_list; + using truth_table_t = TT; + +public: + explicit mux_resyn_xnor_sel(stats &st) : st(st) {} + + template + std::optional + operator()(TT const &target, TT const &care, iterator_type begin, + iterator_type end, truth_table_storage_type const &tts, + uint32_t max_size = std::numeric_limits::max()) { + divisors.clear(); + normalized.clear(); + xor_selects.clear(); + + normalized.emplace_back(~target); // 0 XNOR target = ~target + normalized.emplace_back(target); // 1 XNOR target = target + + while (begin != end) { + auto const &tt = tts[*begin]; + assert(tt.num_bits() == target.num_bits()); + normalized.emplace_back(tt ^ + normalized[0]); // tt XNOR target = tt XOR ~target + normalized.emplace_back(tt ^ target); // ~tt XNOR target = tt XOR target + divisors.emplace_back(tt); + iterator_type iter = begin + 1; + while (iter != end) { + xor_selects.emplace_back((tt ^ tts[*iter])); + iter++; + } + ++begin; + } + assert(xor_selects.size() == (divisors.size() * (divisors.size() - 1)) / 2); + + num_bits = kitty::count_ones(care); + remaining_size = max_size; + // remaining_size = std::numeric_limits::max(); + max_depth = std::min(10u, max_size); + index_list.clear(); + index_list.add_inputs(divisors.size()); + + auto res = compute_function(care, 0); + if (res) { + index_list.add_output(*res); + return index_list; + } else { + return std::nullopt; + } + } + +private: + std::optional compute_function(TT const &care, + uint32_t depth) { + if (depth > max_depth) + return std::nullopt; + + /* + std::cout << "In computation function with depth: " << depth << " and care: + "; kitty::print_binary(care); std::cout << "\n"; + */ + uint32_t chosen_t{0}, chosen_s{0}, chosen_e{0}, score1{0}, score2{0}, + max_score{0}, min_score1{num_bits}, min_score2{num_bits}; + for (auto t = 0u; t < normalized.size(); ++t) { + score1 = kitty::count_ones(normalized.at(t) & care); + if (score1 > max_score) { + max_score = score1; + chosen_t = t; + if (score1 == num_bits) { + /* 0-resub */ + return t; + } + } + } + + if (remaining_size == 0) + return std::nullopt; + + TT uncovered = ~normalized.at(chosen_t) & care; + + /*std::cout << "Chose T: " << chosen_t << " with uncovered: "; + kitty::print_binary(uncovered); + std::cout << "\n"; + */ + for (auto s = 0u; s < divisors.size(); ++s) { + TT &tt_s = divisors.at(s); + TT tt_not_s = ~divisors.at(s); + + // try positive s + score1 = kitty::count_ones(uncovered & tt_s); + if (score1 < min_score1) { + min_score1 = score1; + chosen_s = s * 2; + min_score2 = num_bits; + } + if (score1 == min_score1) { + score2 = kitty::count_ones(tt_not_s & care); + if (score2 < min_score2) { + min_score2 = score2; + chosen_s = s * 2; + } + } + + // try negative s + score1 = kitty::count_ones(uncovered & tt_not_s); + if (score1 < min_score1) { + min_score1 = score1; + chosen_s = s * 2 + 1; + min_score2 = num_bits; + } + if (score1 == min_score1) { + score2 = kitty::count_ones(tt_s & care); + if (score2 < min_score2) { + min_score2 = score2; + chosen_s = s * 2 + 1; + } + } + } + + TT tt_chosen_s = + chosen_s % 2 ? ~divisors.at(chosen_s / 2) : divisors.at(chosen_s / 2); + + uint32_t zeros = 0; + uint32_t min_score_xor = num_bits; + uint32_t score_xor1 = num_bits; + uint32_t score_xnor1 = num_bits; + uint32_t score_xor2 = num_bits; + uint32_t min_score_xor2 = num_bits + 1; + uint32_t chosen_xor1{0}, chosen_xor2{0}; + std::vector> xor_s_candidates; + bool use_xor_s = false; + bool use_xnor_s = false; + for (auto s1_xor = 0u; s1_xor < divisors.size(); s1_xor++) { + TT &tt_s1 = divisors.at(s1_xor); + // TT tt_not_s1 = ~divisors.at(s1_xor); + for (auto s2_xor = s1_xor + 1; s2_xor < divisors.size(); s2_xor++) { + TT &tt_s2 = divisors.at(s2_xor); + // TT tt_not_s2 = ~divisors.at(s2_xor); + score_xor1 = kitty::count_ones(uncovered & (tt_s1 ^ tt_s2)); + // single variable didn't find solution but xor did + if (score_xor1 == 0 && min_score1 != 0) { + use_xnor_s = false; + use_xor_s = true; + xor_s_candidates.emplace_back(s1_xor, s2_xor); + score_xor2 = kitty::count_ones(care & ~(tt_s1 ^ tt_s2)); + if (score_xor2 < min_score_xor2) { + min_score_xor2 = score_xor2; + chosen_xor1 = s1_xor * 2; + chosen_xor2 = s2_xor * 2; + } + } + // try xnor + score_xnor1 = kitty::count_ones(uncovered & ~(tt_s1 ^ tt_s2)); + if (score_xnor1 == 0 && min_score1 != 0) { + use_xnor_s = true; + use_xor_s = false; + score_xor2 = kitty::count_ones(care & ~(tt_s1 ^ tt_s2)); + if (score_xor2 < min_score_xor2) { + min_score_xor2 = score_xor2; + chosen_xor1 = s1_xor * 2; + chosen_xor2 = s2_xor * 2; + } + } + } + } + TT tt_chosen_xor1 = chosen_xor1 % 2 ? ~divisors.at(chosen_xor1 / 2) + : divisors.at(chosen_xor1 / 2); + TT tt_chosen_xor2 = chosen_xor2 % 2 ? ~divisors.at(chosen_xor2 / 2) + : divisors.at(chosen_xor2 / 2); + assert(!(use_xor_s && use_xnor_s)); + if (use_xnor_s) { + tt_chosen_s = ~(tt_chosen_xor1 ^ tt_chosen_xor2); + printf("use xnor\n"); + } + if (use_xor_s) { + tt_chosen_s = tt_chosen_xor1 ^ tt_chosen_xor2; + printf("use xor\n"); + } + // printf("%d/%d, %d\n", zeros, not_zeros, xor_s_candidates.size()); + + if (min_score2 != 0) { + TT to_cover = care & ~tt_chosen_s; + max_score = 0; + for (auto e = 0u; e < normalized.size(); ++e) { + score1 = kitty::count_ones(normalized.at(e) & to_cover); + if (score1 > max_score) { + max_score = score1; + chosen_e = e; + if (max_score == min_score2) // best e-child + break; + } + } + } + + if (min_score1 != 0) /* expand on t-child */ + { + TT t_care = care & tt_chosen_s; + auto res = compute_function(t_care, depth + 1); + if (res && remaining_size > 0) + chosen_t = *res; + else + return std::nullopt; + } + + if (max_score != min_score2) /* expand on e-child */ + { + TT e_care = care & ~tt_chosen_s; + auto res = compute_function(e_care, depth + 1); + if (res && remaining_size > 0) + chosen_e = *res; + else + return std::nullopt; + } + + assert(remaining_size >= 1); + --remaining_size; + if (use_xnor_s) { + return index_list.add_mux( + index_list.add_xor(chosen_xor1 + 2, chosen_xor2 + 2), chosen_e, + chosen_t); + } else if (use_xor_s) { + return index_list.add_mux( + index_list.add_xor(chosen_xor1 + 2, chosen_xor2 + 2), chosen_t, + chosen_e); + } else { + return index_list.add_mux(chosen_s + 2, chosen_t, chosen_e); + } + } + +private: + uint32_t num_bits; + uint32_t remaining_size; + uint32_t max_depth; + + std::vector divisors; + std::vector normalized; + // std::vector>> xnor_selects; + std::vector xor_selects; + + muxig_index_list index_list; + + stats &st; +}; /* mux_resyn */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp b/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp new file mode 100644 index 0000000..ae01942 --- /dev/null +++ b/include/mockturtle/algorithms/resyn_engines/xag_resyn.hpp @@ -0,0 +1,890 @@ +/* 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 xag_resyn.hpp + \brief Resynthesis by recursive decomposition for AIGs or XAGs. + (based on ABC's implementation in `giaResub.c` by Alan Mishchenko) + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "../../utils/index_list.hpp" +#include "../../utils/node_map.hpp" +#include "../../utils/stopwatch.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace mockturtle +{ + +struct xag_resyn_static_params +{ + using base_type = xag_resyn_static_params; + + /*! \brief Maximum number of binate divisors to be considered. */ + static constexpr uint32_t max_binates{ 50u }; + + /*! \brief Reserved capacity for divisor truth tables (number of divisors). */ + static constexpr uint32_t reserve{ 200u }; + + /*! \brief Whether to consider single XOR gates (i.e., using XAGs instead of AIGs). */ + static constexpr bool use_xor{ true }; + + /*! \brief Whether to copy truth tables. */ + static constexpr bool copy_tts{ false }; + + /*! \brief Whether to preserve depth. */ + static constexpr bool preserve_depth{ false }; + + /*! \brief Whether the divisors have uniform costs (size and depth, whenever relevant). */ + static constexpr bool uniform_div_cost{ true }; + + /*! \brief Size cost of each AND gate. */ + static constexpr uint32_t size_cost_of_and{ 1u }; + + /*! \brief Size cost of each XOR gate (only relevant when `use_xor = true`). */ + static constexpr uint32_t size_cost_of_xor{ 1u }; + + /*! \brief Depth cost of each AND gate (only relevant when `preserve_depth = true`). */ + static constexpr uint32_t depth_cost_of_and{ 1u }; + + /*! \brief Depth cost of each XOR gate (only relevant when `preserve_depth = true` and `use_xor = true`). */ + static constexpr uint32_t depth_cost_of_xor{ 1u }; + + using truth_table_storage_type = void; + using node_type = void; +}; + +template +struct xag_resyn_static_params_default : public xag_resyn_static_params +{ + using truth_table_storage_type = std::vector; + using node_type = uint32_t; +}; + +template +struct aig_resyn_static_params_default : public xag_resyn_static_params_default +{ + static constexpr bool use_xor = false; +}; + +template +struct xag_resyn_static_params_for_win_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = unordered_node_map; + using node_type = typename Ntk::node; +}; + +template +struct xag_resyn_static_params_for_sim_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = incomplete_node_map; + using node_type = typename Ntk::node; +}; + +template +struct aig_resyn_static_params_for_win_resub : public xag_resyn_static_params +{ + using truth_table_storage_type = unordered_node_map; + using node_type = typename Ntk::node; + static constexpr bool use_xor = false; +}; + +template +struct aig_resyn_static_params_for_sim_resub : public xag_resyn_static_params_for_sim_resub +{ + static constexpr bool use_xor = false; +}; + +struct xag_resyn_stats +{ + /*! \brief Time for finding 0-resub and collecting unate literals. */ + stopwatch<>::duration time_unate{ 0 }; + + /*! \brief Time for finding 1-resub. */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Time for finding 2-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Time for finding 3-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Time for sorting unate literals and unate pairs. */ + stopwatch<>::duration time_sort{ 0 }; + + /*! \brief Time for collecting unate pairs. */ + stopwatch<>::duration time_collect_pairs{ 0 }; + + /*! \brief Time for dividing the target and recursive call. */ + stopwatch<>::duration time_divide{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] 0-resub : {:>5.2f} secs\n", to_seconds( time_unate ) ); + fmt::print( "[i] 1-resub : {:>5.2f} secs\n", to_seconds( time_resub1 ) ); + fmt::print( "[i] 2-resub : {:>5.2f} secs\n", to_seconds( time_resub2 ) ); + fmt::print( "[i] 3-resub : {:>5.2f} secs\n", to_seconds( time_resub3 ) ); + fmt::print( "[i] sort : {:>5.2f} secs\n", to_seconds( time_sort ) ); + fmt::print( "[i] collect pairs: {:>5.2f} secs\n", to_seconds( time_collect_pairs ) ); + fmt::print( "[i] dividing : {:>5.2f} secs\n", to_seconds( time_divide ) ); + } +}; + +/*! \brief Logic resynthesis engine for AIGs or XAGs. + * + * The algorithm is based on ABC's implementation in `giaResub.c` by Alan Mishchenko. + * + * Divisors are classified as positive unate (not overlapping with target offset), + * negative unate (not overlapping with target onset), or binate (overlapping with + * both onset and offset). Furthermore, pairs of binate divisors are combined with + * an AND operation and considering all possible input polarities and again classified + * as positive unate, negative unate or binate. Simple solutions of zero cost + * (one unate divisor), one node (two unate divisors), two nodes (one unate divisor + + * one unate pair), and three nodes (two unate pairs) are exhaustively examined. + * When no simple solutions can be found, the algorithm heuristically chooses an unate + * divisor or an unate pair to divide the target function with and recursively calls + * itself to decompose the remainder function. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + using TT = kitty::static_truth_table<6>; + const std::vector divisors = ...; + const node_map tts = ...; + const TT target = ..., care = ...; + xag_resyn_stats st; + xag_resyn_decompose, false, false, aig_network::node> resyn( st ); + auto result = resyn( target, care, divisors.begin(), divisors.end(), tts ); + \endverbatim + */ +template> +class xag_resyn_decompose +{ +public: + using stats = xag_resyn_stats; + using index_list_t = large_xag_index_list; + using truth_table_t = TT; + +private: + struct unate_lit + { + unate_lit( uint32_t l ) + : lit( l ) + {} + + bool operator==( unate_lit const& other ) const + { + return lit == other.lit; + } + + uint32_t lit; + uint32_t score{ 0 }; + }; + + struct fanin_pair + { + fanin_pair( uint32_t l1, uint32_t l2 ) + : lit1( l1 < l2 ? l1 : l2 ), lit2( l1 < l2 ? l2 : l1 ) + {} + + fanin_pair( uint32_t l1, uint32_t l2, bool is_xor ) + : lit1( l1 > l2 ? l1 : l2 ), lit2( l1 > l2 ? l2 : l1 ) + { + (void)is_xor; + } + + bool operator==( fanin_pair const& other ) const + { + return lit1 == other.lit1 && lit2 == other.lit2; + } + + uint32_t lit1, lit2; + uint32_t score{ 0 }; + }; + +public: + explicit xag_resyn_decompose( stats& st ) noexcept + : st( st ) + { + static_assert( std::is_same_v, "Invalid static_params type" ); + static_assert( !( static_params::uniform_div_cost && static_params::preserve_depth ), "If depth is to be preserved, divisor depth cost must be provided (usually not uniform)" ); + divisors.reserve( static_params::reserve ); + } + + /*! \brief Perform XAG resynthesis. + * + * `tts[*begin]` must be of type `TT`. + * Moreover, if `static_params::copy_tts = false`, `*begin` must be of type `static_params::node_type`. + * + * \param target Truth table of the target function. + * \param care Truth table of the care set. + * \param begin Begin iterator to divisor nodes. + * \param end End iterator to divisor nodes. + * \param tts A data structure (e.g. std::vector) that stores the truth tables of the divisor functions. + * \param max_size Maximum number of nodes allowed in the dependency circuit. + */ + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, uint32_t max_size = std::numeric_limits::max() ) + { + static_assert( static_params::copy_tts || std::is_same_v::value_type, typename static_params::node_type>, "iterator_type does not dereference to static_params::node_type" ); + + ptts = &tts; + on_off_sets[0] = ~target & care; + on_off_sets[1] = target & care; + + divisors.resize( 1 ); /* clear previous data and reserve 1 dummy node for constant */ + while ( begin != end ) + { + if constexpr ( static_params::copy_tts ) + { + divisors.emplace_back( ( *ptts )[*begin] ); + } + else + { + divisors.emplace_back( *begin ); + } + ++begin; + } + + return compute_function( max_size ); + } + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, Fn&& size_cost, uint32_t max_size = std::numeric_limits::max() ) + {} + + template> + std::optional operator()( TT const& target, TT const& care, iterator_type begin, iterator_type end, typename static_params::truth_table_storage_type const& tts, Fn&& size_cost, Fn&& depth_cost, uint32_t max_size = std::numeric_limits::max(), uint32_t max_depth = std::numeric_limits::max() ) + {} + +private: + std::optional compute_function( uint32_t num_inserts ) + { + index_list.clear(); + index_list.add_inputs( divisors.size() - 1 ); + auto const lit = compute_function_rec( num_inserts ); + if ( lit ) + { + assert( index_list.num_gates() <= num_inserts ); + index_list.add_output( *lit ); + return index_list; + } + return std::nullopt; + } + + std::optional compute_function_rec( uint32_t num_inserts ) + { + pos_unate_lits.clear(); + neg_unate_lits.clear(); + binate_divs.clear(); + pos_unate_pairs.clear(); + neg_unate_pairs.clear(); + + /* try 0-resub and collect unate literals */ + auto const res0 = call_with_stopwatch( st.time_unate, [&]() { + return find_one_unate(); + } ); + if ( res0 ) + { + return *res0; + } + if ( num_inserts == 0u ) + { + return std::nullopt; + } + + /* sort unate literals and try 1-resub */ + call_with_stopwatch( st.time_sort, [&]() { + sort_unate_lits( pos_unate_lits, 1 ); + sort_unate_lits( neg_unate_lits, 0 ); + } ); + auto const res1or = call_with_stopwatch( st.time_resub1, [&]() { + return find_div_div( pos_unate_lits, 1 ); + } ); + if ( res1or ) + { + return *res1or; + } + auto const res1and = call_with_stopwatch( st.time_resub1, [&]() { + return find_div_div( neg_unate_lits, 0 ); + } ); + if ( res1and ) + { + return *res1and; + } + + if ( binate_divs.size() > static_params::max_binates ) + { + binate_divs.resize( static_params::max_binates ); + } + + if constexpr ( static_params::use_xor ) + { + /* collect XOR-type unate pairs and try 1-resub with XOR */ + auto const res1xor = find_xor(); + if ( res1xor ) + { + return *res1xor; + } + } + if ( num_inserts == 1u ) + { + return std::nullopt; + } + + /* collect AND-type unate pairs and sort (both types), then try 2- and 3-resub */ + call_with_stopwatch( st.time_collect_pairs, [&]() { + collect_unate_pairs(); + } ); + call_with_stopwatch( st.time_sort, [&]() { + sort_unate_pairs( pos_unate_pairs, 1 ); + sort_unate_pairs( neg_unate_pairs, 0 ); + } ); + auto const res2or = call_with_stopwatch( st.time_resub2, [&]() { + return find_div_pair( pos_unate_lits, pos_unate_pairs, 1 ); + } ); + if ( res2or ) + { + return *res2or; + } + auto const res2and = call_with_stopwatch( st.time_resub2, [&]() { + return find_div_pair( neg_unate_lits, neg_unate_pairs, 0 ); + } ); + if ( res2and ) + { + return *res2and; + } + + if ( num_inserts >= 3u ) + { + auto const res3or = call_with_stopwatch( st.time_resub3, [&]() { + return find_pair_pair( pos_unate_pairs, 1 ); + } ); + if ( res3or ) + { + return *res3or; + } + auto const res3and = call_with_stopwatch( st.time_resub3, [&]() { + return find_pair_pair( neg_unate_pairs, 0 ); + } ); + if ( res3and ) + { + return *res3and; + } + } + + /* choose something to divide and recursive call on the remainder */ + /* Note: dividing = AND the on-set (if using positive unate) or the off-set (if using negative unate) + with the *negation* of the divisor/pair (subtracting) */ + uint32_t on_off_div, on_off_pair; + uint32_t score_div = 0, score_pair = 0; + + call_with_stopwatch( st.time_divide, [&]() { + if ( pos_unate_lits.size() > 0 ) + { + on_off_div = 1; /* use pos_lit */ + score_div = pos_unate_lits[0].score; + if ( neg_unate_lits.size() > 0 && neg_unate_lits[0].score > pos_unate_lits[0].score ) + { + on_off_div = 0; /* use neg_lit */ + score_div = neg_unate_lits[0].score; + } + } + else if ( neg_unate_lits.size() > 0 ) + { + on_off_div = 0; /* use neg_lit */ + score_div = neg_unate_lits[0].score; + } + + if ( num_inserts > 3u ) + { + if ( pos_unate_pairs.size() > 0 ) + { + on_off_pair = 1; /* use pos_pair */ + score_pair = pos_unate_pairs[0].score; + if ( neg_unate_pairs.size() > 0 && neg_unate_pairs[0].score > pos_unate_pairs[0].score ) + { + on_off_pair = 0; /* use neg_pair */ + score_pair = neg_unate_pairs[0].score; + } + } + else if ( neg_unate_pairs.size() > 0 ) + { + on_off_pair = 0; /* use neg_pair */ + score_pair = neg_unate_pairs[0].score; + } + } + } ); + + if ( score_div > score_pair / 2 ) /* divide with a divisor */ + { + /* if using pos_lit (on_off_div = 1), modify on-set and use an OR gate on top; + if using neg_lit (on_off_div = 0), modify off-set and use an AND gate on top + */ + uint32_t const lit = on_off_div ? pos_unate_lits[0].lit : neg_unate_lits[0].lit; + call_with_stopwatch( st.time_divide, [&]() { + on_off_sets[on_off_div] &= lit & 0x1 ? get_div( lit >> 1 ) : ~get_div( lit >> 1 ); + } ); + + auto const res_remain_div = compute_function_rec( num_inserts - 1 ); + if ( res_remain_div ) + { + auto const new_lit = index_list.add_and( ( lit ^ 0x1 ), *res_remain_div ^ on_off_div ); + return new_lit + on_off_div; + } + } + else if ( score_pair > 0 ) /* divide with a pair */ + { + fanin_pair const pair = on_off_pair ? pos_unate_pairs[0] : neg_unate_pairs[0]; + call_with_stopwatch( st.time_divide, [&]() { + if constexpr ( static_params::use_xor ) + { + if ( pair.lit1 > pair.lit2 ) /* XOR pair: ~(lit1 ^ lit2) = ~lit1 ^ lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) ^ ( pair.lit2 & 0x1 ? ~get_div( pair.lit2 >> 1 ) : get_div( pair.lit2 >> 1 ) ); + } + else /* AND pair: ~(lit1 & lit2) = ~lit1 | ~lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) | ( pair.lit2 & 0x1 ? get_div( pair.lit2 >> 1 ) : ~get_div( pair.lit2 >> 1 ) ); + } + } + else /* AND pair: ~(lit1 & lit2) = ~lit1 | ~lit2 */ + { + on_off_sets[on_off_pair] &= ( pair.lit1 & 0x1 ? get_div( pair.lit1 >> 1 ) : ~get_div( pair.lit1 >> 1 ) ) | ( pair.lit2 & 0x1 ? get_div( pair.lit2 >> 1 ) : ~get_div( pair.lit2 >> 1 ) ); + } + } ); + + auto const res_remain_pair = compute_function_rec( num_inserts - 2 ); + if ( res_remain_pair ) + { + uint32_t new_lit1; + if constexpr ( static_params::use_xor ) + { + new_lit1 = ( pair.lit1 > pair.lit2 ) ? index_list.add_xor( pair.lit1, pair.lit2 ) : index_list.add_and( pair.lit1, pair.lit2 ); + } + else + { + new_lit1 = index_list.add_and( pair.lit1, pair.lit2 ); + } + auto const new_lit2 = index_list.add_and( new_lit1 ^ 0x1, *res_remain_pair ^ on_off_pair ); + return new_lit2 + on_off_pair; + } + } + + return std::nullopt; + } + + /* See if there is a constant or divisor covering all on-set bits or all off-set bits. + 1. Check constant-resub + 2. Collect unate literals + 3. Find 0-resub (both positive unate and negative unate) and collect binate (neither pos nor neg unate) divisors + */ + std::optional find_one_unate() + { + num_bits[0] = kitty::count_ones( on_off_sets[0] ); /* off-set */ + num_bits[1] = kitty::count_ones( on_off_sets[1] ); /* on-set */ + if ( num_bits[0] == 0 ) + { + return 1; + } + if ( num_bits[1] == 0 ) + { + return 0; + } + + for ( auto v = 1u; v < divisors.size(); ++v ) + { + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 ); + unateness[0] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[0] ) ) + { + pos_unate_lits.emplace_back( v << 1 | 0x1 ); + unateness[1] = true; + } + + /* check intersection with on-set */ + if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 ); + unateness[2] = true; + } + else if ( kitty::intersection_is_empty( get_div( v ), on_off_sets[1] ) ) + { + neg_unate_lits.emplace_back( v << 1 | 0x1 ); + unateness[3] = true; + } + + /* 0-resub */ + if ( unateness[0] && unateness[3] ) + { + return ( v << 1 ); + } + if ( unateness[1] && unateness[2] ) + { + return ( v << 1 ) + 1; + } + /* useless unate literal */ + if ( ( unateness[0] && unateness[2] ) || ( unateness[1] && unateness[3] ) ) + { + pos_unate_lits.pop_back(); + neg_unate_lits.pop_back(); + } + /* binate divisor */ + else if ( !unateness[0] && !unateness[1] && !unateness[2] && !unateness[3] ) + { + binate_divs.emplace_back( v ); + } + } + return std::nullopt; + } + + /* Sort the unate literals by the number of minterms in the intersection. + - For `pos_unate_lits`, `on_off` = 1, sort by intersection with on-set; + - For `neg_unate_lits`, `on_off` = 0, sort by intersection with off-set + */ + void sort_unate_lits( std::vector& unate_lits, uint32_t on_off ) + { + for ( auto& l : unate_lits ) + { + l.score = kitty::count_ones( ( l.lit & 0x1 ? ~get_div( l.lit >> 1 ) : get_div( l.lit >> 1 ) ) & on_off_sets[on_off] ); + } + std::stable_sort( unate_lits.begin(), unate_lits.end(), [&]( unate_lit const& l1, unate_lit const& l2 ) { + return l1.score > l2.score; // descending order + } ); + } + + void sort_unate_pairs( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto& p : unate_pairs ) + { + if constexpr ( static_params::use_xor ) + { + p.score = ( p.lit1 > p.lit2 ) ? kitty::count_ones( ( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) ^ ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) ) & on_off_sets[on_off] ) + : kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + else + { + p.score = kitty::count_ones( ( p.lit1 & 0x1 ? ~get_div( p.lit1 >> 1 ) : get_div( p.lit1 >> 1 ) ) & ( p.lit2 & 0x1 ? ~get_div( p.lit2 >> 1 ) : get_div( p.lit2 >> 1 ) ) & on_off_sets[on_off] ); + } + } + std::stable_sort( unate_pairs.begin(), unate_pairs.end(), [&]( fanin_pair const& p1, fanin_pair const& p2 ) { + return p1.score > p2.score; // descending order + } ); + } + + /* See if there are two unate divisors covering all on-set bits or all off-set bits. + - For `pos_unate_lits`, `on_off` = 1, try covering all on-set bits by combining two with an OR gate; + - For `neg_unate_lits`, `on_off` = 0, try covering all off-set bits by combining two with an AND gate + */ + std::optional find_div_div( std::vector& unate_lits, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_lits.size(); ++i ) + { + uint32_t const& lit1 = unate_lits[i].lit; + if ( unate_lits[i].score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_lits.size(); ++j ) + { + uint32_t const& lit2 = unate_lits[j].lit; + if ( unate_lits[i].score + unate_lits[j].score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + auto const ntt2 = lit2 & 0x1 ? get_div( lit2 >> 1 ) : ~get_div( lit2 >> 1 ); + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + auto const new_lit = index_list.add_and( ( lit1 ^ 0x1 ), ( lit2 ^ 0x1 ) ); + return new_lit + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_div_pair( std::vector& unate_lits, std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_lits.size(); ++i ) + { + uint32_t const& lit1 = unate_lits[i].lit; + for ( auto j = 0u; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( unate_lits[i].score + pair2.score < num_bits[on_off] ) + { + break; + } + auto const ntt1 = lit1 & 0x1 ? get_div( lit1 >> 1 ) : ~get_div( lit1 >> 1 ); + TT ntt2; + if constexpr ( static_params::use_xor ) + { + if ( pair2.lit1 > pair2.lit2 ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + uint32_t new_lit1; + if constexpr ( static_params::use_xor ) + { + if ( pair2.lit1 > pair2.lit2 ) + { + new_lit1 = index_list.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + new_lit1 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + } + else + { + new_lit1 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + auto const new_lit2 = index_list.add_and( ( lit1 ^ 0x1 ), new_lit1 ^ 0x1 ); + return new_lit2 + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_pair_pair( std::vector& unate_pairs, uint32_t on_off ) + { + for ( auto i = 0u; i < unate_pairs.size(); ++i ) + { + fanin_pair const& pair1 = unate_pairs[i]; + if ( pair1.score * 2 < num_bits[on_off] ) + { + break; + } + for ( auto j = i + 1; j < unate_pairs.size(); ++j ) + { + fanin_pair const& pair2 = unate_pairs[j]; + if ( pair1.score + pair2.score < num_bits[on_off] ) + { + break; + } + TT ntt1, ntt2; + if constexpr ( static_params::use_xor ) + { + if ( pair1.lit1 > pair1.lit2 ) + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) ^ ( pair1.lit2 & 0x1 ? ~get_div( pair1.lit2 >> 1 ) : get_div( pair1.lit2 >> 1 ) ); + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + } + if ( pair2.lit1 > pair2.lit2 ) + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) ^ ( pair2.lit2 & 0x1 ? ~get_div( pair2.lit2 >> 1 ) : get_div( pair2.lit2 >> 1 ) ); + } + else + { + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + } + else + { + ntt1 = ( pair1.lit1 & 0x1 ? get_div( pair1.lit1 >> 1 ) : ~get_div( pair1.lit1 >> 1 ) ) | ( pair1.lit2 & 0x1 ? get_div( pair1.lit2 >> 1 ) : ~get_div( pair1.lit2 >> 1 ) ); + ntt2 = ( pair2.lit1 & 0x1 ? get_div( pair2.lit1 >> 1 ) : ~get_div( pair2.lit1 >> 1 ) ) | ( pair2.lit2 & 0x1 ? get_div( pair2.lit2 >> 1 ) : ~get_div( pair2.lit2 >> 1 ) ); + } + + if ( kitty::intersection_is_empty( ntt1, ntt2, on_off_sets[on_off] ) ) + { + uint32_t fanin_lit1, fanin_lit2; + if constexpr ( static_params::use_xor ) + { + if ( pair1.lit1 > pair1.lit2 ) + { + fanin_lit1 = index_list.add_xor( pair1.lit1, pair1.lit2 ); + } + else + { + fanin_lit1 = index_list.add_and( pair1.lit1, pair1.lit2 ); + } + if ( pair2.lit1 > pair2.lit2 ) + { + fanin_lit2 = index_list.add_xor( pair2.lit1, pair2.lit2 ); + } + else + { + fanin_lit2 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + } + else + { + fanin_lit1 = index_list.add_and( pair1.lit1, pair1.lit2 ); + fanin_lit2 = index_list.add_and( pair2.lit1, pair2.lit2 ); + } + uint32_t const output_lit = index_list.add_and( fanin_lit1 ^ 0x1, fanin_lit2 ^ 0x1 ); + return output_lit + on_off; + } + } + } + return std::nullopt; + } + + std::optional find_xor() + { + /* collect XOR-type pairs (d1 ^ d2) & off = 0 or ~(d1 ^ d2) & on = 0, selecting d1, d2 from binate_divs */ + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + auto const tt_xor = get_div( binate_divs[i] ) ^ get_div( binate_divs[j] ); + bool unateness[4] = { false, false, false, false }; + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + unateness[0] = true; + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + unateness[1] = true; + } + + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( binate_divs[i] << 1, binate_divs[j] << 1, true ); + unateness[2] = true; + } + if ( kitty::intersection_is_empty( tt_xor, on_off_sets[1] ) && !kitty::intersection_is_empty( tt_xor, on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( binate_divs[i] << 1 ) + 1, binate_divs[j] << 1, true ); + unateness[3] = true; + } + + if ( unateness[0] && unateness[2] ) + { + return index_list.add_xor( ( binate_divs[i] << 1 ), ( binate_divs[j] << 1 ) ); + } + if ( unateness[1] && unateness[3] ) + { + return index_list.add_xor( ( binate_divs[i] << 1 ) + 1, ( binate_divs[j] << 1 ) ); + } + } + } + + return std::nullopt; + } + + /* collect AND-type pairs (d1 & d2) & off = 0 or ~(d1 & d2) & on = 0, selecting d1, d2 from binate_divs */ + void collect_unate_pairs() + { + for ( auto i = 0u; i < binate_divs.size(); ++i ) + { + for ( auto j = i + 1; j < binate_divs.size(); ++j ) + { + collect_unate_pairs_detail<1, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 1>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<1, 0>( binate_divs[i], binate_divs[j] ); + collect_unate_pairs_detail<0, 0>( binate_divs[i], binate_divs[j] ); + } + } + } + + template + void collect_unate_pairs_detail( uint32_t div1, uint32_t div2 ) + { + /* check intersection with off-set; additionally check intersection with on-set is not empty (otherwise it's useless) */ + if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) ) + { + pos_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + /* check intersection with on-set; additionally check intersection with off-set is not empty (otherwise it's useless) */ + else if ( kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[1] ) && !kitty::intersection_is_empty( get_div( div1 ), get_div( div2 ), on_off_sets[0] ) ) + { + neg_unate_pairs.emplace_back( ( div1 << 1 ) + (uint32_t)( !pol1 ), ( div2 << 1 ) + (uint32_t)( !pol2 ) ); + } + } + + inline TT const& get_div( uint32_t idx ) const + { + if constexpr ( static_params::copy_tts ) + { + return divisors[idx]; + } + else + { + return ( *ptts )[divisors[idx]]; + } + } + +private: + std::array on_off_sets; + std::array num_bits; /* number of bits in on-set and off-set */ + + const typename static_params::truth_table_storage_type* ptts; + std::vector> divisors; + + index_list_t index_list; + + /* positive unate: not overlapping with off-set + negative unate: not overlapping with on-set */ + std::vector pos_unate_lits, neg_unate_lits; + std::vector binate_divs; + std::vector pos_unate_pairs, neg_unate_pairs; + + stats& st; +}; /* xag_resyn_decompose */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/retiming.hpp b/include/mockturtle/algorithms/retiming.hpp new file mode 100644 index 0000000..7cb0704 --- /dev/null +++ b/include/mockturtle/algorithms/retiming.hpp @@ -0,0 +1,808 @@ +/* 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 retiming.hpp + \brief Retiming + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include + +namespace mockturtle +{ +/*! \brief Parameters for retiming. + * + * The data structure `retime_params` holds configurable parameters + * with default arguments for `retime`. + */ +struct retime_params +{ + /*! \brief Do forward only retiming. */ + bool forward_only{ false }; + + /*! \brief Do backward only retiming. */ + bool backward_only{ false }; + + /*! \brief Retiming max iterations. */ + uint32_t iterations{ UINT32_MAX }; + + /*! \brief Be verbose */ + bool verbose{ false }; +}; + +/*! \brief Statistics for retiming. + * + * The data structure `retime_stats` provides data collected by running + * `retime`. + */ +struct retime_stats +{ + /*! \brief Initial number of registers. */ + uint32_t registers_pre{ 0 }; + + /*! \brief Number of registers after retime. */ + uint32_t registers_post{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] Initial registers = {:7d}\t Final registers = {:7d}\n", registers_pre, registers_post ); + std::cout << fmt::format( "[i] Total runtime = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class retime_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr uint32_t sink_node = UINT32_MAX; + +public: + explicit retime_impl( Ntk& ntk, retime_params const& ps, retime_stats& st ) + : _ntk( ntk ), + _ps( ps ), + _st( st ), + _flow_path( ntk ) + {} + +public: + void run() + { + stopwatch t( _st.time_total ); + + _st.registers_pre = _ntk.num_registers(); + + if ( !_ps.backward_only ) + { + bool improvement = true; + for ( auto i = 0; i < _ps.iterations && improvement == true; ++i ) + { + improvement = retime_area( i + 1 ); + } + } + + if ( !_ps.forward_only ) + { + bool improvement = true; + for ( auto i = 0; i < _ps.iterations && improvement == true; ++i ) + { + improvement = retime_area( i + 1 ); + } + } + + _st.registers_post = _ntk.num_registers(); + } + +private: + template + bool retime_area( uint32_t iteration ) + { + auto const num_registers_pre = _ntk.num_registers(); + + init_values(); + + auto min_cut = max_flow( iteration ); + + if ( _ps.verbose ) + { + float register_improvement = ( (float)num_registers_pre - min_cut.size() ) / num_registers_pre * 100; + std::cout << fmt::format( "[i] Retiming {}\t pre = {:7d}\t post = {:7d}\t improvement = {:>5.2f}%\n", + forward ? "forward" : "backward", num_registers_pre, min_cut.size(), register_improvement ); + } + + if ( min_cut.size() >= num_registers_pre ) + return false; + + /* move registers */ + update_registers_position( min_cut, iteration ); + + return true; + } + + template + std::vector max_flow( uint32_t iteration ) + { + uint32_t flow = 0; + + _flow_path.reset(); + _ntk.incr_trav_id(); + + /* run max flow from each register (capacity 1) */ + _ntk.foreach_register( [&]( auto const& n ) { + uint32_t local_flow; + if constexpr ( forward ) + { + local_flow = max_flow_forwards_compute_rec( _ntk.fanout( n )[0] ); + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + local_flow = max_flow_backwards_compute_rec( fanin ); + } + + flow += local_flow; + + if ( local_flow ) + _ntk.incr_trav_id(); + + return true; + } ); + + /* run reachability */ + _ntk.incr_trav_id(); + _ntk.foreach_register( [&]( auto const& n ) { + uint32_t local_flow; + if constexpr ( forward ) + { + local_flow = max_flow_forwards_compute_rec( _ntk.fanout( n )[0] ); + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + local_flow = max_flow_backwards_compute_rec( fanin ); + } + + assert( local_flow == 0 ); + return true; + } ); + + auto min_cut = get_min_cut(); + + // assert( check_min_cut( min_cut, iteration ) ); + + legalize_retiming( min_cut, iteration ); + + return min_cut; + } + + uint32_t max_flow_forwards_compute_rec( node const& n ) + { + uint32_t found_path = 0; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return 0; + + _ntk.set_visited( n, _ntk.trav_id() ); + + /* node is not in a flow path */ + if ( _flow_path[n] == 0 ) + { + /* cut boundary (sink) */ + if ( _ntk.value( n ) ) + { + _flow_path[n] = sink_node; + return 1; + } + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + /* there is a path for flow */ + if ( max_flow_forwards_compute_rec( f ) ) + { + _flow_path[n] = _ntk.node_to_index( f ); + found_path = 1; + return false; + } + return true; + } ); + + return found_path; + } + + /* path has flow already, find alternative path from fanin with flow */ + node fanin_flow = 0; + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + if ( _flow_path[f] == _ntk.node_to_index( n ) ) + { + fanin_flow = _ntk.get_node( f ); + return false; + } + return true; + } ); + + if ( fanin_flow == 0 ) + return 0; + + /* augment path */ + _ntk.foreach_fanout( fanin_flow, [&]( auto const& f ) { + /* there is a path for flow */ + if ( max_flow_forwards_compute_rec( f ) ) + { + _flow_path[fanin_flow] = _ntk.node_to_index( f ); + found_path = 1; + return false; + } + return true; + } ); + + if ( found_path ) + return 1; + + if ( max_flow_forwards_compute_rec( fanin_flow ) ) + { + _flow_path[fanin_flow] = 0; + return 1; + } + + return 0; + } + + uint32_t max_flow_backwards_compute_rec( node const& n ) + { + uint32_t found_path = 0; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return 0; + + _ntk.set_visited( n, _ntk.trav_id() ); + + /* node is not in a flow path */ + if ( _flow_path[n] == 0 ) + { + /* cut boundary (sink) */ + if ( _ntk.value( n ) ) + { + _flow_path[n] = sink_node; + return 1; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + /* there is a path for flow */ + if ( max_flow_backwards_compute_rec( _ntk.get_node( f ) ) ) + { + _flow_path[n] = _ntk.node_to_index( _ntk.get_node( f ) ); + found_path = 1; + return false; + } + return true; + } ); + + return found_path; + } + + /* path has flow already, find alternative path from fanout with flow */ + node fanout_flow = 0; + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( _flow_path[f] == _ntk.node_to_index( n ) ) + { + fanout_flow = _ntk.get_node( f ); + return false; + } + return true; + } ); + + if ( fanout_flow == 0 ) + return 0; + + /* augment path */ + _ntk.foreach_fanin( fanout_flow, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + /* there is a path for flow */ + if ( max_flow_backwards_compute_rec( _ntk.get_node( f ) ) ) + { + _flow_path[fanout_flow] = _ntk.node_to_index( _ntk.get_node( f ) ); + found_path = 1; + return false; + } + return true; + } ); + + if ( found_path ) + return 1; + + if ( max_flow_backwards_compute_rec( fanout_flow ) ) + { + _flow_path[fanout_flow] = 0; + return 1; + } + + return 0; + } + + std::vector get_min_cut() + { + std::vector min_cut; + min_cut.reserve( _ntk.num_registers() ); + + _ntk.foreach_node( [&]( auto const& n ) { + if ( _flow_path[n] == 0 ) + return true; + if ( _ntk.visited( n ) != _ntk.trav_id() ) + return true; + + if ( _ntk.value( n ) || _ntk.visited( _flow_path[n] ) != _ntk.trav_id() ) + min_cut.push_back( n ); + return true; + } ); + + return min_cut; + } + + template + void legalize_retiming( std::vector& min_cut, uint32_t iteration ) + { + _ntk.clear_values(); + + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( _ntk.fanout( n )[0], 1 ); + } ); + + for ( auto const& n : min_cut ) + { + rec_mark_tfi( n ); + } + + min_cut.clear(); + + if constexpr ( forward ) + { + _ntk.foreach_gate( [&]( auto const& n ) { + if ( _ntk.value( n ) == 1 ) + { + /* if is sink or before a register */ + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( _ntk.value( f ) != 1 ) + { + min_cut.push_back( n ); + return false; + } + return true; + } ); + } + } ); + } + else + { + _ntk.incr_trav_id(); + _ntk.foreach_register( [&]( auto const& n ) { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + collect_cut_nodes_tfi( fanin, min_cut ); + return true; + } ); + _ntk.foreach_node( [&]( auto const& n ) { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + _ntk.set_value( n, 1 ); + else + _ntk.set_value( n, 0 ); + } ); + for ( auto const& n : min_cut ) + _ntk.set_value( n, 0 ); + } + } + + void collect_cut_nodes_tfi( node const& n, std::vector& min_cut ) + { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return; + + _ntk.set_visited( n, _ntk.trav_id() ); + + if ( _ntk.value( n ) ) + { + min_cut.push_back( n ); + return; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + collect_cut_nodes_tfi( _ntk.get_node( f ), min_cut ); + } ); + } + + template + void init_values() + { + _ntk.clear_values(); + + /* marks the frontiers */ + if constexpr ( forward ) + { + /* mark POs as sink */ + _ntk.foreach_po( [&]( auto const& f ) { + _ntk.set_value( _ntk.get_node( f ), 1 ); + } ); + + /* mark registers as sink */ + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + _ntk.set_value( _ntk.get_node( f ), 1 ); + } ); + } ); + + /* exclude reachable nodes from PIs from retiming */ + _ntk.foreach_pi( [&]( auto const& n ) { + rec_mark_tfo( n ); + } ); + + /* mark childrens of marked nodes */ + std::vector to_mark; + to_mark.reserve( 200 ); + _ntk.foreach_gate( [&]( auto const& n ) { + if ( _ntk.value( n ) == 1 ) + { + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + if ( _ntk.value( _ntk.get_node( f ) ) == 0 ) + to_mark.push_back( _ntk.get_node( f ) ); + } ); + } + } ); + for ( auto const& n : to_mark ) + { + _ntk.set_value( n, 1 ); + } + } + else + { + /* mark PIs as sink */ + _ntk.foreach_pi( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + } ); + + /* mark registers as sink */ + _ntk.foreach_register( [&]( auto const& n ) { + _ntk.set_value( n, 1 ); + _ntk.foreach_fanout( n, [&]( auto const& f ) { + _ntk.set_value( f, 1 ); + } ); + } ); + + /* exclude reachable nodes from POs from retiming */ + _ntk.foreach_po( [&]( auto const& f ) { + rec_mark_tfi( _ntk.get_node( f ) ); + } ); + } + } + + template + void update_registers_position( std::vector const& min_cut, uint32_t iteration ) + { + _ntk.incr_trav_id(); + + /* create new registers and mark the ones to reuse */ + for ( auto const& n : min_cut ) + { + if constexpr ( forward ) + { + if ( _ntk.is_box_output( n ) ) + { + /* reuse the current register */ + auto node_register = _ntk.get_node( _ntk.get_fanin0( n ) ); + auto in_register = _ntk.get_node( _ntk.get_fanin0( node_register ) ); + auto in_in_register = _ntk.get_node( _ntk.get_fanin0( in_register ) ); + + /* check for marked fanouts to connect to register input */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( _ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, in_in_register ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( node_register, _ntk.trav_id() ); + } + else + { + /* create a new register */ + auto const in_register = _ntk.create_box_input( _ntk.make_signal( n ) ); + auto const node_register = _ntk.create_register( in_register ); + auto const node_register_out = _ntk.create_box_output( node_register ); + + /* replace in n fanout */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( f != _ntk.get_node( in_register ) && !_ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, node_register_out ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( _ntk.get_node( node_register ), _ntk.trav_id() ); + } + } + else + { + if ( _ntk.is_box_input( n ) ) + { + _ntk.foreach_fanout( n, [&]( auto const& f ) { + _ntk.set_visited( f, _ntk.trav_id() ); + } ); + } + else + { + /* create a new register */ + auto const in_register = _ntk.create_box_input( _ntk.make_signal( n ) ); + auto const node_register = _ntk.create_register( in_register ); + auto const node_register_out = _ntk.create_box_output( node_register ); + + /* replace in n fanout */ + auto fanout = _ntk.fanout( n ); + for ( auto const& f : fanout ) + { + if ( f != _ntk.get_node( in_register ) && _ntk.value( f ) ) + { + _ntk.replace_in_node( f, n, node_register_out ); + _ntk.decr_fanout_size( n ); + } + } + + _ntk.set_visited( _ntk.get_node( node_register ), _ntk.trav_id() ); + } + } + } + + /* remove retimed registers */ + _ntk.foreach_register( [&]( auto const& n ) { + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return true; + + node node_register_out; + node node_register_in = _ntk.get_node( _ntk.get_fanin0( n ) ); + signal node_register_in_in = _ntk.get_fanin0( node_register_in ); + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + node_register_out = f; + } ); + + auto node_register_fanout = _ntk.fanout_size( node_register_out ); + auto fanin_fanout = _ntk.fanout_size( _ntk.get_node( node_register_in_in ) ); + auto fanin_type = _ntk.is_box_output( _ntk.get_node( node_register_in_in ) ); + + _ntk.substitute_node( node_register_out, node_register_in_in ); + + return true; + } ); + } + + void rec_mark_tfo( node const& n ) + { + if ( _ntk.value( n ) ) + return; + + _ntk.set_value( n, 1 ); + _ntk.foreach_fanout( n, [&]( auto const& f ) { + rec_mark_tfo( f ); + } ); + } + + void rec_mark_tfi( node const& n ) + { + if ( _ntk.value( n ) ) + return; + + _ntk.set_value( n, 1 ); + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return; + rec_mark_tfi( _ntk.get_node( f ) ); + } ); + } + + template + bool check_min_cut( std::vector const& min_cut, uint32_t iteration ) + { + _ntk.incr_trav_id(); + + for ( node const& n : min_cut ) + { + _ntk.set_visited( n, _ntk.trav_id() ); + } + + bool check = true; + _ntk.foreach_register( [&]( auto const& n ) { + if constexpr ( forward ) + { + if ( !check_min_cut_rec( _ntk.fanout( n )[0] ) ) + check = false; + } + else + { + node fanin = _ntk.get_node( _ntk.get_fanin0( n ) ); + if ( !check_min_cut_rec( fanin ) ) + check = false; + } + + return check; + } ); + + return check; + } + + template + bool check_min_cut_rec( node const& n ) + { + bool check = true; + + if ( _ntk.visited( n ) == _ntk.trav_id() ) + return true; + + _ntk.set_visited( n, _ntk.trav_id() ); + + if constexpr ( forward ) + { + if ( _ntk.is_co( n ) ) + { + check = false; + return false; + } + + _ntk.foreach_fanout( n, [&]( auto const& f ) { + if ( !check_min_cut_rec( f ) ) + { + check = false; + } + } ); + } + else + { + if ( _ntk.is_ci( n ) ) + { + check = false; + return false; + } + + _ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( _ntk.is_constant( _ntk.get_node( f ) ) ) + return true; + if ( !check_min_cut_rec( _ntk.get_node( f ) ) ) + { + check = false; + } + return check; + } ); + + return check; + } + + return check; + } + +private: + Ntk& _ntk; + retime_params const& _ps; + retime_stats& _st; + + node_map _flow_path; +}; + +} /* namespace detail */ + +/*! \brief Retiming. + * + * This function implements a retiming algorithm for registers minimization. + * The only supported network type is the `generic_network`. + * The algorithm excecutes the retiming inplace. + * + * Currently, only area-based retiming is implemented. Mixed register types + * such as (active high/low, rising/falling edge) are not supported yet. + * + * **Required network functions:** + * - `size` + * - `is_pi` + * - `is_constant` + * - `node_to_index` + * - `index_to_node` + * - `get_node` + * - `foreach_po` + * - `foreach_node` + * - `fanout_size` + * - `has_incr_value` + * - `has_decr_value` + * - `has_get_fanin0` + * + * \param ntk Network + * \param ps Retiming params + * \param pst Retiming statistics + * + * The implementation of this algorithm was inspired by the + * mapping command ``retime`` in ABC. + */ +template +void retime( Ntk& ntk, retime_params const& ps = {}, retime_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_get_fanin0_v, "Ntk does not implement the get_fanin0 method" ); + + retime_stats st; + + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::retime_impl p( fanout_view, ps, st ); + p.run(); + + if ( ps.verbose ) + st.report(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/rewrite.hpp b/include/mockturtle/algorithms/rewrite.hpp new file mode 100644 index 0000000..e515dae --- /dev/null +++ b/include/mockturtle/algorithms/rewrite.hpp @@ -0,0 +1,953 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file rewrite.hpp + \brief Inplace rewrite + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/window_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/rewrite_cut.hpp" +#include "reconv_cut.hpp" +#include "simulation.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for Rewrite. + * + * The data structure `rewrite_params` holds configurable parameters with + * default arguments for `rewrite`. + */ +struct rewrite_params +{ + rewrite_params() + { + /* 0 < Cut limit < 16 */ + cut_enumeration_ps.cut_limit = 8; + cut_enumeration_ps.minimize_truth_table = true; + } + + /*! \brief Cut enumeration parameters. */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief If true, candidates are only accepted if they do not increase logic depth. */ + bool preserve_depth{ false }; + + /*! \brief Allow rewrite with multiple structures */ + bool allow_multiple_structures{ true }; + + /*! \brief Allow zero-gain substitutions */ + bool allow_zero_gain{ false }; + + /*! \brief Use satisfiability don't cares for optimization. */ + bool use_dont_cares{ false }; + + /*! \brief Window size for don't cares calculation. */ + uint32_t window_size{ 8u }; + + /*! \brief Be verbose. */ + bool verbose{ false }; +}; + +/*! \brief Statistics for rewrite. + * + * The data structure `rewrite_stats` provides data collected by running + * `rewrite`. + */ +struct rewrite_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Expected gain. */ + uint32_t estimated_gain{ 0 }; + + /*! \brief Candidates */ + uint32_t candidates{ 0 }; + + void report() const + { + std::cout << fmt::format( "[i] total time = {:>5.2f} secs\n", to_seconds( time_total ) ); + } +}; + +namespace detail +{ + +template +class rewrite_impl +{ + static constexpr uint32_t num_vars = 4u; + static constexpr uint32_t max_window_size = 8u; + using network_cuts_t = dynamic_network_cuts; + using cut_manager_t = detail::dynamic_cut_enumeration_impl; + using cut_t = typename network_cuts_t::cut_t; + using node_data = typename Ntk::storage::element_type::node_type; + +public: + rewrite_impl( Ntk& ntk, Library&& library, rewrite_params const& ps, rewrite_stats& st, NodeCostFn const& cost_fn ) + : ntk( ntk ), library( library ), ps( ps ), st( st ), cost_fn( cost_fn ), required( ntk, UINT32_MAX ) + { + register_events(); + } + + ~rewrite_impl() + { + if constexpr ( has_level_v ) + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + } + + void run() + { + stopwatch t( st.time_total ); + + ntk.incr_trav_id(); + + if ( ps.preserve_depth ) + { + compute_required(); + } + + if ( ps.use_dont_cares ) + perform_rewriting_dc(); + else + perform_rewriting(); + + st.estimated_gain = _estimated_gain; + st.candidates = _candidates; + } + +private: + void perform_rewriting() + { + /* initialize the cut manager */ + cut_enumeration_stats cst; + network_cuts_t cuts( ntk.size() + ( ntk.size() >> 1 ) ); + cut_manager_t cut_manager( ntk, ps.cut_enumeration_ps, cst, cuts ); + + /* initialize cuts for constant nodes and PIs */ + cut_manager.init_cuts(); + + auto& db = library.get_database(); + + std::array, num_vars> leaves; + std::array, num_vars> best_leaves; + std::array permutation; + signal best_signal; + + const auto size = ntk.size(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( ntk.fanout_size( n ) == 0u ) + return; + + int32_t best_gain = -1; + uint32_t best_level = UINT32_MAX; + bool best_phase = false; + + /* update level for node */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + uint32_t level = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + level = std::max( level, ntk.level( ntk.get_node( f ) ) ); + } ); + ntk.set_level( n, level + 1 ); + best_level = level + 1; + } + } + + cut_manager.clear_cuts( n ); + cut_manager.compute_cuts( n ); + + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cut */ + if ( ( cut->size() == 1 && *cut->begin() == ntk.node_to_index( n ) ) ) + { + ++cut_index; + continue; + } + + /* Boolean matching */ + auto config = kitty::exact_npn_canonization( cuts.truth_table( *cut ) ); + auto tt_npn = std::get<0>( config ); + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + + auto const structures = library.get_supergates( tt_npn ); + + if ( structures == nullptr ) + { + ++cut_index; + continue; + } + + uint32_t negation = 0; + for ( auto j = 0u; j < num_vars; ++j ) + { + permutation[perm[j]] = j; + negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + + /* save output negation to apply */ + bool phase = ( neg >> num_vars == 1 ) ? true : false; + + { + auto j = 0u; + for ( auto const leaf : *cut ) + { + leaves[permutation[j++]] = ntk.make_signal( ntk.index_to_node( leaf ) ); + } + + while ( j < num_vars ) + leaves[permutation[j++]] = ntk.get_constant( false ); + } + + for ( auto j = 0u; j < num_vars; ++j ) + { + if ( ( negation >> j ) & 1 ) + { + leaves[j] = !leaves[j]; + } + } + + { + /* measure the MFFC contained in the cut */ + int32_t mffc_size = measure_mffc_deref( n, cut ); + + for ( auto const& dag : *structures ) + { + auto [nodes_added, level] = evaluate_entry( n, db.get_node( dag.root ), leaves ); + int32_t gain = mffc_size - nodes_added; + + /* discard if dag.root and n are the same */ + if ( ntk.node_to_index( n ) == db.value( db.get_node( dag.root ) ) >> 1 ) + continue; + + /* discard if no gain */ + if ( gain < 0 || ( !ps.allow_zero_gain && gain == 0 ) ) + continue; + + /* discard if level increases */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth && level > required[n] ) + continue; + } + + if ( ( gain > best_gain ) || ( gain == best_gain && level < best_level ) ) + { + ++_candidates; + best_gain = gain; + best_signal = dag.root; + best_leaves = leaves; + best_phase = phase; + best_level = level; + } + + if ( !ps.allow_multiple_structures ) + break; + } + + /* restore contained MFFC */ + measure_mffc_ref( n, cut ); + ++cut_index; + + if ( cut->size() == 0 || ( cut->size() == 1 && *cut->begin() != ntk.node_to_index( n ) ) ) + break; + } + } + + if ( best_gain > 0 || ( ps.allow_zero_gain && best_gain == 0 ) ) + { + /* replace node wth the new structure */ + topo_view topo{ db, best_signal }; + auto new_f = cleanup_dangling( topo, ntk, best_leaves.begin(), best_leaves.end() ).front(); + + assert( n != ntk.get_node( new_f ) ); + + _estimated_gain += best_gain; + ntk.substitute_node_no_restrash( n, new_f ^ best_phase ); + + if constexpr ( has_level_v ) + { + /* propagate new required to leaves */ + if ( ps.preserve_depth ) + { + propagate_required_rec( ntk.node_to_index( n ), ntk.get_node( new_f ), size, required[n] ); + assert( ntk.level( ntk.get_node( new_f ) ) <= required[n] ); + } + } + + clear_cuts_fanout_rec( cuts, cut_manager, ntk.get_node( new_f ) ); + } + } ); + } + + void perform_rewriting_dc() + { + /* initialize the cut manager */ + cut_enumeration_stats cst; + network_cuts_t cuts( ntk.size() + ( ntk.size() >> 1 ) ); + cut_manager_t cut_manager( ntk, ps.cut_enumeration_ps, cst, cuts ); + + /* initialize cuts for constant nodes and PIs */ + cut_manager.init_cuts(); + + auto& db = library.get_database(); + + std::array, num_vars> leaves; + std::array, num_vars> best_leaves; + std::array permutation; + signal best_signal; + + reconvergence_driven_cut_parameters rps; + rps.max_leaves = ps.window_size; + reconvergence_driven_cut_statistics rst; + detail::reconvergence_driven_cut_impl> reconv_cuts( ntk, rps, rst ); + unordered_node_map, Ntk> tts( ntk ); + + color_view color_ntk{ ntk }; + std::array divisors; + for ( uint32_t i = 0; i < num_vars; ++i ) + { + divisors[i] = i; + } + + const auto size = ntk.size(); + ntk.foreach_gate( [&]( auto const& n, auto i ) { + if ( ntk.fanout_size( n ) == 0u ) + return; + + int32_t best_gain = -1; + uint32_t best_level = UINT32_MAX; + bool best_phase = false; + + /* update level for node */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth ) + { + uint32_t level = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + level = std::max( level, ntk.level( ntk.get_node( f ) ) ); + } ); + ntk.set_level( n, level + 1 ); + best_level = level + 1; + } + } + + cut_manager.clear_cuts( n ); + cut_manager.compute_cuts( n ); + + /* compute window */ + std::vector> roots = { n }; + auto const extended_leaves = reconv_cuts.run( roots ).first; + std::vector> gates{ collect_nodes( color_ntk, extended_leaves, roots ) }; + window_view window_ntk{ color_ntk, extended_leaves, roots, gates }; + + default_simulator> sim; + tts.reset(); + simulate_nodes_with_node_map>( window_ntk, tts, sim ); + + uint32_t cut_index = 0; + for ( auto& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + /* skip trivial cut */ + if ( ( cut->size() == 1 && *cut->begin() == ntk.node_to_index( n ) ) ) + { + ++cut_index; + continue; + } + + /* Boolean matching */ + auto config = kitty::exact_npn_canonization( cuts.truth_table( *cut ) ); + auto tt_npn = std::get<0>( config ); + auto neg = std::get<1>( config ); + auto perm = std::get<2>( config ); + + kitty::static_truth_table care; + + bool containment = true; + for ( auto const& l : *cut ) + { + if ( color_ntk.color( ntk.index_to_node( l ) ) != color_ntk.current_color() ) + { + containment = false; + break; + } + } + + if ( containment ) + { + /* compute care set */ + for ( auto i = 0u; i < ( 1u << window_ntk.num_pis() ); ++i ) + { + uint32_t entry{ 0u }; + auto j = 0u; + for ( auto const& l : *cut ) + { + entry |= kitty::get_bit( tts[l], i ) << j; + ++j; + } + kitty::set_bit( care, entry ); + } + } + else + { + /* completely specified */ + care = ~care; + } + + auto const dc_npn = apply_npn_transformation( ~care, neg & ~( 1 << num_vars ), perm ); + auto const structures = library.get_supergates( tt_npn, dc_npn, neg, perm ); + + if ( structures == nullptr ) + { + ++cut_index; + continue; + } + + uint32_t negation = 0; + for ( auto j = 0u; j < num_vars; ++j ) + { + permutation[perm[j]] = j; + negation |= ( ( neg >> perm[j] ) & 1 ) << j; + } + + /* save output negation to apply */ + bool phase = ( neg >> num_vars == 1 ) ? true : false; + + { + auto j = 0u; + for ( auto const leaf : *cut ) + { + leaves[permutation[j++]] = ntk.make_signal( ntk.index_to_node( leaf ) ); + } + + while ( j < num_vars ) + leaves[permutation[j++]] = ntk.get_constant( false ); + } + + for ( auto j = 0u; j < num_vars; ++j ) + { + if ( ( negation >> j ) & 1 ) + { + leaves[j] = !leaves[j]; + } + } + + { + /* measure the MFFC contained in the cut */ + int32_t mffc_size = measure_mffc_deref( n, cut ); + + for ( auto const& dag : *structures ) + { + auto [nodes_added, level] = evaluate_entry( n, db.get_node( dag.root ), leaves ); + int32_t gain = mffc_size - nodes_added; + + /* discard if dag.root and n are the same */ + if ( ntk.node_to_index( n ) == db.value( db.get_node( dag.root ) ) >> 1 ) + continue; + + /* discard if no gain */ + if ( gain < 0 || ( !ps.allow_zero_gain && gain == 0 ) ) + continue; + + /* discard if level increases */ + if constexpr ( has_level_v ) + { + if ( ps.preserve_depth && level > required[n] ) + continue; + } + + if ( ( gain > best_gain ) || ( gain == best_gain && level < best_level ) ) + { + ++_candidates; + best_gain = gain; + best_signal = dag.root; + best_leaves = leaves; + best_phase = phase; + best_level = level; + } + + if ( !ps.allow_multiple_structures ) + break; + } + + /* restore contained MFFC */ + measure_mffc_ref( n, cut ); + ++cut_index; + + if ( cut->size() == 0 || ( cut->size() == 1 && *cut->begin() != ntk.node_to_index( n ) ) ) + break; + } + } + + if ( best_gain > 0 || ( ps.allow_zero_gain && best_gain == 0 ) ) + { + /* replace node wth the new structure */ + topo_view topo{ db, best_signal }; + auto new_f = cleanup_dangling( topo, ntk, best_leaves.begin(), best_leaves.end() ).front(); + + assert( n != ntk.get_node( new_f ) ); + + _estimated_gain += best_gain; + ntk.substitute_node_no_restrash( n, new_f ^ best_phase ); + + if constexpr ( has_level_v ) + { + /* propagate new required to leaves */ + if ( ps.preserve_depth ) + { + propagate_required_rec( ntk.node_to_index( n ), ntk.get_node( new_f ), size, required[n] ); + assert( ntk.level( ntk.get_node( new_f ) ) <= required[n] ); + } + } + + clear_cuts_fanout_rec( cuts, cut_manager, ntk.get_node( new_f ) ); + } + } ); + } + + int32_t measure_mffc_ref( node const& n, cut_t const* cut ) + { + /* reference cut leaves */ + for ( auto leaf : *cut ) + { + ntk.incr_fanout_size( ntk.index_to_node( leaf ) ); + } + + int32_t mffc_size = static_cast( recursive_ref( n ) ); + + /* dereference leaves */ + for ( auto leaf : *cut ) + { + ntk.decr_fanout_size( ntk.index_to_node( leaf ) ); + } + + return mffc_size; + } + + int32_t measure_mffc_deref( node const& n, cut_t const* cut ) + { + /* reference cut leaves */ + for ( auto leaf : *cut ) + { + ntk.incr_fanout_size( ntk.index_to_node( leaf ) ); + } + + int32_t mffc_size = static_cast( recursive_deref( n ) ); + + /* dereference leaves */ + for ( auto leaf : *cut ) + { + ntk.decr_fanout_size( ntk.index_to_node( leaf ) ); + } + + return mffc_size; + } + + uint32_t recursive_deref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.decr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_deref( ntk.get_node( s ) ); + } + } ); + return value; + } + + uint32_t recursive_ref( node const& n ) + { + /* terminate? */ + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return 0; + + /* recursively collect nodes */ + uint32_t value{ cost_fn( ntk, n ) }; + ntk.foreach_fanin( n, [&]( auto const& s ) { + if ( ntk.incr_fanout_size( ntk.get_node( s ) ) == 0 ) + { + value += recursive_ref( ntk.get_node( s ) ); + } + } ); + return value; + } + + inline std::pair evaluate_entry( node const& current_root, node const& n, std::array, num_vars> const& leaves ) + { + auto& db = library.get_database(); + db.incr_trav_id(); + + return evaluate_entry_rec( current_root, n, leaves ); + } + + std::pair evaluate_entry_rec( node const& current_root, node const& n, std::array, num_vars> const& leaves ) + { + auto& db = library.get_database(); + if ( db.is_pi( n ) || db.is_constant( n ) ) + return { 0, 0 }; + if ( db.visited( n ) == db.trav_id() ) + return { 0, 0 }; + + db.set_visited( n, db.trav_id() ); + + int32_t area = 0; + uint32_t level = 0; + bool hashed = true; + + std::array, Ntk::max_fanin_size> node_data; + db.foreach_fanin( n, [&]( auto const& f, auto i ) { + node g = db.get_node( f ); + if ( db.is_constant( g ) ) + { + node_data[i] = f; /* ntk.get_costant( db.is_complemented( f ) ) */ + return; + } + if ( db.is_pi( g ) ) + { + node_data[i] = leaves[db.node_to_index( g ) - 1] ^ db.is_complemented( f ); + if constexpr ( has_level_v ) + { + level = std::max( level, ntk.level( ntk.get_node( leaves[db.node_to_index( g ) - 1] ) ) ); + } + return; + } + + auto [area_rec, level_rec] = evaluate_entry_rec( current_root, g, leaves ); + area += area_rec; + level = std::max( level, level_rec ); + + /* check value */ + if ( db.value( g ) < UINT32_MAX ) + { + signal s; + s.data = static_cast( db.value( g ) ); + node_data[i] = s ^ db.is_complemented( f ); + } + else + { + hashed = false; + } + } ); + + if ( hashed ) + { + /* try hash */ + /* AIG, XAG, MIG, and XMG are supported now */ + std::optional> val; + do + { + /* XAG */ + if constexpr ( has_has_and_v && has_has_xor_v ) + { + if ( db.is_and( n ) ) + val = ntk.has_and( node_data[0], node_data[1] ); + else + val = ntk.has_xor( node_data[0], node_data[1] ); + break; + } + + /* AIG */ + if constexpr ( has_has_and_v ) + { + val = ntk.has_and( node_data[0], node_data[1] ); + break; + } + + /* XMG */ + if constexpr ( has_has_maj_v && has_has_xor3_v ) + { + if ( db.is_maj( n ) ) + val = ntk.has_maj( node_data[0], node_data[1], node_data[2] ); + else + val = ntk.has_xor3( node_data[0], node_data[1], node_data[2] ); + break; + } + + /* MAJ */ + if constexpr ( has_has_maj_v ) + { + val = ntk.has_maj( node_data[0], node_data[1], node_data[2] ); + break; + } + std::cerr << "[e] Only AIGs, XAGs, MAJs, and XMGs are currently supported \n"; + } while ( false ); + + if ( val.has_value() ) + { + /* bad condition (current root is contained in the DAG): return a very high cost */ + if ( db.get_node( *val ) == current_root ) + return { UINT32_MAX / 2, level + 1 }; + + /* annotate hashing info */ + db.set_value( n, val->data ); + return { area + ( ntk.fanout_size( ntk.get_node( *val ) ) > 0 ? 0 : cost_fn( ntk, n ) ), level + 1 }; + } + } + + db.set_value( n, UINT32_MAX ); + return { area + cost_fn( ntk, n ), level + 1 }; + } + + void compute_required() + { + if constexpr ( has_level_v ) + { + ntk.foreach_po( [&]( auto const& f ) { + required[f] = ntk.depth(); + } ); + + for ( uint32_t index = ntk.size() - 1; index > ntk.num_pis(); index-- ) + { + node n = ntk.index_to_node( index ); + uint32_t req = required[n]; + + ntk.foreach_fanin( n, [&]( auto const& f ) { + required[f] = std::min( required[f], req - 1 ); + } ); + } + } + } + + void propagate_required_rec( uint32_t root, node const& n, uint32_t size, uint32_t req ) + { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; + + /* recursively update required time */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + auto const g = ntk.get_node( f ); + + /* recur if it is still a node to explore and to update */ + if ( ntk.node_to_index( g ) > root && ( ntk.node_to_index( g ) >= size || required[g] > req ) ) + propagate_required_rec( root, g, size, req - 1 ); + + /* update the required time */ + if ( ntk.node_to_index( g ) < size ) + required[g] = std::min( required[g], req - 1 ); + } ); + } + + void clear_cuts_fanout_rec( network_cuts_t& cuts, cut_manager_t& cut_manager, node const& n ) + { + ntk.foreach_fanout( n, [&]( auto const& g ) { + auto const index = ntk.node_to_index( g ); + if ( cuts.cuts( index ).size() > 0 ) + { + cut_manager.clear_cuts( g ); + clear_cuts_fanout_rec( cuts, cut_manager, g ); + } + } ); + } + +private: + void register_events() + { + if constexpr ( has_level_v ) + { + auto const update_level_of_new_node = [&]( const auto& n ) { + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_deleted_node = [&]( node const& n ) { + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + } + + /* maybe it should be moved to depth_view */ + void update_node_level( node const& n, bool top_most = true ) + { + if constexpr ( has_level_v ) + { + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + + /* update only one more level */ + if ( top_most ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); + } + } + } + } + +private: + Ntk& ntk; + Library&& library; + rewrite_params const& ps; + rewrite_stats& st; + NodeCostFn cost_fn; + + node_map required; + + uint32_t _candidates{ 0 }; + uint32_t _estimated_gain{ 0 }; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +} /* namespace detail */ + +/*! \brief Boolean rewrite. + * + * This algorithm rewrites enumerated cuts using new network structures from a database. + * The algorithm performs changes in-place and keeps the substituted structures dangling + * in the network. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `make_signal` + * - `foreach_gate` + * - `substitute_node` + * - `clear_visited` + * - `clear_values` + * - `fanout_size` + * - `set_value` + * - `foreach_node` + * + * \param ntk Input network (will be changed in-place) + * \param library Exact library containing pre-computed structures + * \param ps Rewrite params + * \param pst Rewrite statistics + * \param cost_fn Node cost function (a functor with signature `uint32_t(Ntk const&, node const&)`) + */ +template> +void rewrite( Ntk& ntk, Library&& library, rewrite_params const& ps = {}, rewrite_stats* pst = nullptr, NodeCostFn const& cost_fn = {} ) +{ + 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_size_v, "Ntk does not implement the size method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_clear_visited_v, "Ntk does not implement the clear_visited method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + + rewrite_stats st; + + if ( ps.preserve_depth || ps.use_dont_cares ) + { + using depth_view_t = depth_view; + depth_view_t depth_ntk{ ntk }; + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ depth_ntk }; + + detail::rewrite_impl p( fanout_view, library, ps, st, cost_fn ); + p.run(); + } + else + { + using fanout_view_t = fanout_view; + fanout_view_t fanout_view{ ntk }; + + detail::rewrite_impl p( fanout_view, library, ps, st, cost_fn ); + p.run(); + } + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/satlut_mapping.hpp b/include/mockturtle/algorithms/satlut_mapping.hpp new file mode 100644 index 0000000..fab0cca --- /dev/null +++ b/include/mockturtle/algorithms/satlut_mapping.hpp @@ -0,0 +1,480 @@ +/* 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 satlut_mapping.hpp + \brief SAT LUT mapping + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../generators/sorting.hpp" +#include "../utils/include/percy.hpp" +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" +#include "cell_window.hpp" +#include "cut_enumeration.hpp" +#include "cut_enumeration/mf_cut.hpp" + +#include + +namespace mockturtle +{ + +/*! \brief Parameters for satlut_mapping. + * + * The data structure `satlut_mapping_params` holds configurable parameters with + * default arguments for `satlut_mapping`. + */ +struct satlut_mapping_params +{ + satlut_mapping_params() + { + cut_enumeration_ps.cut_size = 6; + cut_enumeration_ps.cut_limit = 8; + } + + /*! \brief Parameters for cut enumeration + * + * The default cut size is 6, the default cut limit is 8. + */ + cut_enumeration_params cut_enumeration_ps{}; + + /*! \brief Conflict limit for SAT solver. + * + * The default limit is 0, which means the number of conflicts is not used + * as a resource limit. + */ + uint32_t conflict_limit{ 0u }; + + /*! \brief Show progress. */ + bool progress{ false }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Be very verbose. */ + bool very_verbose{ false }; +}; + +/*! \brief Statistics for satlut_mapping. + * + * The data structure `satlut_mapping_stats` provides data collected by running + * `satlut_mapping`. + */ +struct satlut_mapping_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Total runtime. */ + stopwatch<>::duration time_sat{ 0 }; + + /*! \brief Number of SAT variables. */ + uint64_t num_vars{ 0u }; + + /*! \brief Number of SAT clauses. */ + uint64_t num_clauses{ 0u }; + + void report() + { + std::cout << fmt::format( "[i] total time = {:>7.2f} secs\n", to_seconds( time_total ) ) + << fmt::format( "[i] SAT solving time = {:>7.2f} secs\n", to_seconds( time_sat ) ) + << fmt::format( "[i] number of SAT variables = {}\n", num_vars ) + << fmt::format( "[i] number of SAT clauses = {}\n", num_clauses ); + } +}; + +namespace detail +{ + +template +std::vector cardinality_network( Solver& solver, std::vector const& vars, int& next_var ) +{ + int lits[3]; + + auto logn = static_cast( ceil( log2( vars.size() ) ) ); + auto current = vars; + + if ( current.size() != static_cast( 1u ) << logn ) + { + current.resize( static_cast( 1u ) << logn, next_var ); + lits[0] = pabc::Abc_Var2Lit( next_var++, 1 ); + solver.add_clause( lits, lits + 1 ); + } + + batcher_sorting_network( static_cast( current.size() ), [&]( auto a, auto b ) { + auto va = current[a]; + auto vb = current[b]; + auto va_next = next_var++; + auto vb_next = next_var++; + + // AND(a, b) a + !c , b + !c , !a + !b + c + lits[0] = pabc::Abc_Var2Lit( va, 0 ); + lits[1] = pabc::Abc_Var2Lit( va_next, 1 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( vb, 0 ); + lits[1] = pabc::Abc_Var2Lit( va_next, 1 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( va, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb, 1 ); + lits[2] = pabc::Abc_Var2Lit( va_next, 0 ); + solver.add_clause( lits, lits + 3 ); + + // OR(a, b) !a + c , !b + c , a + b + !c + lits[0] = pabc::Abc_Var2Lit( va, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb_next, 0 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( vb, 1 ); + lits[1] = pabc::Abc_Var2Lit( vb_next, 0 ); + solver.add_clause( lits, lits + 2 ); + lits[0] = pabc::Abc_Var2Lit( va, 0 ); + lits[1] = pabc::Abc_Var2Lit( vb, 0 ); + lits[2] = pabc::Abc_Var2Lit( vb_next, 1 ); + solver.add_clause( lits, lits + 3 ); + + current[a] = va_next; + current[b] = vb_next; + } ); + + for ( auto i = 0u; i < current.size() - 1; ++i ) + { + lits[0] = pabc::Abc_Var2Lit( current[i], 1 ); + lits[1] = pabc::Abc_Var2Lit( current[i + 1], 0 ); + solver.add_clause( lits, lits + 2 ); + } + + return current; +} + +template +class satlut_mapping_impl +{ +public: + using network_cuts_t = network_cuts; + using cut_t = typename network_cuts_t::cut_t; + +public: + satlut_mapping_impl( Ntk& ntk, satlut_mapping_params const& ps, satlut_mapping_stats& st ) + : ntk( ntk ), + ps( ps ), + st( st ), + cuts( cut_enumeration( ntk, ps.cut_enumeration_ps ) ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + std::vector card_inp; + node_map gate_var( ntk ); + node_map, Ntk> cut_vars( ntk ); + auto next_var = 0; + + percy::bsat_wrapper solver; + + /* initialize gate vars */ + ntk.foreach_gate( [&]( auto n ) { + card_inp.push_back( next_var ); + gate_var[n] = next_var++; + } ); + + const auto card_out = cardinality_network( solver, card_inp, next_var ); + + /* create clauses */ + int cut_lits[2]; + ntk.foreach_gate( [&]( auto n ) { + std::vector gate_is_mapped; + gate_is_mapped.push_back( pabc::Abc_Var2Lit( gate_var[n], 1 ) ); + + for ( auto const& cut : cuts.cuts( ntk.node_to_index( n ) ) ) + { + if ( cut->size() == 1 ) + { + break; /* we assume that trivial cuts are in the end of the set */ + } + gate_is_mapped.push_back( pabc::Abc_Var2Lit( next_var, 0 ) ); + cut_lits[0] = pabc::Abc_Var2Lit( next_var, 1 ); + cut_vars[n].push_back( next_var++ ); + for ( auto leaf : *cut ) + { + if ( ntk.is_pi( ntk.index_to_node( leaf ) ) ) + continue; + cut_lits[1] = pabc::Abc_Var2Lit( gate_var[ntk.index_to_node( leaf )], 0 ); + solver.add_clause( cut_lits, cut_lits + 2 ); + } + } + + solver.add_clause( &gate_is_mapped[0], &gate_is_mapped[0] + gate_is_mapped.size() ); + } ); + + /* outputs must be mapped */ + ntk.foreach_po( [&]( auto f ) { + auto lit = pabc::Abc_Var2Lit( gate_var[f], 0 ); + solver.add_clause( &lit, &lit + 1 ); + } ); + + st.num_vars = solver.nr_vars(); + st.num_clauses = solver.nr_clauses(); + + auto best_size = ntk.has_mapping() ? ntk.num_cells() + 1 : card_inp.size(); + + progress_bar pbar{ "satlut iteration = {0} try size = {1}", ps.progress }; + auto iteration = 0u; + while ( true ) + { + pbar( ++iteration, best_size ); + if ( best_size > card_out.size() ) + { + std::cout << fmt::format( "[e] best_size = {} card_inp.size() = {} card_out.size() = {} ntk.num_cells = {} ntk.has_mapping = {}\n", + best_size, card_inp.size(), card_out.size(), ntk.num_cells(), ntk.has_mapping() ); + assert( false ); + } + auto assump = pabc::Abc_Var2Lit( card_out[card_out.size() - best_size], 1 ); + + const auto result = call_with_stopwatch( st.time_sat, [&]() { return solver.solve( &assump, &assump + 1, ps.conflict_limit ); } ); + if ( result == percy::success ) + { + ntk.clear_mapping(); + ntk.foreach_gate( [&]( auto n ) { + if ( solver.var_value( gate_var[n] ) ) + { + for ( auto i = 0u; i < cut_vars[n].size(); ++i ) + { + if ( solver.var_value( cut_vars[n][i] ) ) + { + const auto index = ntk.node_to_index( n ); + std::vector> nodes; + for ( auto const& l : cuts.cuts( index )[i] ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + ntk.set_cell_function( n, cuts.truth_table( cuts.cuts( index )[i] ) ); + } + break; + } + } + } + } ); + + if ( ntk.num_cells() == ntk.num_pos() ) + { + /* no further improvement possible */ + break; + } + + best_size = ntk.num_cells(); + } + else + { + break; + } + } + } + +private: + Ntk& ntk; + satlut_mapping_params const& ps; + satlut_mapping_stats& st; + network_cuts_t cuts; +}; + +} // namespace detail + +/*! \brief SAT-LUT mapping. + * + * This algorithm implements the SAT-based area-oriented LUT mapping algorithm + * presented in [B. Schmitt, A. Mishchenko, and R.K. Brayton, *ASP-DAC* **23** + * (2018), 586-591]. + * + * The interface is similar to the one in `lut_mapping`. + * + * This algorithm applies SAT-LUT mapping to the whole networking and therefore + * may show poor performance for larger networks. There exists a method with + * the same name that takes as input a window size to apply SAT-LUT mapping to + * windows. + * + * **Required network functions:** + * - `is_pi` + * - `index_to_node` + * - `node_to_index` + * - `foreach_gate` + * - `foreach_po` + * - `num_gates` + * - `num_cells` + * - `has_mapping` + * - `clear_mapping` + * - `add_to_mapping` + * - `set_cell_function` if `StoreFunction` is true + * + * \param ntk Logic network to be mapped + * \param ps Parameters + * \param st Statistics + */ +template +void satlut_mapping( Ntk& ntk, satlut_mapping_params const& ps = {}, satlut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_cells_v, "Ntk does not implement the num_cells method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + satlut_mapping_stats st; + detail::satlut_mapping_impl p( ntk, ps, st ); + p.run(); + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +/*! \brief SAT-LUT mapping (windowed). + * + * This algorithm applies SAT-LUT mapping to windows of a given size (e.g., 32, + * 64, 128) and can therefore better deal with larger networks. It has + * otherwise the same interface as `satlut_mapping`. + * + * The initial network must already contain a mapping, e.g., found with + * `lut_mapping`. + * + * **Required network functions:** + * - `is_pi` + * - `index_to_node` + * - `node_to_index` + * - `foreach_gate` + * - `foreach_po` + * - `num_gates` + * - `num_cells` + * - `has_mapping` + * - `clear_mapping` + * - `add_to_mapping` + * - `is_cell_root` + * - `set_cell_function` if `StoreFunction` is true + * + * \param ntk Logic network to be mapped + * \param window_size Maximum number of gates in a window + * \param ps Parameters + * \param st Statistics + */ +template +void satlut_mapping( Ntk& ntk, uint32_t window_size, satlut_mapping_params ps = {}, satlut_mapping_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_cells_v, "Ntk does not implement the num_cells method" ); + static_assert( has_has_mapping_v, "Ntk does not implement the has_mapping method" ); + static_assert( has_clear_mapping_v, "Ntk does not implement the clear_mapping method" ); + static_assert( has_add_to_mapping_v, "Ntk does not implement the add_to_mapping method" ); + static_assert( has_is_cell_root_v, "Ntk does not implement the is_cell_root method" ); + static_assert( !StoreFunction || has_set_cell_function_v, "Ntk does not implement the set_cell_function method" ); + + if ( !ntk.has_mapping() ) + { + return; + } + + satlut_mapping_stats st; + stopwatch<>::duration time_total{}; + cell_window window( ntk, window_size ); + progress_bar pbar{ ntk.size(), "satlut (windowed) |{0}| node = {1:>4} / " + std::to_string( ntk.size() ), ps.progress }; + ps.progress = false; /* do not show inner progress */ + ntk.foreach_gate( [&]( auto n, int index ) { + stopwatch<> t( time_total ); + pbar( index, ntk.node_to_index( n ) ); + if ( ntk.is_cell_root( n ) ) + { + if ( !window.compute_window_for( n ) ) /* window has been visited before */ + { + return true; + } + + if ( ps.verbose ) + { + std::cout << fmt::format( "[i] cell {:>5} size = {:>4} nodes = {:>2} gates = {:>3} pis = {:>3} pos = {:>3}\n", + n, + window.size(), + window.num_cells(), + window.num_gates(), + window.num_pis(), + window.num_pos() ); + } + if ( window.num_cells() == window.num_pos() || window.num_pos() == 0 ) + { + return true; + } + topo_view window_topo{ window }; + detail::satlut_mapping_impl p( window_topo, ps, st ); + p.run(); + return true; + } + + return true; + } ); + + st.time_total = time_total; + + if ( ps.verbose ) + { + st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/sim_resub.hpp b/include/mockturtle/algorithms/sim_resub.hpp new file mode 100644 index 0000000..6ebe334 --- /dev/null +++ b/include/mockturtle/algorithms/sim_resub.hpp @@ -0,0 +1,459 @@ +/* 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 sim_resub.hpp + \brief Simulation-Guided Resubstitution + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../io/write_patterns.hpp" +#include "../networks/aig.hpp" +#include "../networks/xag.hpp" +#include "../networks/mig.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "circuit_validator.hpp" +#include "pattern_generation.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/mux_resyn_select_opt.hpp" +#include "resyn_engines/mux_resyn_xnor_sel.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "resyn_engines/mig_resyn.hpp" +#include "simulation.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct sim_resub_stats +{ + /*! \brief Time for pattern generation. */ + stopwatch<>::duration time_patgen{ 0 }; + + /*! \brief Time for saving patterns. */ + stopwatch<>::duration time_patsave{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_sim{ 0 }; + + /*! \brief Time for SAT solving. */ + stopwatch<>::duration time_sat{ 0 }; + stopwatch<>::duration time_sat_restart{ 0 }; + + /*! \brief Time for computing ODCs. */ + stopwatch<>::duration time_odc{ 0 }; + + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for translating from index lists to network signals. */ + stopwatch<>::duration time_interface{ 0 }; + + /*! \brief Number of patterns used. */ + uint32_t num_pats{ 0 }; + + /*! \brief Number of counter-examples. */ + uint32_t num_cex{ 0 }; + + /*! \brief Number of successful resubstitutions. */ + uint32_t num_resub{ 0 }; + + /*! \brief Number of SAT solver timeout. */ + uint32_t num_timeout{ 0 }; + + /*! \brief Number of calls to the resynthesis engine. */ + uint32_t num_resyn{ 0 }; + + ResynSt resyn_st; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] ======== Stats ========\n" ); + fmt::print( "[i] #pat = {:6d}\n", num_pats ); + fmt::print( "[i] #resyn call = {:6d}\n", num_resyn ); + fmt::print( "[i] #valid = {:6d}\n", num_resub ); + fmt::print( "[i] #CEX = {:6d}\n", num_cex ); + fmt::print( "[i] #timeout = {:6d}\n", num_timeout ); + fmt::print( "[i] ======== Runtime ========\n" ); + fmt::print( "[i] generate pattern: {:>5.2f} secs [excluded]\n", to_seconds( time_patgen ) ); + fmt::print( "[i] save pattern : {:>5.2f} secs [excluded]\n", to_seconds( time_patsave ) ); + fmt::print( "[i] simulation : {:>5.2f} secs\n", to_seconds( time_sim ) ); + fmt::print( "[i] SAT solve : {:>5.2f} secs\n", to_seconds( time_sat ) ); + fmt::print( "[i] SAT restart : {:>5.2f} secs\n", to_seconds( time_sat_restart ) ); + fmt::print( "[i] compute ODCs : {:>5.2f} secs\n", to_seconds( time_odc ) ); + fmt::print( "[i] interfacing : {:>5.2f} secs\n", to_seconds( time_interface ) ); + fmt::print( "[i] compute function: {:>5.2f} secs\n", to_seconds( time_resyn ) ); + fmt::print( "[i] ======== Details ========\n" ); + resyn_st.report(); + fmt::print( "[i] =========================\n\n" ); + } +}; + +/*! \brief Simulation-based resubstitution engine. + * + * This engine simulates the entire network using partial truth tables and calls a + * resynthesis engine (template parameter `ResynEngine`) to find potential resubstitutions. + * If a resubstitution candidate is found, it then formally verifies it with SAT solving. + * If the validation fails, a counter-example will be added to the simulation patterns, + * and resynthesis will be invoked again with updated truth tables, looping until it returns + * `std::nullopt`. This engine only requires the divisor collector to prepare `divs`. + * + * Please refer to the following paper for further details. + * + * [1] A Simulation-Guided Paradigm for Logic Synthesis and Verification. TCAD, 2022. + * + * Required interface of `ResynEngine`: + * - A public `operator()`: `std::optional operator()` + * `( TT const& target, TT const& care, iterator_type begin, iterator_type end, + * truth_table_storage_type const& tts, uint32_t max_size )` + * + * All classes implemented in `algorithms/resyn_engines/` are compatible. + * + * \tparam validator_t Specialization of `circuit_validator`. + * \tparam ResynEngine A resynthesis solver to compute the resubstitution candidate. + * \tparam MffcRes Typename of `potential_gain`. + */ +template, class ResynEngine = xag_resyn_decompose>, typename MffcRes = uint32_t> +class simulation_based_resub_engine +{ +public: + static constexpr bool require_leaves_and_mffc = false; + using stats = sim_resub_stats; + using mffc_result_t = MffcRes; + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using TT = kitty::partial_truth_table; + + explicit simulation_based_resub_engine( Ntk& ntk, resubstitution_params const& ps, stats& st ) + : ntk( ntk ), ps( ps ), st( st ), tts( ntk ), validator( ntk, { ps.max_clauses, ps.odc_levels, ps.conflict_limit, ps.random_seed } ), engine( st.resyn_st ) + { + if constexpr ( !validator_t::use_odc_ ) + { + assert( ps.odc_levels == 0 && "to consider ODCs, circuit_validator::use_odc (the last template parameter) has to be turned on" ); + } + + add_event = ntk.events().register_add_event( [&]( const auto& n ) { + tts.resize(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } ); + } + + ~simulation_based_resub_engine() + { + if ( ps.save_patterns ) + { + call_with_stopwatch( st.time_patsave, [&]() { + write_patterns( sim, *ps.save_patterns ); + } ); + } + + if ( add_event ) + { + ntk.events().release_add_event( add_event ); + } + } + + void init() + { + /* prepare simulation patterns */ + call_with_stopwatch( st.time_patgen, [&]() { + if ( ps.pattern_filename ) + { + sim = partial_simulator( *ps.pattern_filename ); + } + else + { + sim = partial_simulator( ntk.num_pis(), 1024 ); + pattern_generation( ntk, sim ); + } + + if constexpr ( has_EXCDC_interface_v ) + { + sim.remove_CDC_patterns( ntk ); + } + } ); + st.num_pats = sim.num_bits(); + assert( sim.num_bits() > 0 ); + + /* first simulation: the whole circuit; from 0 bits. */ + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + + void update() + { + if constexpr ( validator_t::use_odc_ || has_EXODC_interface_v ) + { + call_with_stopwatch( st.time_sat_restart, [&]() { + validator.update(); + } ); + tts.reset(); + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, true ); + } ); + } + } + + std::optional run( node const& n, std::vector const& divs, mffc_result_t potential_gain, uint32_t& last_gain ) + { + for ( auto j = 0u; j < ps.max_trials; ++j ) + { + check_tts( n ); + for ( auto const& d : divs ) + { + check_tts( d ); + } + + TT const care = call_with_stopwatch( st.time_odc, [&]() { + return ( ps.odc_levels == 0 ) ? sim.compute_constant( true ) : ~observability_dont_cares( ntk, n, sim, tts, ps.odc_levels ); + } ); + + const auto res = call_with_stopwatch( st.time_resyn, [&]() { + ++st.num_resyn; + return engine( tts[n], care, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, ps.max_inserts ) ); + } ); + + if ( res ) + { + auto const& id_list = *res; + assert( id_list.num_pos() == 1u ); + last_gain = potential_gain - id_list.num_gates(); + auto valid = call_with_stopwatch( st.time_sat, [&]() { + return validator.validate( n, divs, id_list ); + } ); + if ( valid ) + { + if ( *valid ) + { + ++st.num_resub; + signal out_sig; + call_with_stopwatch( st.time_interface, [&]() { + std::vector divs_sig( divs.size() ); + std::transform( divs.begin(), divs.end(), divs_sig.begin(), [&]( const node n ) { + return ntk.make_signal( n ); + } ); + insert( ntk, divs_sig.begin(), divs_sig.end(), id_list, [&]( signal const& s ) { + out_sig = s; + } ); + } ); + return out_sig; + } + else + { + found_cex(); + continue; + } + } + else /* timeout */ + { + return std::nullopt; + } + } + else /* functor can not find any potential resubstitution */ + { + return std::nullopt; + } + } + return std::nullopt; + } + + void found_cex() + { + ++st.num_cex; + call_with_stopwatch( st.time_sim, [&]() { + sim.add_pattern( validator.cex ); + } ); + + /* re-simulate the whole circuit (for the last block) when a block is full */ + if ( sim.num_bits() % 64 == 0 ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_nodes( ntk, tts, sim, false ); + } ); + } + } + + void check_tts( node const& n ) + { + if ( tts[n].num_bits() != sim.num_bits() ) + { + call_with_stopwatch( st.time_sim, [&]() { + simulate_node( ntk, n, tts, sim ); + } ); + } + } + +private: + Ntk& ntk; + resubstitution_params const& ps; + stats& st; + + incomplete_node_map tts; + partial_simulator sim; + + validator_t validator; + ResynEngine engine; + + /* events */ + std::shared_ptr::add_event_type> add_event; +}; /* simulation_based_resub_engine */ + +template +void sim_resubstitution_run( Ntk& ntk, resubstitution_params const& ps, resubstitution_stats* pst ) +{ + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + st.time_resub -= engine_st.time_patgen; + st.time_total -= engine_st.time_patgen + engine_st.time_patsave; + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace detail */ + +template +void sim_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v, "Currently only supports AIG, XAG, and MIG" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + if constexpr ( std::is_same_v ) + { + using resyn_engine_t = xag_resyn_decompose>; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } + else if constexpr ( std::is_same_v ) + { + using resyn_engine_t = xag_resyn_decompose>; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } + else if constexpr ( std::is_same_v ) + { + using resyn_engine_t = mig_resyn_topdown; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } + else if constexpr ( std::is_same_v ) + { + //using resyn_engine_t = mux_resyn; + //using resyn_engine_t = mux_resyn_select_opt; + using resyn_engine_t = mux_resyn_xnor_sel; + + if ( ps.odc_levels != 0 ) + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + else + { + using validator_t = circuit_validator; + using resub_impl_t = typename detail::resubstitution_impl>; + detail::sim_resubstitution_run( resub_view, ps, pst ); + } + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/simulation.hpp b/include/mockturtle/algorithms/simulation.hpp new file mode 100644 index 0000000..119c96a --- /dev/null +++ b/include/mockturtle/algorithms/simulation.hpp @@ -0,0 +1,982 @@ +/* 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 simulation.hpp + \brief Simulate networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Abstract template class for simulation. */ +template +class default_simulator +{ +public: + default_simulator() = delete; +}; + +/*! \brief Simulates Boolean assignments. + * + * This simulator simulates Boolean values. A vector with assignments for each + * primary input must be passed to the constructor. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( std::vector const& assignments ) : assignments( assignments ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return assignments[index]; } + bool compute_not( bool value ) const { return !value; } + +private: + std::vector assignments; +}; + +/*! \brief Simulates Boolean assignments with input word. + * + * This simulator simulates Boolean values. A bitstring with assignments for + * each primary input must be passed to the constructor. Because this + * bitstring can have at most 64 bits, this simulator is not suitable for + * logic networks with more than 64 primary inputs. + */ +class input_word_simulator +{ +public: + input_word_simulator( uint64_t word ) : word( word ) {} + + bool compute_constant( bool value ) const { return value; } + bool compute_pi( uint32_t index ) const { return ( word >> index ) & 1; } + bool compute_not( bool value ) const { return !value; } + +private: + uint64_t word; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables be + * passed to the constructor of the simulator. + */ +template<> +class default_simulator +{ +public: + default_simulator() = delete; + default_simulator( unsigned num_vars ) : num_vars( num_vars ) {} + + kitty::dynamic_truth_table compute_constant( bool value ) const + { + kitty::dynamic_truth_table tt( num_vars ); + return value ? ~tt : tt; + } + + kitty::dynamic_truth_table compute_pi( uint32_t index ) const + { + kitty::dynamic_truth_table tt( num_vars ); + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::dynamic_truth_table compute_not( kitty::dynamic_truth_table const& value ) const + { + return ~value; + } + +private: + unsigned num_vars; +}; + +/*! \brief Simulates truth tables. + * + * This simulator simulates truth tables. Each primary input is assigned the + * projection function according to the index. The number of variables must be + * known at compile time. + */ +template +class default_simulator> +{ +public: + kitty::static_truth_table compute_constant( bool value ) const + { + kitty::static_truth_table tt; + return value ? ~tt : tt; + } + + kitty::static_truth_table compute_pi( uint32_t index ) const + { + kitty::static_truth_table tt; + kitty::create_nth_var( tt, index ); + return tt; + } + + kitty::static_truth_table compute_not( kitty::static_truth_table const& value ) const + { + return ~value; + } +}; + +/*! \brief Simulates partial truth tables. + * + * This simulator simulates partial truth tables, whose length is flexible + * and new simulation patterns can be added. + */ +class partial_simulator +{ + friend class bit_packed_simulator; + +public: + partial_simulator() {} + + /*! \brief Create a `partial_simulator` with random simulation patterns. + * + * \param num_pis Number of primary inputs, which is the same as the length of a simulation pattern. + * \param num_patterns Number of initial random simulation patterns. + */ + partial_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : num_patterns( num_patterns ) + { + assert( num_pis > 0u ); + + for ( auto i = 0u; i < num_pis; ++i ) + { + patterns.emplace_back( num_patterns ); + kitty::create_random( patterns.back(), seed + i ); + } + } + + /* copy constructors */ + partial_simulator( partial_simulator const& sim ) = default; + partial_simulator& operator=( partial_simulator const& sim ) = default; + + /*! \brief Create a `partial_simulator` with given simulation patterns. + * + * \param initial_patterns Initial simulation patterns. + */ + partial_simulator( std::vector const& initial_patterns ) + : patterns( initial_patterns ), num_patterns( patterns.at( 0 ).num_bits() ) + {} + + /*! \brief Create a `partial_simulator` with simulation patterns read from a file. + * + * The simulation pattern file should contain `num_pis` lines of the same length. + * Each line is the simulation signature of a primary input, represented in hexadecimal. + * + * \param filename Name of the simulation pattern file. + * \param length Number of simulation patterns to keep. Should not be greater than 4 times + * the length of a line in the file. Setting this parameter to 0 means to keep all patterns in the file. + */ + partial_simulator( const std::string& filename, uint32_t length = 0u ) + { + std::ifstream in( filename, std::ifstream::in ); + std::string line; + + while ( getline( in, line ) ) + { + patterns.emplace_back( line.length() * 4 ); + kitty::create_from_hex_string( patterns.back(), line ); + if ( length != 0u ) + { + patterns.back().resize( length ); + } + } + + in.close(); + + assert( patterns.size() > 0 ); + num_patterns = patterns[0].num_bits(); + } + + kitty::partial_truth_table compute_constant( bool value ) const + { + kitty::partial_truth_table zero( num_patterns ); + return value ? ~zero : zero; + } + + kitty::partial_truth_table compute_pi( uint32_t index ) const + { + return patterns.at( index ); + } + + kitty::partial_truth_table compute_not( kitty::partial_truth_table const& value ) const + { + return ~value; + } + + /*! \brief Get the current number of simulation patterns. */ + uint32_t num_bits() const + { + return num_patterns; + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + */ + void add_pattern( std::vector const& pattern ) + { + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Get the simulation patterns. + * + * \return A vector of `num_pis()` patterns stored in `kitty::partial_truth_table`s. + */ + std::vector get_patterns() const + { + return patterns; + } + + template, typename = std::enable_if_t> + void remove_CDC_patterns( Ntk const& ntk ) + { + std::vector pattern( patterns.size() ); + for ( int i = 0; i < (int)num_patterns; ++i ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + pattern[j] = kitty::get_bit( patterns[j], i ); + } + if ( ntk.pattern_is_EXCDC( pattern ) ) + { + for ( auto j = 0u; j < patterns.size(); ++j ) + { + kitty::copy_bit( patterns[j], num_patterns - 1, patterns[j], i ); + } + --num_patterns; + --i; + } + } + for ( auto j = 0u; j < patterns.size(); ++j ) + { + patterns[j].resize( num_patterns ); + } + } + +private: + std::vector patterns; + uint32_t num_patterns; +}; + +/*! \brief Simulates partial truth tables, and performs bit packing when requested. + * + * This class has the same interfaces as `partial_simulator`, except that + * (1) care bits should be provided as the second argument of `add_pattern`; and + * (2) `pack_bits` can be called to reduce the size of pattern set. + */ +class bit_packed_simulator : public partial_simulator +{ +public: + using partial_simulator::compute_constant; + using partial_simulator::compute_not; + using partial_simulator::compute_pi; + using partial_simulator::get_patterns; + using partial_simulator::num_bits; + + bit_packed_simulator() {} + + bit_packed_simulator( uint32_t num_pis, uint32_t num_patterns, std::default_random_engine::result_type seed = 1 ) + : partial_simulator( num_pis, num_patterns, seed ), packed_patterns( num_patterns ) + { + fill_cares( num_pis ); + } + + /* copy constructors */ + bit_packed_simulator( bit_packed_simulator const& sim ) = default; + bit_packed_simulator& operator=( bit_packed_simulator const& sim ) = default; + + /* copy constructor from `partial_simulator` */ + bit_packed_simulator( partial_simulator const& sim ) + : partial_simulator( sim ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( std::vector const& initial_patterns ) + : partial_simulator( initial_patterns ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + bit_packed_simulator( const std::string& filename, uint32_t length = 0u ) + : partial_simulator( filename, length ), packed_patterns( num_patterns ) + { + fill_cares( patterns.size() ); + } + + /*! \brief Add a pattern (primary input assignment) into the pattern set. + * + * \param pattern The pattern. Length should be the same as number of PIs. + * \param care_bits Care bits of the pattern. Length should be the same as `pattern`. + */ + void add_pattern( std::vector const& pattern, std::vector const& care_bits ) + { + assert( pattern.size() == care_bits.size() ); + assert( pattern.size() == patterns.size() ); + + for ( auto i = 0u; i < pattern.size(); ++i ) + { + patterns.at( i ).add_bit( pattern.at( i ) ); + care.at( i ).add_bit( care_bits.at( i ) ); + } + ++num_patterns; + } + + /*! \brief Try to pack the newly added patterns (since the last call) into preceding patterns. + * + * \return `true` when some patterns are packed (so that update of simulated truth tables is needed) + */ + bool pack_bits() + { + if ( num_patterns == 0u ) + { + return false; + } + if ( num_patterns == packed_patterns ) + { + return false; + } + assert( num_patterns > packed_patterns ); + + std::vector empty_slots; + /* for each unpacked pattern (at `p`), try to pack it into one of the patterns before it (at `pos` in block `block`). */ + for ( int64_t p = num_patterns - 1; p >= (int64_t)packed_patterns; --p ) + { + for ( auto block = p < 1024 ? 0 : std::rand() % ( p >> 6 ); block <= ( p >> 6 ); ++block ) + { + uint64_t unavailable = 0u; + /* check each PI */ + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], p ) ) + { + continue; + } /* only check for the cared PIs of p */ + unavailable |= care[i]._bits[block]; + } + auto pos = kitty::find_first_bit_in_word( ~unavailable ); + if ( pos != -1 && ( block < ( p >> 6 ) || pos < ( p % 64 ) ) ) + { + move_pattern( p, pos + ( block << 6 ) ); + empty_slots.emplace_back( p ); + break; + } + } + } + + if ( empty_slots.size() > 0u ) + { + /* fill the empty slots (from smaller values; `empty_slots` should be reversely sorted) */ + /* `empty_slots[j]` is the smallest position where larger positions are all empty */ + int64_t j = 0; + for ( int64_t i = empty_slots.size() - 1; i >= 0; --i ) + { + while ( j <= i && empty_slots[j] >= num_patterns - 1 ) + { + if ( empty_slots[j] == num_patterns - 1 ) + { + --num_patterns; + } + ++j; + if ( j == (int64_t)empty_slots.size() ) + { + break; + } + } + if ( j > i ) + { + break; + } + move_pattern( num_patterns - 1, empty_slots[i] ); + --num_patterns; + } + assert( patterns[0].num_bits() - num_patterns == empty_slots.size() ); + for ( auto i = 0u; i < patterns.size(); ++i ) + { + patterns[i].resize( num_patterns ); + care[i].resize( num_patterns ); + } + packed_patterns = num_patterns; + return true; + } + packed_patterns = num_patterns; + return false; + } + + void randomize_dont_care_bits( std::default_random_engine::result_type seed = 1 ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + kitty::partial_truth_table tt( num_patterns ); + kitty::create_random( tt, std::default_random_engine::result_type( seed + patterns.size() + i ) ); + patterns.at( i ) = ( patterns.at( i ) & care.at( i ) ) | ( tt & ~care.at( i ) ); + } + } + +private: + /* all bits in patterns generated before construction are care bits */ + void fill_cares( uint64_t const num_pis ) + { + for ( auto i = 0u; i < num_pis; ++i ) + { + care.emplace_back( num_patterns ); + care.back() = ~care.back(); + } + } + + /* move the pattern at position `from` to position `to`. */ + void move_pattern( uint64_t const from, uint64_t const to ) + { + for ( auto i = 0u; i < patterns.size(); ++i ) + { + if ( !kitty::get_bit( care[i], from ) ) + { + continue; + } + assert( !kitty::get_bit( care[i], to ) ); + kitty::copy_bit( patterns[i], from, patterns[i], to ); + kitty::set_bit( care[i], to ); + kitty::clear_bit( care[i], from ); + } + } + +private: + std::vector care; + uint32_t packed_patterns; +}; + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +node_map simulate_nodes( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + node_map node_to_value( ntk ); + + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } ); + + return node_to_value; +} + +namespace detail +{ + +template +void simulate_nodes_with_node_map( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_compute_v, "Ntk does not implement the compute method for SimulationType" ); + + /* constants */ + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( false ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( !node_to_value.has( ntk.get_node( ntk.get_constant( true ) ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + if ( !node_to_value.has( n ) ) + { + node_to_value[n] = sim.compute_pi( i ); + } + } ); + + /* gates */ + ntk.foreach_gate( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( !node_to_value.has( n ) ) + { + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); +} + +} // namespace detail + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a map that maps each node to its computed simulation + * value. + * + * **Required network functions:** + * - `foreach_po` + * - `get_constant` + * - `constant_value` + * - `get_node` + * - `foreach_pi` + * - `foreach_gate` + * - `fanin_size` + * - `num_pos` + * - `compute` + * + * \param ntk Network + * \param node_to_value A map from nodes to values + * \param sim Simulator, which implements the simulator interface + */ +template> +void simulate_nodes( Ntk const& ntk, unordered_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +template> +void simulate_nodes( Ntk const& ntk, incomplete_node_map& node_to_value, Simulator const& sim = Simulator() ) +{ + detail::simulate_nodes_with_node_map>( ntk, node_to_value, sim ); +} + +namespace detail +{ +/* Forward declaration */ +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ); + +template +void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); +} + +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + std::vector fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + if ( !node_to_value.has( ntk.get_node( f ) ) ) + { + simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + else if ( node_to_value[ntk.get_node( f )].num_bits() != sim.num_bits() ) + { + re_simulate_fanin_cone( ntk, ntk.get_node( f ), node_to_value, sim ); + } + fanin_values[i] = node_to_value[ntk.get_node( f )]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + ntk.compute( n, node_to_value[n], fanin_values.begin(), fanin_values.end() ); +} + +template +void update_const_pi( Ntk const& ntk, Container& node_to_value, Simulator const& sim ) +{ + /* constants */ + node_to_value[ntk.get_constant( false )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_constant( true )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + /* pis */ + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); +} + +} // namespace detail + +/*! \brief (Re-)simulate `n` and its transitive fanin cone. + * + * Note that re-simulation (when `node_to_value.has( n ) == true`) is only done + * for the last block, no matter how many bits are used in this block. + * Hence, it is advised to call `simulate_nodes` with `simulate_whole_tt = false` + * whenever `sim.num_bits() % 64 == 0`. + * + */ +template> +void simulate_node( Ntk const& ntk, typename Ntk::node const& n, Container& node_to_value, Simulator const& sim ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + if ( node_to_value[ntk.get_node( ntk.get_constant( false ) )].num_bits() != sim.num_bits() ) + { + detail::update_const_pi( ntk, node_to_value, sim ); + } + + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + else if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } +} + +/*! \brief Simulates a network with `partial_simulator` (or `bit_packed_simulator`). + * + * This is the specialization for `partial_truth_table`. + * This function simulates every node in the circuit. + * + * \param simulate_whole_tt When this parameter is true, it is assumed that `node_to_value.has( n )` is false for every node. + * In contrast, when this parameter is false, only the last block of `partial_truth_table` will be re-computed, + * and it is assumed that `node_to_value.has( n )` is true for every node. + */ +template> +void simulate_nodes( Ntk const& ntk, Container& node_to_value, Simulator const& sim, bool simulate_whole_tt ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_constant_value_v, "Ntk does not implement the constant_value method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_compute_v, "Ntk does not implement the compute specialization for kitty::partial_truth_table" ); + static_assert( has_compute_inplace_v, "Ntk does not implement the in-place compute specialization for kitty::partial_truth_table" ); + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + detail::update_const_pi( ntk, node_to_value, sim ); + + /* gates */ + if ( simulate_whole_tt ) + { + ntk.foreach_gate( [&]( auto const& n ) { + if ( !node_to_value.has( n ) ) + { + detail::simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } + else + { + ntk.foreach_gate( [&]( auto const& n ) { + assert( node_to_value.has( n ) ); + if ( node_to_value[n].num_bits() != sim.num_bits() ) + { + detail::re_simulate_fanin_cone( ntk, n, node_to_value, sim ); + } + } ); + } +} + +/*! \brief Simulates a network with a generic simulator. + * + * This is a generic simulation algorithm that can simulate arbitrary values. + * In order to that, the network needs to implement the `compute` method for + * `SimulationType` and one must pass an instance of a `Simulator` that + * implements the three methods: + * - `SimulationType compute_constant(bool)` + * - `SimulationType compute_pi(index)` + * - `SimulationType compute_not(SimulationType const&)` + * + * The method `compute_constant` returns a simulation value for a constant + * value. The method `compute_pi` returns a simulation value for a primary + * input based on its index, and `compute_not` to invert a simulation value. + * + * This method returns a vector that maps each primary output (ordered by + * position) to it's simulation value (taking possible complemented attributes + * into account). + * + * **Required network functions:** + * - `foreach_po` + * - `is_complemented` + * - `compute` + * + * \param ntk Network + * \param sim Simulator, which implements the simulator interface + */ +template> +std::vector simulate( Ntk const& ntk, Simulator const& sim = Simulator() ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po function" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented function" ); + static_assert( has_compute_v, "Ntk does not implement the compute function for SimulationType" ); + + auto const node_to_value = simulate_nodes( ntk, sim ); + + std::vector po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +/*! \brief Simulates a buffered network + * + * The implementation is only slightly different from `simulate` by + * replacing `foreach_gate` with `foreach_node` and checking `fanin_size` + * because buffers are not counted as gates but still need to be simulated. + */ +template +std::vector> simulate_buffered( Ntk const& ntk ) +{ + static_assert( has_is_buf_v, "Ntk is not a buffered network type" ); + static_assert( has_compute_v>, "Ntk does not implement the compute function for static_truth_table" ); + assert( ntk.num_pis() == NumPIs ); + + default_simulator> sim; + node_map, Ntk> node_to_value( ntk ); + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_to_value[n] = sim.compute_pi( i ); + } ); + ntk.foreach_node( [&]( auto const& n ) { + // skip crossings + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return; + } + } + + if ( ntk.fanin_size( n ) > 0 ) + { + std::vector> fanin_values( ntk.fanin_size( n ) ); + auto const fanin_fun = [&]( auto const& f, auto i ) { + fanin_values[i] = node_to_value[f]; + }; + + if constexpr ( is_crossed_network_type_v ) + { + ntk.foreach_fanin_ignore_crossings( n, fanin_fun ); + } + else + { + ntk.foreach_fanin( n, fanin_fun ); + } + node_to_value[n] = ntk.compute( n, fanin_values.begin(), fanin_values.end() ); + } + } ); + + std::vector> po_values( ntk.num_pos() ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk.is_complemented( f ) ) + { + po_values[i] = sim.compute_not( node_to_value[f] ); + } + else + { + po_values[i] = node_to_value[f]; + } + } ); + return po_values; +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/testcase_minimizer.hpp b/include/mockturtle/algorithms/testcase_minimizer.hpp new file mode 100644 index 0000000..34a9a72 --- /dev/null +++ b/include/mockturtle/algorithms/testcase_minimizer.hpp @@ -0,0 +1,558 @@ +/* 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 testcase_minimizer.hpp + \brief Minimize testcase for debugging + + \author Siang-Yun (Sonia) Lee +*/ + +#include "../io/aiger_reader.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_aiger.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/aig.hpp" +#include "../utils/debugging_utils.hpp" +#include "../views/color_view.hpp" +#include "cleanup.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for testcase_minimizer. */ +struct testcase_minimizer_params +{ + /*! \brief File format of the testcase. */ + enum + { + verilog, + aiger + } file_format = verilog; + + /*! \brief Path to find the initial test case and to store the minimized test case. */ + std::string path{ "." }; + + /*! \brief File name of the initial test case (excluding extension). */ + std::string init_case{ "testcase" }; + + /*! \brief File name of the minimized test case (excluding extension). */ + std::string minimized_case{ "minimized" }; + + /*! \brief Maximum number of iterations in total. nullopt = infinity */ + std::optional num_iterations{ std::nullopt }; + + /*! \brief Step into the next stage if nothing works for this number of iterations. */ + std::optional num_iterations_stage{ std::nullopt }; + + /*! \brief Be verbose. */ + bool verbose{ false }; + + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; +}; /* testcase_minimizer_params */ + +/*! \brief Debugging testcase minimizer + * + * Given a (sequence of) algorithm(s) and a testcase that is + * known to trigger a bug in the algorithm(s), this utility + * minimizes the testcase by trying to remove parts of the network + * with an increasing granularity in each stage. Only changes after + * which the bug is still triggered are kept; otherwise, the change + * is reverted. + * + * The script of algorithm(s) to be run can be provided as (1) a + * lambda function taking a network as input and returning a Boolean, + * which is true if the script runs normally and is false otherwise + * (i.e. the buggy behavior is observed); or (2) (not supported on + * Windows platform) a lambda function making a command string to be + * called, taking a filename string as input. The command should return + * 0 if the program runs normally, return 1 if the concerned buggy + * behavior is observed, and return other values if the input network + * is not valid (thus the latest change will not be kept). If the + * command segfaults or an assertion fails, it is treated as observing + * the buggy behavior. + * + * + * + \verbatim embed:rst + + Usage + + .. code-block:: c++ + + auto opt = []( mig_network ntk ) -> bool { + direct_resynthesis resyn; + refactoring( ntk, resyn ); + return network_is_acyclic( ntk ); + }; + + auto make_command = []( std::string const& filename ) -> std::string { + return "./abc -c \"read " + filename + "; drw\""; + }; + + testcase_minimizer_params ps; + ps.path = "."; // current directory + testcase_minimizer minimizer( ps ); + minimizer.run( opt ); // debug algorithms implemented in mockturtle + minimizer.run( make_command ); // debug external scripts + \endverbatim +*/ +template +class testcase_minimizer +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit testcase_minimizer( testcase_minimizer_params const ps = {} ) + : ps( ps ) + { + // assert( ps.file_format != testcase_minimizer_params::aiger || std::is_same_v ); + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + file_extension = ".v"; + break; + case testcase_minimizer_params::aiger: + file_extension = ".aig"; + break; + default: + fmt::print( "[e] Unsupported format\n" ); + } + std::srand( ps.seed ); + } + + void run( std::function const& fn ) + { + if ( !read_initial_testcase() ) + { + return; + } + + if ( !test( fn ) ) + { + fmt::print( "[e] The initial test case does not trigger the buggy behavior\n" ); + return; + } + + uint32_t counter{ 0 }; + while ( !ps.num_iterations || counter++ < ps.num_iterations ) + { + ntk_backup2 = cleanup_dangling( ntk ); + if ( !reduce() ) + { + write_testcase( ps.minimized_case ); + break; + } + if ( ntk.num_gates() == 0 ) + { + ++stage_counter; + ntk = ntk_backup2; + continue; + } + + if ( test( fn ) ) + { + fmt::print( "[i] Testcase with I/O = {}/{} gates = {} triggers the buggy behavior\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + write_testcase( ps.minimized_case ); + stage_counter = 0; + sampled.clear(); + } + else + { + ++stage_counter; + ntk = ntk_backup2; + } + } + + if ( init_PIs != ntk.num_pis() || init_POs != ntk.num_pos() || init_gates != ntk.num_gates() ) + { + fmt::print( "[i] Minimized the testcase from I/O = {}/{} gates = {}\n", init_PIs, init_POs, init_gates ); + fmt::print( " to I/O = {}/{} gates = {}\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + } + } + +#ifndef _MSC_VER + void run( std::function const& make_command ) + { + if ( !read_initial_testcase() ) + { + return; + } + + if ( !test( make_command, ps.init_case ) ) + { + fmt::print( "[e] The initial test case does not trigger the buggy behavior\n" ); + return; + } + + uint32_t counter{ 0 }; + while ( !ps.num_iterations || counter++ < ps.num_iterations ) + { + ntk_backup2 = cleanup_dangling( ntk ); + if ( !reduce() ) + { + break; + } + if ( ntk.num_gates() == 0 ) + { + ++stage_counter; + ntk = ntk_backup2; + continue; + } + + write_testcase( "tmp" ); + + if ( test( make_command, "tmp" ) ) + { + fmt::print( "[i] Testcase with I/O = {}/{} gates = {} triggers the buggy behavior\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + write_testcase( ps.minimized_case ); + stage_counter = 0; + sampled.clear(); + } + else + { + ++stage_counter; + ntk = ntk_backup2; + } + } + + if ( init_PIs != ntk.num_pis() || init_POs != ntk.num_pos() || init_gates != ntk.num_gates() ) + { + fmt::print( "[i] Minimized the testcase from I/O = {}/{} gates = {}\n", init_PIs, init_POs, init_gates ); + fmt::print( " to I/O = {}/{} gates = {}\n", ntk.num_pis(), ntk.num_pos(), ntk.num_gates() ); + } + } +#endif + +private: + bool read_initial_testcase() + { + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + if ( lorina::read_verilog( ps.path + "/" + ps.init_case + file_extension, verilog_reader( ntk ) ) != lorina::return_code::success ) + { + fmt::print( "[e] Could not read test case `{}`\n", ps.path + "/" + ps.init_case + file_extension ); + return false; + } + break; + case testcase_minimizer_params::aiger: + if ( lorina::read_aiger( ps.path + "/" + ps.init_case + file_extension, aiger_reader( ntk ) ) != lorina::return_code::success ) + { + fmt::print( "[e] Could not read test case `{}`\n", ps.path + "/" + ps.init_case + file_extension ); + return false; + } + break; + default: + fmt::print( "[e] Unsupported format\n" ); + return false; + } + init_PIs = ntk.num_pis(); + init_POs = ntk.num_pos(); + init_gates = ntk.num_gates(); + return true; + } + + void write_testcase( std::string const& filename ) + { + switch ( ps.file_format ) + { + case testcase_minimizer_params::verilog: + write_verilog( ntk, ps.path + "/" + filename + file_extension ); + break; + case testcase_minimizer_params::aiger: + write_aiger( ntk, ps.path + "/" + filename + file_extension ); + break; + default: + fmt::print( "[e] Unsupported format\n" ); + } + } + + bool test( std::function const& fn ) + { + ntk_backup = cleanup_dangling( ntk ); + bool res = fn( ntk ); + ntk = ntk_backup; + was_FIT = !res; + return was_FIT; + } + +#ifndef _MSC_VER + bool test( std::function const& make_command, std::string const& filename ) + { + was_FIT = test_inner( make_command, filename ); + return was_FIT; + } + + bool test_inner( std::function const& make_command, std::string const& filename ) + { + std::string const command = make_command( ps.path + "/" + filename + file_extension ); + int status = std::system( command.c_str() ); + if ( status < 0 ) + { + std::cout << "[e] Unexpected error when calling command: " << strerror( errno ) << '\n'; + return false; + } + else + { + if ( WIFEXITED( status ) ) + { + if ( WEXITSTATUS( status ) == 0 ) // normal + return false; + else if ( WEXITSTATUS( status ) == 1 ) // buggy + return true; + else if ( WEXITSTATUS( status ) == 134 ) // assertion fail + return true; + else + { + std::cout << "[e] Unexpected return value: " << WEXITSTATUS( status ) << '\n'; + return false; + } + } + else // segfault + { + return true; + } + } + } +#endif + + bool reduce() + { + pos_to_remove = ntk.num_pos() >> 3; // 1/8 + + if ( ( ps.num_iterations_stage && stage_counter >= *ps.num_iterations_stage ) || + ( reducing_stage == many_pos && pos_to_remove > ntk.num_pos() - sampled.size() ) || + ( reducing_stage == many_pos && pos_to_remove < 2 ) || + ( reducing_stage == po && ntk.num_pos() == 1 ) || + ( reducing_stage == po && sampled.size() == ntk.num_pos() ) || + ( reducing_stage == pi && ntk.num_pis() > ntk.num_pos() ) || + ( reducing_stage == pi && sampled.size() == ntk.num_pis() ) || + ( reducing_stage == const_gate && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == mffc && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == half_mffc && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == simplify_tfo && sampled.size() == ntk.num_gates() ) || + ( reducing_stage == single_gate && sampled.size() == ntk.num_gates() ) ) + { + reducing_stage = static_cast( static_cast( reducing_stage ) + 1 ); + stage_counter = 0; + sampled.clear(); + } + + switch ( reducing_stage ) + { + case many_pos: + { + assert( pos_to_remove <= ntk.num_pos() - sampled.size() ); + if ( ps.verbose ) + fmt::print( "[i] Remove {} POs\n", pos_to_remove ); + for ( auto i = 0u; i < pos_to_remove; ++i ) + { + auto const& [ith_po, n] = get_random_po(); + if ( ntk.is_pi( n ) || ntk.is_constant( n ) ) + continue; + ntk.substitute_node( n, ntk.get_constant( false ) ); + } + break; + } + case po: + { + auto const& [ith_po, n] = get_random_po(); + if ( ps.verbose ) + fmt::print( "[i] Remove {}-th PO (node {})\n", ith_po, n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case pi: + { + const node n = get_random_pi(); + if ( ps.verbose ) + fmt::print( "[i] Remove PI {}\n", n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case const_gate: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with const0\n", n ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case mffc: + { + node const& n = get_random_gate(); + signal fi = ntk.create_pi(); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with a new PI {}\n", n, ntk.get_node( fi ) ); + ntk.substitute_node( n, fi ); + break; + } + case half_mffc: + { + node const& n = get_random_gate(); + signal fi; + uint32_t const ith_fanin = std::rand() % ntk.fanin_size( n ); + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( i == ith_fanin ) + fi = f; + } ); + if ( ps.verbose ) + fmt::print( "[i] Substitute gate {} with its {}-th fanin {}{}\n", n, ith_fanin, ntk.is_complemented( fi ) ? "!" : "", ntk.get_node( fi ) ); + ntk.substitute_node( n, fi ); + assert( network_is_acyclic( color_view{ ntk } ) ); + break; + } + case simplify_tfo: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Simplify TFO of gate {} with const0 but keep all its fanins\n", n ); + ntk.foreach_fanin( n, [&]( auto f ) { + ntk.create_po( f ); + } ); + ntk.substitute_node( n, ntk.get_constant( false ) ); + break; + } + case single_gate: + { + node const& n = get_random_gate(); + if ( ps.verbose ) + fmt::print( "[i] Remove a single gate {}\n", n ); + ntk.foreach_fanin( n, [&]( auto f ) { + ntk.create_po( f ); + } ); + signal fi = ntk.create_pi(); + ntk.substitute_node( n, fi ); + break; + } + default: + { + ntk = cleanup_dangling( ntk, true, true ); + return false; // all stages done, nothing to reduce + } + } + + ntk = cleanup_dangling( ntk, true, true ); + return true; + } + + std::pair get_random_po() + { + while ( true ) + { + uint32_t const ith_po = std::rand() % ntk.num_pos(); + const node n = ntk.get_node( ntk.po_at( ith_po ) ); + if ( sampled.find( ith_po ) == sampled.end() ) + { + sampled.insert( ith_po ); + return std::make_pair( ith_po, n ); + } + } + } + + node get_random_pi() + { + while ( true ) + { + uint32_t const ith_pi = std::rand() % ntk.num_pis() + 1; + if ( sampled.find( ith_pi ) == sampled.end() ) + { + sampled.insert( ith_pi ); + return ntk.index_to_node( ith_pi ); + } + } + } + + node get_random_gate() + { + while ( true ) + { + uint32_t const node_index = ( std::rand() % ntk.num_gates() ) + ntk.num_pis() + 1; + node n = ntk.index_to_node( node_index ); + if ( sampled.find( node_index ) == sampled.end() ) + { + sampled.insert( node_index ); + assert( !ntk.is_dead( n ) && !ntk.is_pi( n ) ); + return n; + } + } + } + + std::pair get_random_gate_with_gate_fanin() + { + while ( true ) + { + node const& n = get_random_gate(); + node ni; + bool has_gate_fanin = false; + ntk.foreach_fanin( n, [&]( auto const& f ) { + ni = ntk.get_node( f ); + if ( !ntk.is_pi( ni ) && !ntk.is_constant( ni ) ) + { + has_gate_fanin = true; + return false; // break + } + return true; // next fanin + } ); + if ( has_gate_fanin ) + { + return std::make_pair( n, ni ); + } + } + } + +private: + testcase_minimizer_params const ps; + std::string file_extension; + Ntk ntk, ntk_backup, ntk_backup2; + + enum stages : int + { + pi = 1, // remove a PI (substitute with const0) + many_pos, + po, // remove a PO (substitute with const0) + const_gate, // substitute a gate with const0 + simplify_tfo, // create PO for all of a gate's fanins, then substitute it with const0 + mffc, // substitute a gate with a new PI + half_mffc, // substitute a gate with one of its fanin + single_gate // remove a single gate by creating POs for its fanins and substitute it with a new PI + } reducing_stage{ pi }; + uint32_t stage_counter{ 0u }; + uint32_t pos_to_remove{ 1000 }; + bool was_FIT{ true }; + std::set sampled; + + uint32_t init_PIs, init_POs, init_gates; +}; /* testcase_minimizer */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp new file mode 100644 index 0000000..a35ef2d --- /dev/null +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -0,0 +1,799 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_rewriting.hpp + \brief Window rewriting + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#include "../networks/aig.hpp" +#include "../networks/events.hpp" +#include "../networks/xag.hpp" +#include "../utils/debugging_utils.hpp" +#include "../utils/index_list.hpp" +#include "../utils/network_utils.hpp" +#include "../utils/node_map.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "detail/resub_utils.hpp" +#include "resyn_engines/xag_resyn.hpp" +#include "simulation.hpp" + +#include +#include +#include + +#pragma once + +namespace mockturtle +{ + +struct window_rewriting_params +{ + uint64_t cut_size{ 6 }; + uint64_t num_levels{ 5 }; + + /* Level information guides the windowing construction and as such impacts QoR: + -- dont_update: fastest, but levels are wrong (QoR degrades) + -- eager: fast, some levels are wrong + -- precise: fast, all levels are correct (best QoR) + -- recompute: slow, same as precise (used only for debugging) + */ + enum + { + /* do not update any levels */ + dont_update, + /* eagerly update the levels of changed nodes but avoid + topological sorting (some levels will be wrong) */ + eager, + /* precisely update the levels of changed nodes bottom-to-top and + in topological order */ + precise, + /* recompute all levels (also precise, but more expensive to + compute) */ + recompute, + } level_update_strategy = dont_update; + + uint64_t max_num_divs{ 100 }; + + bool filter_cyclic_substitutions{ false }; +}; /* window_rewriting_params */ + +struct window_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Time for constructing windows. */ + stopwatch<>::duration time_window{ 0 }; + + /*! \brief Time for optimizing windows. */ + stopwatch<>::duration time_optimize{ 0 }; + + /*! \brief Time for substituting. */ + stopwatch<>::duration time_substitute{ 0 }; + + /*! \brief Time for updating level information. */ + stopwatch<>::duration time_levels{ 0 }; + + /*! \brief Time for topological sorting. */ + stopwatch<>::duration time_topo_sort{ 0 }; + + /*! \brief Time for encoding index_list. */ + stopwatch<>::duration time_encode{ 0 }; + + /*! \brief Time for computing dependency circuit. */ + stopwatch<>::duration time_resyn{ 0 }; + + /*! \brief Time for simulation. */ + stopwatch<>::duration time_simulate{ 0 }; + + /*! \brief Time for marking TFO and MFFC. */ + stopwatch<>::duration time_mark{ 0 }; + + /*! \brief Time for adding divisor truth tables. */ + stopwatch<>::duration time_add_divisor{ 0 }; + + /*! \brief Time for substitution within windows. */ + stopwatch<>::duration time_window_substitute{ 0 }; + + /*! \brief Time for constructing fanout_view within windows. */ + stopwatch<>::duration time_fanout_view{ 0 }; + + /*! \brief Time for detecting cycles. */ + stopwatch<>::duration time_cycle{ 0 }; + + /*! \brief Total number of calls to the resub. engine. */ + uint64_t num_resyn_invokes{ 0 }; + uint64_t num_substitutions{ 0 }; + uint64_t num_restrashes{ 0 }; + uint64_t num_windows{ 0 }; + uint64_t gain{ 0 }; + + window_rewriting_stats operator+=( window_rewriting_stats const& other ) + { + time_total += other.time_total; + time_window += other.time_window; + time_optimize += other.time_optimize; + time_substitute += other.time_substitute; + time_levels += other.time_levels; + time_topo_sort += other.time_topo_sort; + time_encode += other.time_encode; + time_resyn += other.time_resyn; + time_simulate += other.time_simulate; + time_mark += other.time_mark; + time_add_divisor += other.time_add_divisor; + time_window_substitute += other.time_window_substitute; + time_fanout_view += other.time_fanout_view; + num_substitutions += other.num_substitutions; + num_restrashes += other.num_restrashes; + num_windows += other.num_windows; + num_resyn_invokes += other.num_resyn_invokes; + gain += other.gain; + return *this; + } + + void report() const + { + stopwatch<>::duration time_other = + time_total - time_window - time_topo_sort - time_optimize - time_substitute - time_levels; + + fmt::print( "===========================================================================\n" ); + fmt::print( "[i] Windowing = {:7.2f} ({:5.2f}%) (#win = {})\n", + to_seconds( time_window ), to_seconds( time_window ) / to_seconds( time_total ) * 100, num_windows ); + fmt::print( "[i] Top.sort = {:7.2f} ({:5.2f}%)\n", to_seconds( time_topo_sort ), to_seconds( time_topo_sort ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Enc.list = {:7.2f} ({:5.2f}%)\n", to_seconds( time_encode ), to_seconds( time_encode ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Optimize = {:7.2f} ({:5.2f}%) (#invokes = {}, #resubs = {}, est. gain = {})\n", + to_seconds( time_optimize ), to_seconds( time_optimize ) / to_seconds( time_total ) * 100, num_resyn_invokes, num_substitutions, gain ); + fmt::print( "[i] >> resynthesis = {:7.2f} ({:5.2f}%)\n", to_seconds( time_resyn ), to_seconds( time_resyn ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> simulate = {:7.2f} ({:5.2f}%)\n", to_seconds( time_simulate ), to_seconds( time_simulate ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> marking = {:7.2f} ({:5.2f}%)\n", to_seconds( time_mark ), to_seconds( time_mark ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> add div. = {:7.2f} ({:5.2f}%)\n", to_seconds( time_add_divisor ), to_seconds( time_add_divisor ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> substitute = {:7.2f} ({:5.2f}%)\n", to_seconds( time_window_substitute ), to_seconds( time_window_substitute ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] >> fanout_view = {:7.2f} ({:5.2f}%)\n", to_seconds( time_fanout_view ), to_seconds( time_fanout_view ) / to_seconds( time_optimize ) * 100 ); + fmt::print( "[i] Substitute = {:7.2f} ({:5.2f}%) (#hash upd. = {})\n", + to_seconds( time_substitute ), + to_seconds( time_substitute ) / to_seconds( time_total ) * 100, + num_restrashes ); + fmt::print( "[i] Upd.levels = {:7.2f} ({:5.2f}%)\n", to_seconds( time_levels ), to_seconds( time_levels ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Other = {:7.2f} ({:5.2f}%)\n", to_seconds( time_other ), to_seconds( time_other ) / to_seconds( time_total ) * 100 ); + fmt::print( "---------------------------------------------------------------------------\n" ); + fmt::print( "[i] TOTAL = {:7.2f}\n", to_seconds( time_total ) ); + fmt::print( "===========================================================================\n" ); + } +}; /* window_rewriting_stats */ + +namespace detail +{ + +template +bool is_contained_in_tfi_recursive( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + if ( ntk.color( node ) == ntk.current_color() ) + { + return false; + } + ntk.paint( node ); + + if ( n == node ) + { + return true; + } + + bool found = false; + ntk.foreach_fanin( node, [&]( typename Ntk::signal const& fi ) { + if ( is_contained_in_tfi_recursive( ntk, ntk.get_node( fi ), n ) ) + { + found = true; + return false; + } + return true; + } ); + + return found; +} + +} /* namespace detail */ + +template +bool is_contained_in_tfi( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + /* do not even build the TFI, but just search for the node */ + ntk.new_color(); + return detail::is_contained_in_tfi_recursive( ntk, node, n ); +} + +namespace detail +{ + +template +struct resyn_sparams : public xag_resyn_static_params +{ + using truth_table_storage_type = node_map; + using node_type = typename NtkWin::signal; + static constexpr bool use_xor = false; +}; + +template>> +class window_rewriting_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit window_rewriting_impl( Ntk& ntk, window_rewriting_params const& ps, window_rewriting_stats& st ) + : ntk( ntk ), ps( ps ), st( st ) + /* initialize levels to network depth */ + , + levels( ntk.depth() ), engine( engine_st ) + { + register_events(); + } + + ~window_rewriting_impl() + { + ntk.events().release_add_event( add_event ); + ntk.events().release_modified_event( modified_event ); + ntk.events().release_delete_event( delete_event ); + } + + void run() + { + stopwatch t( st.time_total ); + + if constexpr ( std::is_same_v ) + { + sim = new default_simulator( ps.cut_size ); + } + else + { + sim = new default_simulator(); + } + + create_window_impl windowing( ntk ); + uint32_t const size = ntk.size(); + for ( uint32_t n = 0u; n < size; ++n ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) || ntk.is_dead( n ) ) + { + continue; + } + + if ( auto w = call_with_stopwatch( st.time_window, [&]() { return windowing.run( n, ps.cut_size, ps.num_levels ); } ) ) + { + ++st.num_windows; + + NtkWin win; + call_with_stopwatch( st.time_encode, [&]() { + clone_subnetwork( ntk, w->inputs, w->outputs, w->nodes, win ); + } ); + + if ( !optimize( win ) ) + { + continue; + } + + std::vector signals; + for ( auto const& i : w->inputs ) + { + signals.push_back( ntk.make_signal( i ) ); + } + + uint32_t counter{ 0 }; + ++st.num_substitutions; + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + std::list> substitutions; + insert_ntk( ntk, std::begin( signals ), std::end( signals ), win, + [&]( signal const& _new ) { + assert( !ntk.is_dead( ntk.get_node( _new ) ) ); + auto const _old = w->outputs.at( counter++ ); + if ( _old == _new ) + { + return true; + } + + /* ensure that _old is not in the TFI of _new */ + // assert( !is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ); + if ( ps.filter_cyclic_substitutions && + call_with_stopwatch( st.time_window, [&]() { return is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ); } ) ) + { + std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); + for ( auto it = std::rbegin( substitutions ); it != std::rend( substitutions ); ++it ) + { + if ( ntk.fanout_size( ntk.get_node( it->second ) ) == 0u ) + { + ntk.take_out_node( ntk.get_node( it->second ) ); + } + } + substitutions.clear(); + return false; + } + + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); + return true; + } ); + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + substitute_nodes( substitutions ); + + /* recompute levels and depth */ + if ( ps.level_update_strategy == window_rewriting_params::recompute ) + { + call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); + } + if ( ps.level_update_strategy != window_rewriting_params::dont_update ) + { + update_depth(); + } + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + /* ensure that the network structure is still acyclic */ + assert( network_is_acyclic( ntk ) ); + + if ( ps.level_update_strategy == window_rewriting_params::precise || + ps.level_update_strategy == window_rewriting_params::recompute ) + { + /* ensure that the levels and depth is correct */ + assert( check_network_levels( ntk ) ); + } + + /* update internal data structures in windowing */ + windowing.resize( ntk.size() ); + } + } + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + delete sim; + } + +private: + void register_events() + { + auto const update_level_of_new_node = [&]( const auto& n ) { + stopwatch t( st.time_total ); + update_levels( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + stopwatch t( st.time_total ); + update_levels( n ); + }; + + auto const update_level_of_deleted_node = [&]( node const& n ) { + stopwatch t( st.time_total ); + assert( ntk.fanout_size( n ) == 0u ); + assert( ntk.is_dead( n ) ); + ntk.set_level( n, -1 ); + }; + + add_event = ntk.events().register_add_event( update_level_of_new_node ); + modified_event = ntk.events().register_modified_event( update_level_of_existing_node ); + delete_event = ntk.events().register_delete_event( update_level_of_deleted_node ); + } + + bool optimize( NtkWin& win ) + { + stopwatch t( st.time_optimize ); + bool changed = false; + + node_map tts = call_with_stopwatch( st.time_simulate, [&]() { + return simulate_nodes( win, *sim ); + } ); + auto win_add_event = win.events().register_add_event( [&]( auto const& n ) { + call_with_stopwatch( st.time_simulate, [&]() { + tts.resize(); + std::vector fanin_values( win.fanin_size( n ) ); + win.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin_values[i] = tts[f]; + } ); + tts[n] = win.compute( n, fanin_values.begin(), fanin_values.end() ); + } ); + } ); + fanout_view fanout_win = make_with_stopwatch, NtkWin&>( st.time_fanout_view, win ); + // fanout_view fanout_win{win}; + + win.foreach_po( [&]( auto const& f ) { + auto root = win.get_node( f ); + if ( win.value( root ) != 1 ) + { + win.set_value( root, 1 ); + changed |= optimize_node( win, fanout_win, tts, root ); + } + } ); + + win.foreach_gate( [&]( auto const& root ) { + if ( win.value( root ) != 1 ) + { + win.set_value( root, 1 ); + bool all_fanin_is_pi = true; + win.foreach_fanin( root, [&]( auto const& fi ) { + if ( !win.is_pi( win.get_node( fi ) ) ) + { + all_fanin_is_pi = false; + } + } ); + if ( !all_fanin_is_pi ) + changed |= optimize_node( win, fanout_win, tts, root ); + } + } ); + + win.events().release_add_event( win_add_event ); + return changed; + } + + bool optimize_node( NtkWin& win, fanout_view& fanout_win, node_map& tts, typename NtkWin::node const& root ) + { + st.num_resyn_invokes++; + + auto mffc_size = call_with_stopwatch( st.time_mark, [&]() { + /* mark MFFC */ + std::vector mffc; + node_mffc_inside mffc_mgr( win ); + auto mffc_size = mffc_mgr.run( root, {}, mffc ); + win.incr_trav_id(); + for ( auto const& n : mffc ) + { + win.set_visited( n, win.trav_id() ); + } + /* mark TFO */ + mark_tfo( fanout_win, root ); + + /* exclude constant node */ + if constexpr ( std::is_same_v || std::is_same_v ) + { + win.set_visited( win.get_node( win.get_constant( false ) ), win.trav_id() ); + } + return mffc_size; + } ); + + /* add divisors (all nodes in the window except TFO and MFFC) */ + std::vector divs; + call_with_stopwatch( st.time_add_divisor, [&]() { + win.foreach_node( [&]( auto const& n ) { + if ( win.visited( n ) != win.trav_id() ) + { + divs.emplace_back( win.make_signal( n ) ); + if ( divs.size() > ps.max_num_divs ) + { + return false; + } + } + return true; + } ); + } ); + + /* run resynthesis */ + auto const il = call_with_stopwatch( st.time_resyn, [&]() { + return engine( tts[root], ~tts[win.get_constant( false )], divs.begin(), divs.end(), tts, mffc_size - 1 ); + } ); + if ( il ) + { + st.gain += mffc_size - il->num_gates(); + call_with_stopwatch( st.time_window_substitute, [&]() { + insert( win, divs.begin(), divs.end(), *il, [&]( auto const& s ) { + win.substitute_node( root, s ); + } ); + } ); + return true; + } + return false; + } + + void mark_tfo( fanout_view& fanout_win, typename NtkWin::node const& n ) + { + fanout_win.set_visited( n, fanout_win.trav_id() ); + fanout_win.foreach_fanout( n, [&]( auto const& fo ) { + if ( fanout_win.visited( fo ) != fanout_win.trav_id() ) + { + mark_tfo( fanout_win, fo ); + } + } ); + } + +private: + void substitute_nodes( std::list> substitutions ) + { + stopwatch t( st.time_substitute ); + + auto clean_substitutions = [&]( node const& n ) { + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ) { + if ( s.first == n ) + { + node const nn = ntk.get_node( s.second ); + if ( ntk.is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( ntk.fanout_size( nn ) > 0 ) + { + ntk.decr_fanout_size( nn ); + } + /* remove the node if its fanout_size becomes 0 */ + if ( ntk.fanout_size( nn ) == 0 ) + { + ntk.take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; + + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + auto clean_subs_event = ntk.events().register_delete_event( clean_substitutions ); + + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + ntk.incr_fanout_size( ntk.get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + for ( auto index : ntk.fanout( old_node ) ) + { + /* skip CIs and dead nodes */ + if ( ntk.is_dead( index ) ) + { + continue; + } + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ) { return s.first == index; } ) != std::end( substitutions ) ) + { + continue; + } + + /* replace in node */ + if ( const auto repl = ntk.replace_in_node( index, old_node, new_signal ); repl ) + { + ntk.incr_fanout_size( ntk.get_node( repl->second ) ); + substitutions.emplace_back( *repl ); + ++st.num_restrashes; + } + } + + /* replace in outputs */ + ntk.replace_in_outputs( old_node, new_signal ); + + /* replace in substitutions */ + for ( auto& s : substitutions ) + { + if ( ntk.get_node( s.second ) == old_node ) + { + s.second = ntk.is_complemented( s.second ) ? !new_signal : new_signal; + ntk.incr_fanout_size( ntk.get_node( new_signal ) ); + } + } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !ntk.is_dead( old_node ) ); + ntk.take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + ntk.decr_fanout_size( ntk.get_node( new_signal ) ); + if ( ntk.fanout_size( ntk.get_node( new_signal ) ) == 0 ) + { + ntk.take_out_node( ntk.get_node( new_signal ) ); + } + } + + ntk.events().release_delete_event( clean_subs_event ); + } + + void update_levels( node const& n ) + { + ntk.resize_levels(); + if ( ps.level_update_strategy == window_rewriting_params::precise ) + { + call_with_stopwatch( st.time_levels, [&]() { update_node_level_precise( n ); } ); + } + else if ( ps.level_update_strategy == window_rewriting_params::eager ) + { + call_with_stopwatch( st.time_levels, [&]() { update_node_level_eager( n ); } ); + } + + /* levels can be wrong until substitute_nodes has finished */ + // assert( check_network_levels( ntk ) ); + } + + /* precisely update node levels using an iterative topological sorting approach */ + void update_node_level_precise( node const& n ) + { + assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); + // assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); + + /* compute level of current node */ + uint32_t level_offset{ 0 }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + level_offset = std::max( ntk.level( ntk.get_node( fi ) ), level_offset ); + } ); + ++level_offset; + + /* add node into levels */ + if ( levels.size() < 1u ) + { + levels.resize( 1u ); + } + levels[0].emplace_back( n ); + + for ( uint32_t level_index = 0u; level_index < levels.size(); ++level_index ) + { + if ( levels[level_index].empty() ) + continue; + + for ( uint32_t node_index = 0u; node_index < levels[level_index].size(); ++node_index ) + { + node const p = levels[level_index][node_index]; + + /* recompute level of this node */ + uint32_t lvl{ 0 }; + ntk.foreach_fanin( p, [&]( signal const& fi ) { + if ( ntk.is_dead( ntk.get_node( fi ) ) ) + return; + + lvl = std::max( ntk.level( ntk.get_node( fi ) ), lvl ); + return; + } ); + ++lvl; + assert( lvl > 0 ); + + /* update level and add fanouts to levels[.] if the recomputed + level is different from the current level */ + if ( lvl != ntk.level( p ) ) + { + ntk.set_level( p, lvl ); + ntk.foreach_fanout( p, [&]( node const& fo ) { + assert( std::max( ntk.level( fo ), lvl + 1 ) >= level_offset ); + uint32_t const pos = std::max( ntk.level( fo ), lvl + 1 ) - level_offset; + assert( pos >= 0u ); + assert( pos >= level_index ); + if ( levels.size() <= pos ) + { + levels.resize( std::max( uint32_t( levels.size() << 1 ), pos + 1 ) ); + } + levels[pos].emplace_back( fo ); + } ); + } + } + + /* clean the level */ + levels[level_index].clear(); + } + levels.clear(); + } + + /* eagerly update the node levels without topologically sorting (may + stack-overflow if the network is deep)*/ + void update_node_level_eager( node const& n ) + { + uint32_t const curr_level = ntk.level( n ); + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + ntk.foreach_fanout( n, [&]( const auto& p ) { + if ( !ntk.is_dead( p ) ) + { + update_node_level_eager( p ); + } + } ); + } + } + + /* update network depth (needs level information!) */ + void update_depth() + { + stopwatch t( st.time_levels ); + + uint32_t max_level{ 0 }; + ntk.foreach_co( [&]( signal const& s ) { + assert( !ntk.is_dead( ntk.get_node( s ) ) ); + max_level = std::max( ntk.level( ntk.get_node( s ) ), max_level ); + } ); + + if ( ntk.depth() != max_level ) + { + ntk.set_depth( max_level ); + } + } + +private: + Ntk& ntk; + window_rewriting_params ps; + window_rewriting_stats& st; + + std::vector> levels; + + /* events */ + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; + + default_simulator* sim; + typename ResynEngine::stats engine_st; + ResynEngine engine; +}; /* window_rewriting_impl */ + +} /* namespace detail */ + +template +void window_rewriting( Ntk& ntk, window_rewriting_params const& ps = {}, window_rewriting_stats* pst = nullptr ) +{ + fanout_view fntk{ ntk }; + depth_view dntk{ fntk }; + color_view cntk{ dntk }; + + window_rewriting_stats st; + using NtkWin = typename Ntk::base_type; + using TT = kitty::dynamic_truth_table; + detail::window_rewriting_impl( cntk, ps, st ).run(); + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp b/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp new file mode 100644 index 0000000..358ea75 --- /dev/null +++ b/include/mockturtle/algorithms/xag_algebraic_rewriting.hpp @@ -0,0 +1,565 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_algebraic_rewriting.hpp + \brief xag algebraric rewriting + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../views/fanout_view.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xag_algebraic_depth_rewriting. + * + * The data structure `xag_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `xag_algebraic_depth_rewriting`. + */ +struct xag_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; + + /*! \brief Try rules that are rarely applied. */ + bool allow_rare_rules{ false }; +}; + +namespace detail +{ + +template +class xag_algebraic_depth_rewriting_impl +{ +public: + xag_algebraic_depth_rewriting_impl( Ntk& ntk, xag_algebraic_depth_rewriting_params const& ps ) + : ntk( ntk ), ps( ps ) + { + } + + void run() + { + switch ( ps.strategy ) + { + case xag_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case xag_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case xag_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [&]( auto n ) { + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + if ( res ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + bool res = reduce_depth_and_associativity( n ); + res |= reduce_depth_xor_associativity( n ); + + if ( ps.allow_area_increase && !res ) + { + reduce_depth_and_or_distributivity( n ); + + reduce_depth_and_xor_distributivity( n ); + } + + if ( ps.allow_rare_rules && !res ) + res = reduce_depth_and_distributity( n ); + + if ( !res ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + /* AND associativity */ + bool reduce_depth_and_associativity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || ntk.is_complemented( ocs[1] ) ) + return false; + + /* depth of second child must be (significantly) higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) + return false; + + /* get children of second child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + auto opt = ntk.create_and( ocs1[1], ntk.create_and( ocs[0], ocs1[0] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* XOR associativity */ + bool reduce_depth_xor_associativity( node const& n ) + { + if ( !ntk.is_xor( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor( ntk.get_node( ocs[1] ) ) ) + return false; + + /* depth of second child must be (significantly) higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + auto opt = ntk.create_xor( ocs1[1], ntk.create_xor( ocs[0], ocs1[0] ) ); + + if ( ntk.is_complemented( ocs[1] ) ) + opt = !opt; + + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND distributivity */ + bool reduce_depth_and_distributity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[0] ) ) || !ntk.is_complemented( ocs[0] ) ) + return false; + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || !ntk.is_complemented( ocs[1] ) ) + return false; + + /* children must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) != 1 || ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) ) + return false; + + /* get children of first child */ + auto ocs0 = ordered_children( ntk.get_node( ocs[0] ) ); + + /* get children of second child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + /* find common support */ + bool critical_common = false; + signal common, x, y; + if ( ocs0[0] == ocs1[0] ) + { + common = ocs0[0]; + x = ocs0[1]; + y = ocs1[1]; + } + else if ( ocs0[0] == ocs1[1] ) + { + common = ocs0[0]; + x = ocs0[1]; + y = ocs1[0]; + } + else if ( ocs0[1] == ocs1[0] ) + { + common = ocs0[1]; + x = ocs0[0]; + y = ocs1[1]; + } + else if ( ocs0[1] == ocs1[1] ) + { + common = ocs0[1]; + x = ocs0[0]; + y = ocs1[0]; + critical_common = true; + } + else + { + return false; + } + + /* common signal is not critical, children must have single fanout, to not increase the area */ + if ( !critical_common && ( ntk.fanout_size( ntk.get_node( ocs[0] ) ) != 1 || ntk.fanout_size( ntk.get_node( ocs[1] ) ) != 1 ) ) + return false; + + auto opt = !ntk.create_and( common, !ntk.create_and( !x, !y ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND-OR distributivity */ + bool reduce_depth_and_or_distributivity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) < 3 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_and( ntk.get_node( ocs[1] ) ) || !ntk.is_complemented( ocs[1] ) ) + return false; + + /* depth of second child must be significantly higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 2 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + if ( !ntk.is_and( ntk.get_node( ocs1[1] ) ) || !ntk.is_complemented( ocs1[1] ) ) + return false; + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + /* get children of last grand-child */ + auto ocs11 = ordered_children( ntk.get_node( ocs1[1] ) ); + + /* depth of second grand-grand-child must be higher than depth of first grand-grand-child */ + if ( ntk.level( ntk.get_node( ocs11[1] ) ) == ntk.level( ntk.get_node( ocs11[0] ) ) ) + return false; + + auto opt = !ntk.create_and( !ntk.create_and( ocs[0], !ocs1[0] ), !ntk.create_and( ntk.create_and( ocs[0], ocs11[0] ), ocs11[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* AND-XOR distributivity */ + bool reduce_depth_and_xor_distributivity( node const& n ) + { + if ( !ntk.is_and( n ) ) + return false; + + if ( ntk.level( n ) < 3 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor( ntk.get_node( ocs[1] ) ) ) + return false; + + /* depth of second child must be significantly higher than depth of first child */ + if ( ntk.level( ntk.get_node( ocs[1] ) ) <= ntk.level( ntk.get_node( ocs[0] ) ) + 2 ) + return false; + + /* get children of last child */ + auto ocs1 = ordered_children( ntk.get_node( ocs[1] ) ); + + if ( !ntk.is_and( ntk.get_node( ocs1[1] ) ) ) + return false; + + /* depth of second grand-child must be higher than depth of first grand-child */ + if ( ntk.level( ntk.get_node( ocs1[1] ) ) == ntk.level( ntk.get_node( ocs1[0] ) ) ) + return false; + + /* get children of last grand-child */ + auto ocs11 = ordered_children( ntk.get_node( ocs1[1] ) ); + + /* depth of second grand-grand-child must be higher than depth of first grand-grand-child */ + if ( ntk.level( ntk.get_node( ocs11[1] ) ) == ntk.level( ntk.get_node( ocs11[0] ) ) ) + return false; + + /* if XOR is complemented, complement first grand-child */ + if ( ntk.is_complemented( ocs[1] ) != ntk.is_complemented( ocs1[1] ) ) + { + ocs1[0] = !ocs1[0]; + } + + auto opt = ntk.create_xor( ntk.create_and( ocs[0], ocs1[0] ), ntk.create_and( ntk.create_and( ocs[0], ocs11[0] ), ocs11[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + inline std::array, 2> ordered_children( node const& n ) const + { + std::array, 2> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { + children[i] = f; + } ); + if ( ntk.level( ntk.get_node( children[0] ) ) > ntk.level( ntk.get_node( children[1] ) ) ) + { + std::swap( children[0], children[1] ); + } + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + xag_algebraic_depth_rewriting_params const& ps; +}; + +} // namespace detail + +/*! \brief XAG algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with AND/XOR gates for depth + * optimization using the associativity and distributivity rule in + * AND-XOR logic. It can be applied to networks other than XAGs, but + * only considers pairs of nodes which both implement the AND + * function and the XOR function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_and` + * - `create_xor` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_and` + * - `is_xor` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + \endverbatim + */ +template +void xag_algebraic_depth_rewriting( Ntk& ntk, xag_algebraic_depth_rewriting_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_and_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xag_algebraic_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/xag_balancing.hpp b/include/mockturtle/algorithms/xag_balancing.hpp new file mode 100644 index 0000000..e3e6cfc --- /dev/null +++ b/include/mockturtle/algorithms/xag_balancing.hpp @@ -0,0 +1,729 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_balancing.hpp + \brief Balances the XAG to reduce the depth + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +namespace mockturtle +{ + +struct xag_balancing_params +{ + /*! \brief Minimizes the number of levels. */ + bool minimize_levels{ true }; + + /*! \brief Use fast version, it may not find some area optimizations. */ + bool fast_mode{ true }; +}; + +namespace detail +{ + +template +class xag_balance_impl +{ +public: + static constexpr size_t storage_init_size = 30; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using storage_t = std::vector>; + +public: + xag_balance_impl( Ntk& ntk, xag_balancing_params const& ps ) + : ntk( ntk ), ps( ps ), storage( storage_init_size ) + { + } + + void run() + { + ntk.clear_values(); + + for ( auto i = 0; i < storage_init_size; ++i ) + storage[i].reserve( 10 ); + + /* balance every CO */ + ntk.foreach_co( [&]( auto const& f ) { + balance_rec( ntk.get_node( f ), 0 ); + } ); + } + +private: + signal balance_rec( node const& n, uint32_t level ) + { + if ( ntk.is_ci( n ) ) + return ntk.make_signal( n ); + + /* node has been replaced in a previous recursion */ + if ( ntk.is_dead( n ) || ntk.value( n ) > 0 ) + { + return ntk.make_signal( find_substituted_node( n ) ); + } + + if ( level >= storage.size() ) + { + storage.emplace_back( std::vector() ); + storage.back().reserve( 10 ); + } + + /* collect leaves of the AND or XOR trees */ + bool polarity = false; + bool is_and = true; + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_and( n ) ) + { + collect_leaves_and( n, storage[level] ); + } + else + { + polarity = collect_leaves_xor( n, storage[level] ); + is_and = false; + } + } + else + { + collect_leaves_and( n, storage[level] ); + } + + if ( storage[level].size() == 0 ) + { + ntk.substitute_node( n, ntk.get_constant( polarity ) ); + return ntk.get_constant( polarity ); + } + + /* recur over the leaves */ + for ( auto& f : storage[level] ) + { + signal new_signal = balance_rec( ntk.get_node( f ), level + 1 ); + f = new_signal ^ ntk.is_complemented( f ); + } + + assert( storage[level].size() > 1 ); + + /* sort by decreasing level */ + std::stable_sort( storage[level].begin(), storage[level].end(), [this]( auto const& a, auto const& b ) { + return ntk.level( ntk.get_node( a ) ) > ntk.level( ntk.get_node( b ) ); + } ); + + /* mark TFI cone of n */ + ntk.incr_trav_id(); + mark_tfi( ntk.make_signal( n ), true ); + + /* generate the AND or XOR tree */ + if ( is_and ) + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.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 + { + if ( ps.minimize_levels ) + pick_nodes_and( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area( storage[level] ); + } + + /* pop the two selected nodes to create the new AND gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_and( child1, child2 ); + + /* update level for AND node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_and( storage[level], new_sig ); + } + } + else + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.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 + { + if ( ps.minimize_levels ) + pick_nodes_xor( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area( storage[level] ); + } + + /* pop the two selected nodes to create the new XOR gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_xor( child1, child2 ); + + /* update level for XOR node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_xor( storage[level], new_sig, polarity ); + } + } + + signal root = storage[level][0] ^ polarity; + + /* replace if new */ + if ( n != ntk.get_node( root ) ) + { + ntk.substitute_node_no_restrash( n, root ); + } + + /* remember the substitution and the new node as already balanced */ + ntk.set_value( n, ntk.node_to_index( ntk.get_node( root ) ) ); + ntk.set_value( ntk.get_node( root ), ntk.node_to_index( ntk.get_node( root ) ) ); + + /* clean leaves storage */ + storage[level].clear(); + + return root; + } + + void collect_leaves_and( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_and_rec( ntk.make_signal( n ), leaves, true ); + + /* check for constant false */ + if ( ret < 0 ) + { + leaves.clear(); + } + } + + int collect_leaves_and_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + for ( signal const& s : leaves ) + { + if ( ntk.get_node( s ) != n ) + continue; + + if ( s == f ) + return 1; /* same polarity: duplicate */ + else + return -1; /* opposite polarity: const0 */ + } + + return 0; + } + + /* set as leaf if signal is complemented or is a XOR or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_xor( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret |= collect_leaves_and_rec( child, leaves, false ); + } ); + + return ret; + } + + bool collect_leaves_xor( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_xor_rec( ntk.make_signal( n ), leaves, true ); + + /* return top polarity */ + return ret ? true : false; + } + + int collect_leaves_xor_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) != n ) + { + ++it; + continue; + } + + /* remove node (XOR property) */ + if ( ntk.get_node( *it ) == n ) + leaves.erase( it ); + + return 0; + } + + return 0; + } + + /* set as leaf if signal is an AND or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_and( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ^ ntk.is_complemented( f ) ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret ^= ntk.is_complemented( child ) ? 1 : 0; + ret ^= collect_leaves_xor_rec( child, leaves, false ); + } ); + + return ret; + } + + size_t find_left_most_at_level( std::vector const& leaves ) + { + size_t pointer = leaves.size() - 1; + uint32_t current_level = ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ); + + while ( pointer > 0 ) + { + if ( ntk.level( ntk.get_node( leaves[pointer - 1] ) ) > current_level ) + break; + + --pointer; + } + + assert( ntk.level( ntk.get_node( leaves[pointer] ) ) == current_level ); + return pointer; + } + + inline void pick_nodes_and( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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 ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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 ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + 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 ); + + /* check uniqueness */ + for ( auto const& s : leaves ) + { + if ( s == f ) + return; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void insert_node_sorted_xor( std::vector& leaves, signal const& f, bool& polarity ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) == ntk.get_node( f ) ) + { + polarity ^= ntk.is_complemented( f ) ^ ntk.is_complemented( *it ); + leaves.erase( it ); + return; + } + + ++it; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void update_level( node const& n ) + { + uint32_t l = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + l = std::max( l, ntk.level( ntk.get_node( f ) ) ); + } ); + + ntk.set_level( n, l + 1 ); + } + + node find_substituted_node( node n ) + { + while ( ntk.is_dead( n ) ) + n = ntk.index_to_node( ntk.value( n ) ); + + return n; + } + + void mark_tfi( signal const& f, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( auto const& child ) { + mark_tfi( child, false ); + } ); + } + +private: + Ntk& ntk; + xag_balancing_params const& ps; + + storage_t storage; +}; + +} /* namespace detail */ + +/*! \brief XAG balancing. + * + * This method balance the XAG to reduce the + * depth. Level minimization can be turned off. + * In this case, balancing tries to reconstruct + * AND and XOR trees such that logic sharing is maximized. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_and` + */ +template +void xag_balance( Ntk& ntk, xag_balancing_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_clone_node_v, "Ntk does not implement the clone_node method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_has_and_v, "Ntk does not implement the has_and method" ); + static_assert( has_has_xor_v, "Ntk does not implement the has_xor method" ); + + fanout_view f_ntk{ ntk }; + depth_view> d_ntk{ f_ntk }; + + detail::xag_balance_impl p( d_ntk, ps ); + p.run(); + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/xag_optimization.hpp b/include/mockturtle/algorithms/xag_optimization.hpp new file mode 100644 index 0000000..d07ac24 --- /dev/null +++ b/include/mockturtle/algorithms/xag_optimization.hpp @@ -0,0 +1,273 @@ +/* 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 xag_optimization.hpp + \brief Various XAG optimization algorithms + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken + \author Zhufei Chu +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../algorithms/extract_linear.hpp" +#include "../algorithms/linear_resynthesis.hpp" +#include "../io/write_verilog.hpp" +#include "../networks/xag.hpp" +#include "../properties/mccost.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "dont_cares.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +class xag_constant_fanin_optimization_impl +{ +public: + xag_constant_fanin_optimization_impl( xag_network const& xag ) + : xag( xag ) + { + } + + xag_network run() + { + xag_network dest; + + node_map old2new( xag ); + node_map, xag_network> lfi( xag ); + + old2new[xag.get_node( xag.get_constant( false ) )] = dest.get_constant( false ); + if ( xag.get_node( xag.get_constant( true ) ) != xag.get_node( xag.get_constant( false ) ) ) + { + old2new[xag.get_node( xag.get_constant( true ) )] = dest.get_constant( true ); + } + xag.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + lfi[n].emplace_back( n ); + } ); + topo_view topo{ xag }; + topo.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + if ( xag.is_xor( n ) ) + { + std::array children{}; + std::array*, 2> clfi{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + children[i] = &old2new[f]; + clfi[i] = &lfi[f]; + } ); + lfi[n] = merge( *clfi[0], *clfi[1] ); + if ( lfi[n].size() == 0 ) + { + old2new[n] = dest.get_constant( false ); + } + else if ( lfi[n].size() == 1 ) + { + old2new[n] = old2new[lfi[n].front()]; + } + else + { + old2new[n] = dest.create_xor( *children[0], *children[1] ); + } + } + else /* is AND */ + { + lfi[n].emplace_back( n ); + std::vector children; + xag.foreach_fanin( n, [&]( auto const& f ) { + children.push_back( old2new[f] ^ xag.is_complemented( f ) ); + } ); + old2new[n] = dest.create_and( children[0], children[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old2new[f] ^ xag.is_complemented( f ) ); + } ); + + return cleanup_dangling( dest ); + } + +private: + std::vector merge( std::vector const& s1, std::vector const& s2 ) const + { + std::vector s; + std::set_symmetric_difference( s1.cbegin(), s1.cend(), s2.cbegin(), s2.cend(), std::back_inserter( s ) ); + return s; + } + +private: + xag_network const& xag; +}; + +} // namespace detail + +/*! \brief Optimizes some AND gates by computing transitive linear fanin + * + * This function reevaluates the transitive linear fanin for each AND gate. + * This is a subnetwork composed of all immediate XOR gates in the transitive + * fanin cone until primary inputs or AND gates are reached. This linear + * transitive fanin might be constant for some fanin due to the cancellation + * property of the XOR operation. In such cases the AND gate can be replaced + * by a constant or a fanin. + */ +inline xag_network xag_constant_fanin_optimization( xag_network const& xag ) +{ + return detail::xag_constant_fanin_optimization_impl( xag ).run(); +} + +/*! \brief Optimizes some AND gates using satisfiability don't cares + * + * If an AND gate is satisfiability don't care for assignment 00, it can be + * replaced by an XNOR gate, therefore reducing the multiplicative complexity. + */ +inline xag_network xag_dont_cares_optimization( xag_network const& xag ) +{ + node_map old_to_new( xag ); + + xag_network dest; + old_to_new[xag.get_constant( false )] = dest.get_constant( false ); + + xag.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + satisfiability_dont_cares_checker checker( xag ); + + topo_view{ xag }.foreach_node( [&]( auto const& n ) { + if ( xag.is_constant( n ) || xag.is_pi( n ) ) + return; + + std::array fanin{}; + xag.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin[i] = old_to_new[f] ^ xag.is_complemented( f ); + } ); + + if ( xag.is_and( n ) ) + { + if ( checker.is_dont_care( n, { false, false } ) ) + { + old_to_new[n] = dest.create_xnor( fanin[0], fanin[1] ); + } + else + { + old_to_new[n] = dest.create_and( fanin[0], fanin[1] ); + } + } + else /* is XOR */ + { + old_to_new[n] = dest.create_xor( fanin[0], fanin[1] ); + } + } ); + + xag.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xag.is_complemented( f ) ); + } ); + + return dest; +} + +/*! \brief Optimizes XOR gates by linear network resynthesis + * + * See `exact_linear_resynthesis_optimization` for an example implementation + * of this function. + */ +inline xag_network linear_resynthesis_optimization( xag_network const& xag, std::function linear_resyn, std::function const& )> const& on_ignore_inputs = {} ) +{ + const auto num_ands = *multiplicative_complexity( xag ); + if ( num_ands == 0u ) + { + return linear_resyn( xag ); + } + + const auto linear = extract_linear_circuit( xag ).first; + + /* ignore inputs (if linear resynthesis is not cancellation-free) */ + on_ignore_inputs( {} ); + for ( auto i = 0u; i < num_ands; ++i ) + { + std::vector ignore( num_ands - i ); + std::iota( ignore.begin(), ignore.end(), xag.num_pis() + i ); + on_ignore_inputs( ignore ); + on_ignore_inputs( ignore ); + } + + const auto linear_optimized = linear_resyn( linear ); + + assert( linear.num_pis() == linear_optimized.num_pis() ); + assert( linear.num_pos() == linear_optimized.num_pos() ); + assert( linear.num_pis() == xag.num_pis() + num_ands ); + assert( linear.num_pos() == 1 + 2 * num_ands ); + + return merge_linear_circuit( linear_optimized, num_ands ); +} + +/*! \brief Optimizes XOR gates by exact linear network resynthesis + */ +template +inline xag_network exact_linear_resynthesis_optimization( xag_network const& xag, uint32_t conflict_limit = 0u ) +{ + exact_linear_synthesis_params ps; + ps.conflict_limit = conflict_limit; + + const auto linear_resyn = [&]( xag_network const& linear ) { + if ( const auto optimized = exact_linear_resynthesis( linear, ps ); optimized ) + { + return *optimized; + } + else + { + return linear; + } + }; + + const auto on_ignore_inputs = [&]( std::vector const& ignore ) { + ps.ignore_inputs.push_back( ignore ); + }; + + return linear_resynthesis_optimization( xag, linear_resyn, on_ignore_inputs ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/xag_resub.hpp b/include/mockturtle/algorithms/xag_resub.hpp new file mode 100644 index 0000000..1a2774f --- /dev/null +++ b/include/mockturtle/algorithms/xag_resub.hpp @@ -0,0 +1,216 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2024 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_resub.hpp + \brief XAG-specific resubstitution rules + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../networks/xag.hpp" +#include "../utils/index_list.hpp" +#include "../utils/truth_table_utils.hpp" +#include "resubstitution.hpp" +#include "resyn_engines/xag_resyn.hpp" + +#include + +namespace mockturtle +{ + +struct xag_resyn_resub_stats +{ + /*! \brief Time for finding dependency function. */ + stopwatch<>::duration time_compute_function{ 0 }; + + /*! \brief Number of found solutions. */ + uint32_t num_success{ 0 }; + + /*! \brief Number of times that no solution can be found. */ + uint32_t num_fail{ 0 }; + + void report() const + { + fmt::print( "[i] \n" ); + fmt::print( "[i] #solution = {:6d}\n", num_success ); + fmt::print( "[i] #invoke = {:6d}\n", num_success + num_fail ); + fmt::print( "[i] engine time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + } +}; /* xag_resyn_resub_stats */ + +/*! \brief Interfacing resubstitution functor with XAG resynthesis engines for `window_based_resub_engine`. + */ +template>> +struct xag_resyn_functor +{ +public: + using node = xag_network::node; + using signal = xag_network::signal; + using stats = xag_resyn_resub_stats; + using TT = typename ResynEngine::truth_table_t; + + static_assert( std::is_same_v, "truth table type of the simulator does not match" ); + +public: + explicit xag_resyn_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), tts( ntk ), divs( divs ), st( st ) + { + assert( divs.size() == num_divs ); + (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)required; + TT target = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + TT care_transformed = target.construct(); + care_transformed = care; + + typename ResynEngine::stats st_eng; + ResynEngine engine( st_eng ); + 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( ntk.make_signal( d ) ); + } + + auto const res = call_with_stopwatch( st.time_compute_function, [&]() { + return engine( target, care_transformed, std::begin( divs ), std::end( divs ), tts, std::min( potential_gain - 1, max_inserts ) ); + } ); + if ( res ) + { + ++st.num_success; + 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 + { + ++st.num_fail; + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; /* xag_resyn_functor */ + +/*! \brief XAG-specific resubstitution algorithm. + * + * This algorithms iterates over each node, creates a + * reconvergence-driven cut, and attempts to re-express the node's + * function using existing nodes from the cut. Node which are no + * longer used (including nodes in their transitive fanins) can then + * be removed. The objective is to reduce the size of the network as + * much as possible while maintaining the global input-output + * functionality. + * + * **Required network functions:** + * + * - `clear_values` + * - `fanout_size` + * - `foreach_fanin` + * - `foreach_fanout` + * - `foreach_gate` + * - `foreach_node` + * - `get_constant` + * - `get_node` + * - `is_complemented` + * - `is_pi` + * - `level` + * - `make_signal` + * - `set_value` + * - `set_visited` + * - `size` + * - `substitute_node` + * - `value` + * - `visited` + * + * \param ntk A network type derived from xag_network + * \param ps Resubstitution parameters + * \param pst Resubstitution statistics + */ +template +void xag_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( std::is_same_v, "Network type is not xag_network" ); + + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + + using truthtable_t = kitty::dynamic_truth_table; + using truthtable_dc_t = kitty::dynamic_truth_table; + using functor_t = xag_resyn_functor, truthtable_dc_t>; + + using resub_impl_t = detail::resubstitution_impl>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( ntk, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/algorithms/xag_resub_withDC.hpp b/include/mockturtle/algorithms/xag_resub_withDC.hpp new file mode 100644 index 0000000..e3c851f --- /dev/null +++ b/include/mockturtle/algorithms/xag_resub_withDC.hpp @@ -0,0 +1,989 @@ +/* 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 xag_resub_withDC.hpp + \brief Resubstitution with free xor (works for XAGs, XOR gates are considered for free) + + \author Eleonora Testa + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/xag.hpp" +#include "dont_cares.hpp" +#include "resubstitution.hpp" +#include + +namespace mockturtle +{ + +struct xag_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Accumulated runtime for two-resub. */ + stopwatch<>::duration time_resub2{ 0 }; + + /*! \brief Accumulated runtime for three-resub. */ + stopwatch<>::duration time_resub3{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1_and{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub2_and{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_unate_divisors{ 0 }; + + /*! \brief Accumulated runtime for collecting unate divisors. */ + stopwatch<>::duration time_collect_binate_divisors{ 0 }; + + /*! \brief Accumulated runtime for 12-resub. */ + stopwatch<>::duration time_resub12{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint64_t num_div1_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions */ + uint64_t num_div2_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions for AND */ + uint64_t num_div1_and_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions for AND */ + uint64_t num_div2_and_accepts{ 0 }; + + /*! \brief Number of accepted two resubsitutions using triples of unate divisors */ + uint64_t num_div12_accepts{ 0 }; + + void report() const + { + std::cout << "[i] kernel: xag_resub_functor\n"; + std::cout << fmt::format( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + std::cout << fmt::format( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + std::cout << fmt::format( "[i] 1-resub {:6d} ({:>5.2f} secs)\n", + num_div1_accepts, to_seconds( time_resub1 ) ); + std::cout << fmt::format( "[i] 2-resub {:6d} ({:>5.2f} secs)\n", + num_div2_accepts, to_seconds( time_resub2 ) ); + std::cout << fmt::format( "[i] 1-resub AND {:6d} ({:>5.2f} secs)\n", + num_div1_and_accepts, to_seconds( time_resub1_and ) ); + std::cout << fmt::format( "[i] 12-resub {:6d} ({:>5.2f} secs)\n", + num_div12_accepts, to_seconds( time_resub12 ) ); + std::cout << fmt::format( "[i] 2-resub AND {:6d} ({:>5.2f} secs)\n", + num_div2_and_accepts, to_seconds( time_resub2_and ) ); + std::cout << fmt::format( "[i] collect unate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_unate_divisors ) ); + std::cout << fmt::format( "[i] collect binate divisors ({:>5.2f} secs)\n", to_seconds( time_collect_binate_divisors ) ); + std::cout << fmt::format( "[i] total {:6d}\n", + ( num_const_accepts + num_div0_accepts + num_div1_accepts + num_div2_accepts + num_div1_and_accepts + num_div12_accepts + num_div2_and_accepts ) ); + } +}; /* xag_resub_stats */ + +namespace detail +{ + +template +class node_mffc_inside_xag +{ +public: + using node = typename Ntk::node; + +public: + explicit node_mffc_inside_xag( Ntk const& ntk ) + : ntk( ntk ) + { + } + + std::pair run( node const& n, std::vector const& leaves, std::vector& inside ) + { + /* increment the fanout counters for the leaves */ + for ( const auto& l : leaves ) + ntk.incr_fanout_size( l ); + + /* dereference the node */ + auto count1 = node_deref_rec( n ); + + /* collect the nodes inside the MFFC */ + node_mffc_cone( n, inside ); + + /* reference it back */ + auto count2 = node_ref_rec( n ); + (void)count2; + + assert( count1.first == count2.first ); + assert( count1.second == count2.second ); + + for ( const auto& l : leaves ) + ntk.decr_fanout_size( l ); + + return count1; + } + +private: + /* ! \brief Dereference the node's MFFC */ + std::pair node_deref_rec( node const& n ) + { + + if ( ntk.is_pi( n ) ) + return { 0, 0 }; + + int32_t counter_and = 0; + int32_t counter_xor = 0; + + if ( ntk.is_and( n ) ) + { + counter_and = 1; + } + else if ( ntk.is_xor( n ) ) + { + counter_xor = 1; + } + + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + ntk.decr_fanout_size( p ); + if ( ntk.fanout_size( p ) == 0 ) + { + auto counter = node_deref_rec( p ); + counter_and += counter.first; + counter_xor += counter.second; + } + } ); + + return { counter_and, counter_xor }; + } + + /* ! \brief Reference the node's MFFC */ + std::pair node_ref_rec( node const& n ) + { + if ( ntk.is_pi( n ) ) + return { 0, 0 }; + + int32_t counter_and = 0; + int32_t counter_xor = 0; + + if ( ntk.is_and( n ) ) + { + counter_and = 1; + } + else if ( ntk.is_xor( n ) ) + { + counter_xor = 1; + } + + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const& p = ntk.get_node( f ); + + auto v = ntk.fanout_size( p ); + ntk.incr_fanout_size( p ); + if ( v == 0 ) + { + auto counter = node_ref_rec( p ); + counter_and += counter.first; + counter_xor += counter.second; + } + } ); + + return { counter_and, counter_xor }; + } + + void node_mffc_cone_rec( node const& n, std::vector& cone, bool top_most ) + { + /* skip visited nodes */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + ntk.set_visited( n, ntk.trav_id() ); + + if ( !top_most && ( ntk.is_pi( n ) || ntk.fanout_size( n ) > 0 ) ) + return; + + /* recurse on children */ + ntk.foreach_fanin( n, [&]( const auto& f ) { + node_mffc_cone_rec( ntk.get_node( f ), cone, false ); + } ); + + /* collect the internal nodes */ + cone.emplace_back( n ); + } + + void node_mffc_cone( node const& n, std::vector& cone ) + { + cone.clear(); + ntk.incr_trav_id(); + node_mffc_cone_rec( n, cone, true ); + } + +private: + Ntk const& ntk; +}; + +} /* namespace detail */ + +template +struct xag_resub_functor +{ +public: + using node = xag_network::node; + using signal = xag_network::signal; + using stats = xag_resub_stats; + + struct unate_divisors + { + using signal = typename xag_network::signal; + + std::vector positive_divisors; + std::vector negative_divisors; + std::vector next_candidates; + + void clear() + { + positive_divisors.clear(); + negative_divisors.clear(); + next_candidates.clear(); + } + }; + + struct binate_divisors + { + using signal = typename xag_network::signal; + + std::vector positive_divisors0; + std::vector positive_divisors1; + std::vector negative_divisors0; + std::vector negative_divisors1; + + void clear() + { + positive_divisors0.clear(); + positive_divisors1.clear(); + negative_divisors0.clear(); + negative_divisors1.clear(); + } + }; + +public: + explicit xag_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, std::pair num_mffc, uint32_t& last_gain ) + { + + uint32_t num_and_mffc = num_mffc.first; + uint32_t num_xor_mffc = num_mffc.second; + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, care, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, care, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + if ( num_and_mffc == 0 ) + { + return std::nullopt; + if ( max_inserts == 0 || num_xor_mffc == 1 ) + return std::nullopt; + + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = 0; + return g; /* accepted resub */ + } + + if ( max_inserts == 1 || num_xor_mffc == 2 ) + return std::nullopt; + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = 0; + return g; /* accepted resub */ + } + } + else + { + + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2, [&]() { return resub_div2( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_accepts; + last_gain = num_and_mffc; + return g; /* accepted resub */ + } + + if ( num_and_mffc < 2 ) /* it is worth trying also AND resub here */ + return std::nullopt; + + /* collect level one divisors */ + call_with_stopwatch( st.time_collect_unate_divisors, [&]() { + collect_unate_divisors( root, required ); + } ); + + g = call_with_stopwatch( st.time_resub1_and, [&]() { return resub_div1_and( root, care, required ); } ); + if ( g ) + { + ++st.num_div1_and_accepts; + last_gain = num_and_mffc - 1; + return g; /* accepted resub */ + } + if ( num_and_mffc < 3 ) /* it is worth trying also AND-12 resub here */ + return std::nullopt; + + /* consider triples */ + g = call_with_stopwatch( st.time_resub12, [&]() { return resub_div12( root, care, required ); } ); + if ( g ) + { + ++st.num_div12_accepts; + last_gain = num_and_mffc - 2; + return g; /* accepted resub */ + } + + /* collect level two divisors */ + call_with_stopwatch( st.time_collect_binate_divisors, [&]() { + collect_binate_divisors( root, required ); + } ); + + /* consider two nodes */ + g = call_with_stopwatch( st.time_resub2_and, [&]() { return resub_div2_and( root, care, required ); } ); + if ( g ) + { + ++st.num_div2_and_accepts; + last_gain = num_and_mffc - 2; + return g; /* accepted resub */ + } + } + return std::nullopt; + } + + std::optional resub_const( node const& root, TT care, uint32_t required ) const + { + (void)required; + auto tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + if ( binary_and( tt, care ) == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, TT care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( binary_and( tt, care ) != binary_and( sim.get_tt( ntk.make_signal( d ) ), care ) ) + continue; + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + return std::nullopt; + } + + std::optional resub_div1( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + /* check for divisors */ + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const& s0 = divs.at( i ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const& s1 = divs.at( j ); + auto const& tt_s0 = sim.get_tt( ntk.make_signal( s0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( s1 ) ); + + if ( binary_and( ( tt_s0 ^ tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( s0 ) ? !ntk.make_signal( s0 ) : ntk.make_signal( s0 ); + auto const r = sim.get_phase( s1 ) ? !ntk.make_signal( s1 ) : ntk.make_signal( s1 ); + return sim.get_phase( root ) ? !ntk.create_xor( l, r ) : ntk.create_xor( l, r ); + } + else if ( binary_and( ( tt_s0 ^ tt_s1 ), care ) == binary_and( kitty::unary_not( tt ), care ) ) + { + auto const l = sim.get_phase( s0 ) ? !ntk.make_signal( s0 ) : ntk.make_signal( s0 ); + auto const r = sim.get_phase( s1 ) ? !ntk.make_signal( s1 ) : ntk.make_signal( s1 ); + return sim.get_phase( root ) ? ntk.create_xor( l, r ) : !ntk.create_xor( l, r ); + } + } + } + return std::nullopt; + } + + std::optional resub_div2( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const s0 = divs.at( i ); + + for ( auto j = i + 1; j < num_divs; ++j ) + { + auto const s1 = divs.at( j ); + + for ( auto k = j + 1; k < num_divs; ++k ) + { + auto const s2 = divs.at( k ); + auto const& tt_s0 = sim.get_tt( ntk.make_signal( s0 ) ); + auto const& tt_s1 = sim.get_tt( ntk.make_signal( s1 ) ); + auto const& tt_s2 = sim.get_tt( ntk.make_signal( s2 ) ); + + if ( binary_and( ( tt_s0 ^ tt_s1 ^ tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( s0 ), + ntk.level( s1 ), + ntk.level( s2 ) } ); + assert( max_level <= required - 1 ); + + signal max = ntk.make_signal( s0 ); + signal min0 = ntk.make_signal( s1 ); + signal min1 = ntk.make_signal( s2 ); + if ( ntk.level( s1 ) == max_level ) + { + max = ntk.make_signal( s1 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s2 ); + } + else if ( ntk.level( s2 ) == max_level ) + { + max = ntk.make_signal( s2 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s1 ); + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_xor( a, ntk.create_xor( b, c ) ) : ntk.create_xor( a, ntk.create_xor( b, c ) ); + } + else if ( binary_and( ( tt_s0 ^ tt_s1 ^ tt_s2 ), care ) == binary_and( kitty::unary_not( tt ), care ) ) + { + auto const max_level = std::max( { ntk.level( s0 ), + ntk.level( s1 ), + ntk.level( s2 ) } ); + assert( max_level <= required - 1 ); + + signal max = ntk.make_signal( s0 ); + signal min0 = ntk.make_signal( s1 ); + signal min1 = ntk.make_signal( s2 ); + if ( ntk.level( s1 ) == max_level ) + { + max = ntk.make_signal( s1 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s2 ); + } + else if ( ntk.level( s2 ) == max_level ) + { + max = ntk.make_signal( s2 ); + min0 = ntk.make_signal( s0 ); + min1 = ntk.make_signal( s1 ); + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? ntk.create_xor( a, ntk.create_xor( b, c ) ) : !ntk.create_xor( a, ntk.create_xor( b, c ) ); + } + } + } + } + return std::nullopt; + } + + void collect_unate_divisors( node const& root, uint32_t required ) + { + udivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( ntk.level( d ) > required - 1 ) + continue; + + auto const& tt_d = sim.get_tt( ntk.make_signal( d ) ); + + /* check positive containment */ + if ( kitty::implies( tt_d, tt ) ) + { + udivs.positive_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + /* check negative containment */ + if ( kitty::implies( tt, tt_d ) ) + { + udivs.negative_divisors.emplace_back( ntk.make_signal( d ) ); + continue; + } + + udivs.next_candidates.emplace_back( ntk.make_signal( d ) ); + } + } + + std::optional resub_div1_and( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check for positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const& s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const& s1 = udivs.positive_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( binary_and( ( tt_s0 | tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + return sim.get_phase( root ) ? !ntk.create_or( l, r ) : ntk.create_or( l, r ); + } + } + } + /* check for negative unate divisors */ + for ( auto i = 0u; i < udivs.negative_divisors.size(); ++i ) + { + auto const& s0 = udivs.negative_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.negative_divisors.size(); ++j ) + { + auto const& s1 = udivs.negative_divisors.at( j ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + + if ( binary_and( ( tt_s0 & tt_s1 ), care ) == binary_and( tt, care ) ) + { + auto const l = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const r = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + return sim.get_phase( root ) ? !ntk.create_and( l, r ) : ntk.create_and( l, r ); + } + } + } + + return std::nullopt; + } + + std::optional resub_div12( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check positive unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( binary_and( ( tt_s0 | tt_s1 | tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= required - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_or( b, c ) ) : ntk.create_or( a, ntk.create_or( b, c ) ); + } + } + } + } + + /* check negative unate divisors */ + for ( auto i = 0u; i < udivs.positive_divisors.size(); ++i ) + { + auto const s0 = udivs.positive_divisors.at( i ); + + for ( auto j = i + 1; j < udivs.positive_divisors.size(); ++j ) + { + auto const s1 = udivs.positive_divisors.at( j ); + + for ( auto k = j + 1; k < udivs.positive_divisors.size(); ++k ) + { + auto const s2 = udivs.positive_divisors.at( k ); + + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + if ( binary_and( ( tt_s0 & tt_s1 & tt_s2 ), care ) == binary_and( tt, care ) ) + { + auto const max_level = std::max( { ntk.level( ntk.get_node( s0 ) ), + ntk.level( ntk.get_node( s1 ) ), + ntk.level( ntk.get_node( s2 ) ) } ); + assert( max_level <= required - 1 ); + + signal max = s0; + signal min0 = s1; + signal min1 = s2; + if ( ntk.level( ntk.get_node( s1 ) ) == max_level ) + { + max = s1; + min0 = s0; + min1 = s2; + } + else if ( ntk.level( ntk.get_node( s2 ) ) == max_level ) + { + max = s2; + min0 = s0; + min1 = s1; + } + + auto const a = sim.get_phase( ntk.get_node( max ) ) ? !max : max; + auto const b = sim.get_phase( ntk.get_node( min0 ) ) ? !min0 : min0; + auto const c = sim.get_phase( ntk.get_node( min1 ) ) ? !min1 : min1; + + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_and( b, c ) ) : ntk.create_and( a, ntk.create_and( b, c ) ); + } + } + } + } + + return std::nullopt; + } + + void collect_binate_divisors( node const& root, uint32_t required ) + { + bdivs.clear(); + + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < udivs.next_candidates.size(); ++i ) + { + auto const& s0 = udivs.next_candidates.at( i ); + if ( ntk.level( ntk.get_node( s0 ) ) > required - 2 ) + continue; + + for ( auto j = i + 1; j < udivs.next_candidates.size(); ++j ) + { + auto const& s1 = udivs.next_candidates.at( j ); + if ( ntk.level( ntk.get_node( s1 ) ) > required - 2 ) + continue; + + if ( bdivs.positive_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( ~tt_s0 & tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( ~tt_s0 & ~tt_s1, tt ) ) + { + bdivs.positive_divisors0.emplace_back( !s0 ); + bdivs.positive_divisors1.emplace_back( !s1 ); + } + } + + if ( bdivs.negative_divisors0.size() < 500 ) // ps.max_divisors2 + { + auto const& tt_s0 = sim.get_tt( s0 ); + auto const& tt_s1 = sim.get_tt( s1 ); + if ( kitty::implies( tt, tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( s1 ); + } + + if ( kitty::implies( tt, tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + + if ( kitty::implies( tt, ~tt_s0 & ~tt_s1 ) ) + { + bdivs.negative_divisors0.emplace_back( !s0 ); + bdivs.negative_divisors1.emplace_back( !s1 ); + } + } + } + } + } + + std::optional resub_div2_and( node const& root, TT care, uint32_t required ) + { + (void)required; + auto const s = ntk.make_signal( root ); + auto const& tt = sim.get_tt( s ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* check positive unate divisors */ + for ( const auto& s0 : udivs.positive_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.positive_divisors0.size(); ++j ) + { + auto const s1 = bdivs.positive_divisors0.at( j ); + auto const s2 = bdivs.positive_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( binary_and( ( tt_s0 | ( tt_s1 & tt_s2 ) ), care ) == binary_and( tt, care ) ) + { + return sim.get_phase( root ) ? !ntk.create_or( a, ntk.create_and( b, c ) ) : ntk.create_or( a, ntk.create_and( b, c ) ); + } + } + } + + /* check negative unate divisors */ + for ( const auto& s0 : udivs.negative_divisors ) + { + auto const& tt_s0 = sim.get_tt( s0 ); + + for ( auto j = 0u; j < bdivs.negative_divisors0.size(); ++j ) + { + auto const s1 = bdivs.negative_divisors0.at( j ); + auto const s2 = bdivs.negative_divisors1.at( j ); + + auto const& tt_s1 = sim.get_tt( s1 ); + auto const& tt_s2 = sim.get_tt( s2 ); + + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + if ( binary_and( ( tt_s0 | ( tt_s1 & tt_s2 ) ), care ) == binary_and( tt, care ) ) + { + return sim.get_phase( root ) ? !ntk.create_and( a, ntk.create_or( b, c ) ) : ntk.create_and( a, ntk.create_or( b, c ) ); + } + } + } + + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; + + unate_divisors udivs; + binate_divisors bdivs; +}; /* xag_resub_functor */ + +template +void resubstitution_minmc_withDC( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + using truthtable_t = kitty::dynamic_truth_table; + using mffc_result_t = std::pair; + using resub_impl_t = detail::resubstitution_impl, truthtable_t>, mffc_result_t>, typename detail::default_divisor_collector, mffc_result_t>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp b/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp new file mode 100644 index 0000000..7b8640e --- /dev/null +++ b/include/mockturtle/algorithms/xmg_algebraic_rewriting.hpp @@ -0,0 +1,518 @@ +/* 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 xmg_algebraic_rewriting.hpp + \brief xmg algebraric rewriting + + \author Heinz Riener + \author Mathias Soeken + \author Zhufei Chu +*/ + +#pragma once + +#include +#include + +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Parameters for xmg_algebraic_depth_rewriting. + * + * The data structure `xmg_algebraic_depth_rewriting_params` holds configurable + * parameters with default arguments for `xmg_algebraic_depth_rewriting`. + */ +struct xmg_algebraic_depth_rewriting_params +{ + /*! \brief Rewriting strategy. */ + enum strategy_t + { + /*! \brief DFS rewriting strategy. + * + * Applies depth rewriting once to all output cones whose drivers have + * maximum levels + */ + dfs, + /*! \brief Aggressive rewriting strategy. + * + * Applies depth reduction multiple times until the number of nodes, which + * cannot be rewritten, matches the number of nodes, in the current + * network; or the new network size is larger than the initial size w.r.t. + * to an `overhead`. + */ + aggressive, + /*! \brief Selective rewriting strategy. + * + * Like `aggressive`, but only applies rewriting to nodes on critical paths + * and without `overhead`. + */ + selective + } strategy = dfs; + + /*! \brief Overhead factor in aggressive rewriting strategy. + * + * When comparing to the initial size in aggressive depth rewriting, also the + * number of dangling nodes are taken into account. + */ + float overhead{ 2.0f }; + + /*! \brief Allow area increase while optimizing depth. */ + bool allow_area_increase{ true }; +}; + +namespace detail +{ + +template +class xmg_algebraic_depth_rewriting_impl +{ +public: + xmg_algebraic_depth_rewriting_impl( Ntk& ntk, xmg_algebraic_depth_rewriting_params const& ps ) + : ntk( ntk ), ps( ps ) + { + } + + void run() + { + switch ( ps.strategy ) + { + case xmg_algebraic_depth_rewriting_params::dfs: + run_dfs(); + break; + case xmg_algebraic_depth_rewriting_params::selective: + run_selective(); + break; + case xmg_algebraic_depth_rewriting_params::aggressive: + run_aggressive(); + break; + } + } + +private: + void run_dfs() + { + ntk.foreach_po( [this]( auto po ) { + const auto driver = ntk.get_node( po ); + if ( ntk.level( driver ) < ntk.depth() ) + return; + topo_view topo{ ntk, po }; + topo.foreach_node( [this]( auto n ) { + reduce_depth( n ); + reduce_depth_xor_associativity( n ); + reduce_depth_xor_complementary_associativity( n ); + return true; + } ); + } ); + } + + void run_selective() + { + uint32_t counter{ 0 }; + while ( true ) + { + mark_critical_paths(); + + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 || ntk.value( n ) == 0 ) + return; + + if ( reduce_depth( n ) ) + { + mark_critical_paths(); + } + else + { + ++counter; + } + } ); + + if ( counter > ntk.size() ) + break; + } + } + + void run_aggressive() + { + uint32_t counter{ 0 }, init_size{ ntk.size() }; + while ( true ) + { + topo_view topo{ ntk }; + topo.foreach_node( [this, &counter]( auto n ) { + if ( ntk.fanout_size( n ) == 0 ) + return; + + if ( !reduce_depth( n ) ) + { + ++counter; + } + } ); + + if ( ntk.size() > ps.overhead * init_size ) + break; + if ( counter > ntk.size() ) + break; + } + } + +private: + bool reduce_depth( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_maj( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + ocs2[0] = !ocs2[0]; + ocs2[1] = !ocs2[1]; + ocs2[2] = !ocs2[2]; + } + + if ( auto cand = associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( z, assoc ? u : x, ntk.create_maj( x, y, u ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* distributivity */ + if ( ps.allow_area_increase ) + { + auto opt = ntk.create_maj( ocs2[2], + ntk.create_maj( ocs[0], ocs[1], ocs2[0] ), + ntk.create_maj( ocs[0], ocs[1], ocs2[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + } + return true; + } + + using candidate_t = std::tuple, signal, signal, signal, bool>; + std::optional associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + + return std::nullopt; + } + + /* XOR associativity */ + bool reduce_depth_xor_associativity( node const& n ) + { + if ( !ntk.is_xor3( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) <= ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* child must have single fanout, if no area overhead is allowed */ + if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + if ( ntk.is_complemented( ocs2[0] ) ) + { + ocs2[0] = !ocs2[0]; + } + else if ( ntk.is_complemented( ocs2[1] ) ) + { + ocs2[1] = !ocs2[1]; + } + else if ( ntk.is_complemented( ocs2[2] ) ) + { + ocs2[2] = !ocs2[2]; + } + else + { + ocs2[0] = !ocs2[0]; + } + } + + auto opt = ntk.create_xor3( ocs[0], ocs2[2], + ntk.create_xor3( ocs2[0], ocs2[1], ocs[1] ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + /* XOR complementary associativity = */ + bool reduce_depth_xor_complementary_associativity( node const& n ) + { + if ( !ntk.is_maj( n ) ) + return false; + + if ( ntk.level( n ) == 0 ) + return false; + + /* get children of top node, ordered by node level (ascending) */ + const auto ocs = ordered_children( n ); + + if ( !ntk.is_xor3( ntk.get_node( ocs[2] ) ) ) + return false; + + /* depth of last child must be (significantly) higher than depth of second child */ + /* depth of last child must be higher than depth of second child */ + if ( ntk.level( ntk.get_node( ocs[2] ) ) < ntk.level( ntk.get_node( ocs[1] ) ) + 1 ) + return false; + + /* multiple child fanout is allowable */ + // if ( !ps.allow_area_increase && ntk.fanout_size( ntk.get_node( ocs[2] ) ) != 1 ) + // return false; + + /* get children of last child */ + auto ocs2 = ordered_children( ntk.get_node( ocs[2] ) ); + + /* depth of last grand-child must be higher than depth of second grand-child */ + if ( ntk.level( ntk.get_node( ocs2[2] ) ) == ntk.level( ntk.get_node( ocs2[1] ) ) ) + return false; + + /* propagate inverter if necessary */ + if ( ntk.is_complemented( ocs[2] ) ) + { + if ( ntk.is_complemented( ocs2[0] ) ) + { + ocs2[0] = !ocs2[0]; + } + else if ( ntk.is_complemented( ocs2[1] ) ) + { + ocs2[1] = !ocs2[1]; + } + else if ( ntk.is_complemented( ocs2[2] ) ) + { + ocs2[2] = !ocs2[2]; + } + else + { + ocs2[0] = !ocs2[0]; + } + } + + if ( auto cand = xor_compl_associativity_candidate( ocs[0], ocs[1], ocs2[0], ocs2[1], ocs2[2] ); cand ) + { + const auto& [x, y, z, u, assoc] = *cand; + auto opt = ntk.create_maj( x, u, ntk.create_xor3( assoc ? !x : x, y, z ) ); + ntk.substitute_node( n, opt ); + ntk.update_levels(); + + return true; + } + + return true; + } + + std::optional xor_compl_associativity_candidate( signal const& v, signal const& w, signal const& x, signal const& y, signal const& z ) const + { + if ( v.index == x.index ) + { + return candidate_t{ w, y, z, v, v.complement == x.complement }; + } + if ( v.index == y.index ) + { + return candidate_t{ w, x, z, v, v.complement == y.complement }; + } + if ( v.index == z.index ) + { + return candidate_t{ w, x, y, v, v.complement == z.complement }; + } + if ( w.index == x.index ) + { + return candidate_t{ v, y, z, w, w.complement == x.complement }; + } + if ( w.index == y.index ) + { + return candidate_t{ v, x, z, w, w.complement == y.complement }; + } + if ( w.index == z.index ) + { + return candidate_t{ v, x, y, w, w.complement == z.complement }; + } + + return std::nullopt; + } + + std::array, 3> ordered_children( node const& n ) const + { + std::array, 3> children; + ntk.foreach_fanin( n, [&children]( auto const& f, auto i ) { children[i] = f; } ); + std::stable_sort( children.begin(), children.end(), [this]( auto const& c1, auto const& c2 ) { + return ntk.level( ntk.get_node( c1 ) ) < ntk.level( ntk.get_node( c2 ) ); + } ); + return children; + } + + void mark_critical_path( node const& n ) + { + if ( ntk.is_pi( n ) || ntk.is_constant( n ) || ntk.value( n ) ) + return; + + const auto level = ntk.level( n ); + ntk.set_value( n, 1 ); + ntk.foreach_fanin( n, [this, level]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == level - 1 ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + + void mark_critical_paths() + { + ntk.clear_values(); + ntk.foreach_po( [this]( auto const& f ) { + if ( ntk.level( ntk.get_node( f ) ) == ntk.depth() ) + { + mark_critical_path( ntk.get_node( f ) ); + } + } ); + } + +private: + Ntk& ntk; + xmg_algebraic_depth_rewriting_params const& ps; +}; + +} // namespace detail + +/*! \brief XMG algebraic depth rewriting. + * + * This algorithm tries to rewrite a network with majority gates for depth + * optimization using the associativity and distributivity rule in + * majority-of-3 logic. It can be applied to networks other than XMGs, but + * only considers pairs of nodes which both implement the majority-of-3 + * function and the XOR function. + * + * **Required network functions:** + * - `get_node` + * - `level` + * - `update_levels` + * - `create_maj` + * - `substitute_node` + * - `foreach_node` + * - `foreach_po` + * - `foreach_fanin` + * - `is_maj` + * - `clear_values` + * - `set_value` + * - `value` + * - `fanout_size` + * + \verbatim embed:rst + + .. note:: + + The implementation of this algorithm was heavily inspired by an + implementation from Luca Amarù. + \endverbatim + */ +template +void xmg_algebraic_depth_rewriting( Ntk& ntk, xmg_algebraic_depth_rewriting_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_level_v, "Ntk does not implement the level method" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_maj method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_update_levels_v, "Ntk does not implement the update_levels method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_maj method" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + + detail::xmg_algebraic_depth_rewriting_impl p( ntk, ps ); + p.run(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/xmg_optimization.hpp b/include/mockturtle/algorithms/xmg_optimization.hpp new file mode 100644 index 0000000..b19c73d --- /dev/null +++ b/include/mockturtle/algorithms/xmg_optimization.hpp @@ -0,0 +1,101 @@ +/* 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 xmg_optimization.hpp + \brief Rewriting MAJ to XNORs. + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../networks/xmg.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "dont_cares.hpp" + +namespace mockturtle +{ + +/*! \brief Optimizes some MAJ gates using satisfiability don't cares + * + * The function is based on `xag_dont_cares_optimization` in `xag_optimization.hpp`. + * + * If a MAJ gate is satisfiability don't care for assignments 000 and 111, it can be + * replaced by an XNOR gate. + */ +inline xmg_network xmg_dont_cares_optimization( xmg_network const& xmg ) +{ + node_map old_to_new( xmg ); + + xmg_network dest; + old_to_new[xmg.get_constant( false )] = dest.get_constant( false ); + + xmg.foreach_pi( [&]( auto const& n ) { + old_to_new[n] = dest.create_pi(); + } ); + + satisfiability_dont_cares_checker checker( xmg ); + + topo_view{ xmg }.foreach_node( [&]( auto const& n ) { + if ( xmg.is_constant( n ) || xmg.is_pi( n ) ) + return; + + std::array fanin; + xmg.foreach_fanin( n, [&]( auto const& f, auto i ) { + fanin[i] = old_to_new[f] ^ xmg.is_complemented( f ); + } ); + + if ( xmg.is_maj( n ) ) + { + if ( checker.is_dont_care( n, { false, false, false } ) && checker.is_dont_care( n, { true, true, true } ) ) + { + old_to_new[n] = dest.create_xor3( !fanin[0], fanin[1], fanin[2] ); + } + else + { + old_to_new[n] = dest.create_maj( fanin[0], fanin[1], fanin[2] ); + } + } + else /* is XOR */ + { + old_to_new[n] = dest.create_xor3( fanin[0], fanin[1], fanin[2] ); + } + } ); + + xmg.foreach_po( [&]( auto const& f ) { + dest.create_po( old_to_new[f] ^ xmg.is_complemented( f ) ); + } ); + + return dest; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/xmg_resub.hpp b/include/mockturtle/algorithms/xmg_resub.hpp new file mode 100644 index 0000000..44690e0 --- /dev/null +++ b/include/mockturtle/algorithms/xmg_resub.hpp @@ -0,0 +1,346 @@ +/* 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 xmg_resub.hpp + \brief Resubstitution + + \author Heinz Riener + \author Mathias Soeken + \author Shubham Rai + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once +#include "../networks/xmg.hpp" +#include "dont_cares.hpp" +#include "resubstitution.hpp" +#include + +namespace mockturtle +{ + +namespace detail +{ + +/*! \brief Ternary XOR of three truth tables */ +template +inline TT ternary_xor( const TT& first, const TT& second, const TT& third ) +{ + return kitty::ternary_operation( first, second, third, []( auto a, auto b, auto c ) { return ( ( a ^ b ) ^ c ); } ); +} + +} // namespace detail + +struct xmg_resub_stats +{ + /*! \brief Accumulated runtime for const-resub */ + stopwatch<>::duration time_resubC{ 0 }; + + /*! \brief Accumulated runtime for zero-resub */ + stopwatch<>::duration time_resub0{ 0 }; + + /*! \brief Accumulated runtime for one-resub */ + stopwatch<>::duration time_resub1{ 0 }; + + /*! \brief Number of accepted constant resubsitutions */ + uint32_t num_const_accepts{ 0 }; + + /*! \brief Number of accepted zero resubsitutions */ + uint32_t num_div0_accepts{ 0 }; + + /*! \brief Number of accepted one resubsitutions */ + uint32_t num_div1_accepts{ 0 }; + + uint32_t num_div1_xor3_accepts{ 0 }; + uint32_t num_div1_xnor3_accepts{ 0 }; + uint32_t num_div1_maj3_accepts{ 0 }; + uint32_t num_div1_not_maj3_accepts{ 0 }; + + uint32_t num_filtered0{ 0 }; + uint32_t num_filtered1{ 0 }; + + void report() const + { + fmt::print( "[i] kernel: xmg_resub_functor\n" ); + fmt::print( "[i] constant-resub {:6d} ({:>5.2f} secs)\n", + num_const_accepts, to_seconds( time_resubC ) ); + fmt::print( "[i] 0-resub {:6d} ({:>5.2f} secs)\n", + num_div0_accepts, to_seconds( time_resub0 ) ); + fmt::print( "[i] 1-resub {:6d} = {:6d} XOR3 + {:6d} XNOR3 + {:6d} MAJ3 + {:6d} NOT-MAJ3 ({:>5.2f} secs)\n", + num_div1_accepts, num_div1_xor3_accepts, num_div1_xnor3_accepts, num_div1_maj3_accepts, num_div1_not_maj3_accepts, + to_seconds( time_resub1 ) ); + fmt::print( "[i] filtering candidates: {:6d} candidates in first loop + {:6d} candidates in second loop\n", num_filtered0, num_filtered1 ); + } +}; /* xmg_resub_stats */ + +template +struct xmg_resub_functor +{ +public: + using node = xmg_network::node; + using signal = xmg_network::signal; + using stats = xmg_resub_stats; + +public: + explicit xmg_resub_functor( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ), sim( sim ), divs( divs ), num_divs( num_divs ), st( st ) + { + } + + std::optional operator()( node const& root, TT& care, uint32_t required, uint32_t max_inserts, uint32_t num_mffc, uint32_t& last_gain ) + { + (void)care; + assert( is_const0( ~care ) ); + + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + if ( care.num_vars() > tt.num_vars() ) + care = kitty::shrink_to( care, tt.num_vars() ); + else + care = kitty::extend_to( care, tt.num_vars() ); + + /* consider constants */ + auto g = call_with_stopwatch( st.time_resubC, [&]() { + return resub_const( root, care, required ); + } ); + if ( g ) + { + ++st.num_const_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + /* consider equal nodes */ + g = call_with_stopwatch( st.time_resub0, [&]() { + return resub_div0( root, care, required ); + } ); + if ( g ) + { + ++st.num_div0_accepts; + last_gain = num_mffc; + return g; /* accepted resub */ + } + + if ( max_inserts == 0 || num_mffc == 1 ) + return std::nullopt; + + /* consider adding one gate */ + g = call_with_stopwatch( st.time_resub1, [&]() { + return resub_div1( root, care, required ); + } ); + if ( g ) + { + ++st.num_div1_accepts; + last_gain = num_mffc - 1; + return g; /* accepted resub */ + } + + return std::nullopt; + } + + std::optional resub_const( node const& root, TT& care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + + if ( binary_and( tt, care ) == sim.get_tt( ntk.get_constant( false ) ) ) + { + return sim.get_phase( root ) ? ntk.get_constant( true ) : ntk.get_constant( false ); + } + return std::nullopt; + } + + std::optional resub_div0( node const& root, TT& care, uint32_t required ) const + { + (void)required; + auto const tt = sim.get_tt( ntk.make_signal( root ) ); + for ( auto i = 0u; i < num_divs; ++i ) + { + auto const d = divs.at( i ); + + if ( binary_and( tt, care ) != binary_and( sim.get_tt( ntk.make_signal( d ) ), care ) ) + continue; /* next */ + + return ( sim.get_phase( d ) ^ sim.get_phase( root ) ) ? !ntk.make_signal( d ) : ntk.make_signal( d ); + } + + return std::nullopt; + } + + struct divisor + { + explicit divisor( uint32_t node, int32_t entropy ) + : node( node ), entropy( entropy ) + { + } + + uint32_t node; + int32_t entropy; + }; + + std::optional resub_div1( node const& root, TT& care, uint32_t required ) + { + (void)required; + auto const& tt = sim.get_tt( ntk.make_signal( root ) ); + + const auto root_rdb = static_cast( absolute_distinguishing_power( tt ) ); + + std::vector sorted_divs; + for ( auto it = std::begin( divs ), ie = std::begin( divs ) + num_divs; it != ie; ++it ) + { + auto const s = ntk.make_signal( *it ); + auto const& tt_s = sim.get_tt( s ); + sorted_divs.emplace_back( static_cast( *it ), static_cast( relative_distinguishing_power( tt_s, tt ) ) ); + } + std::stable_sort( std::rbegin( sorted_divs ), std::rend( sorted_divs ), + [&]( auto const& u, auto const& v ) { + if ( u.entropy == v.entropy ) + return u.node < v.node; + return u.entropy < v.entropy; + } ); + + for ( auto i = 0u; i < sorted_divs.size(); ++i ) + { + auto const s0 = ntk.make_signal( sorted_divs.at( i ).node ); + auto const& tt0 = sim.get_tt( s0 ); + auto const a = sim.get_phase( ntk.get_node( s0 ) ) ? !s0 : s0; + + int64_t const db_s0 = sorted_divs.at( i ).entropy; + int64_t const bound0 = root_rdb - db_s0; + for ( auto j = i + 1; j < sorted_divs.size(); ++j ) + { + auto const s1 = ntk.make_signal( sorted_divs.at( j ).node ); + auto const& tt1 = sim.get_tt( s1 ); + auto const b = sim.get_phase( ntk.get_node( s1 ) ) ? !s1 : s1; + + int64_t const db_s1 = sorted_divs.at( j ).entropy; + if ( ( 2u * db_s1 ) < bound0 ) + { + st.num_filtered0 += static_cast( ( sorted_divs.size() - j - 1 ) * ( sorted_divs.size() - j ) ); + break; + } + + int64_t const bound1 = bound0 - db_s1; + for ( auto k = j + 1; k < sorted_divs.size(); ++k ) + { + auto const s2 = ntk.make_signal( sorted_divs.at( k ).node ); + auto const& tt2 = sim.get_tt( s2 ); + auto const c = sim.get_phase( ntk.get_node( s2 ) ) ? !s2 : s2; + + int64_t const db_s2 = sorted_divs.at( k ).entropy; + if ( db_s2 < bound1 ) + { + st.num_filtered1 += static_cast( sorted_divs.size() - k - 1 ); + break; + } + + if ( binary_and( tt, care ) == binary_and( detail::ternary_xor( tt0, tt1, tt2 ), care ) ) + { + /* XOR3 */ + ++st.num_div1_xor3_accepts; + return sim.get_phase( root ) ? !ntk.create_xor3( a, b, c ) : ntk.create_xor3( a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( detail::ternary_xor( ~tt0, tt1, tt2 ), care ) ) + { + /* XNOR3 */ + ++st.num_div1_xnor3_accepts; + return sim.get_phase( root ) ? !ntk.create_xor3( !a, b, c ) : ntk.create_xor3( !a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( kitty::ternary_majority( tt0, tt1, tt2 ), care ) ) + { + /* MAJ3 */ + ++st.num_div1_maj3_accepts; + return sim.get_phase( root ) ? !ntk.create_maj( a, b, c ) : ntk.create_maj( a, b, c ); + } + else if ( binary_and( tt, care ) == binary_and( kitty::ternary_majority( ~tt0, tt1, tt2 ), care ) ) + { + /* NOT-MAJ3 */ + ++st.num_div1_not_maj3_accepts; + return sim.get_phase( root ) ? !ntk.create_maj( !a, b, c ) : ntk.create_maj( !a, b, c ); + } + } + } + } + return std::nullopt; + } + +private: + Ntk& ntk; + Simulator const& sim; + std::vector const& divs; + uint32_t const num_divs; + stats& st; +}; /* xmg_resub_functor */ + +template +void xmg_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubstitution_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_clear_values_v, "Ntk does not implement the clear_values method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_set_value_v, "Ntk does not implement the set_value method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_size_v, "Ntk does not implement the has_size method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the has substitute_node method" ); + static_assert( has_value_v, "Ntk does not implement the has_value method" ); + static_assert( has_visited_v, "Ntk does not implement the has_visited method" ); + + using resub_view_t = fanout_view>; + depth_view depth_view{ ntk }; + resub_view_t resub_view{ depth_view }; + + 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>>>; + + resubstitution_stats st; + typename resub_impl_t::engine_st_t engine_st; + typename resub_impl_t::collector_st_t collector_st; + + resub_impl_t p( resub_view, ps, st, engine_st, collector_st ); + p.run(); + + if ( ps.verbose ) + { + st.report(); + collector_st.report(); + engine_st.report(); + } + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/generators/arithmetic.hpp b/include/mockturtle/generators/arithmetic.hpp new file mode 100644 index 0000000..f99df24 --- /dev/null +++ b/include/mockturtle/generators/arithmetic.hpp @@ -0,0 +1,411 @@ +/* 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 arithmetic.hpp + \brief Generate arithmetic logic networks + + \author Heinz Riener + \author Jovan Blanuša + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include + +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +/*! \brief Inserts a full adder into a network. + * + * Inserts a full adder for three inputs (two 1-bit operands and one carry) + * into the network and returns a pair of sum and carry bit. + * + * By default creates a seven 2-input gate network composed of AND, NOR, and OR + * gates. If network has `create_node` function, creates two 3-input gate + * network. If the network has ternary `create_maj` and `create_xor3` + * functions, it will use them (except for AIGs). + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + * \param c Carry + * \return Pair of sum (`first`) and carry (`second`) + */ +template +inline std::pair, signal> full_adder( Ntk& ntk, const signal& a, const signal& b, const signal& c ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + /* specialization for LUT-ish networks */ + if constexpr ( has_create_node_v ) + { + kitty::dynamic_truth_table tt_maj( 3u ), tt_xor( 3u ); + kitty::create_from_hex_string( tt_maj, "e8" ); + kitty::create_from_hex_string( tt_xor, "96" ); + + const auto sum = ntk.create_node( { a, b, c }, tt_xor ); + const auto carry = ntk.create_node( { a, b, c }, tt_maj ); + + return { sum, carry }; + } + /* use MAJ and XOR3 if available by network, unless network is AIG */ + else if constexpr ( !std::is_same_v && has_create_maj_v && has_create_xor3_v ) + { + const auto carry = ntk.create_maj( a, b, c ); + const auto sum = ntk.create_xor3( a, b, c ); + return { sum, carry }; + } + else + { + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_nor_v, "Ntk does not implement the create_nor method" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or method" ); + + const auto w1 = ntk.create_and( a, b ); + const auto w2 = ntk.create_nor( a, b ); + const auto w3 = ntk.create_nor( w1, w2 ); + const auto w4 = ntk.create_and( c, w3 ); + const auto w5 = ntk.create_nor( c, w3 ); + const auto sum = ntk.create_nor( w4, w5 ); + const auto carry = ntk.create_or( w1, w4 ); + + return { sum, carry }; + } +} + +/*! \brief Inserts a half adder into a network. + * + * Inserts a half adder for two inputs (two 1-bit operands) + * into the network and returns a pair of sum and carry bit. + * + * It creates three 2-input gate network composed of AND and NOR gates. + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + * \return Pair of sum (`first`) and carry (`second`) + */ +template +inline std::pair, signal> half_adder( Ntk& ntk, const signal& a, const signal& b ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + /* specialization for LUT-ish networks */ + if constexpr ( has_create_node_v ) + { + kitty::dynamic_truth_table tt_and( 2u ), tt_xor( 2u ); + kitty::create_from_hex_string( tt_and, "8" ); + kitty::create_from_hex_string( tt_xor, "6" ); + + const auto sum = ntk.create_node( { a, b }, tt_xor ); + const auto carry = ntk.create_node( { a, b }, tt_and ); + + return { sum, carry }; + } + else + { + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_nor_v, "Ntk does not implement the create_nor method" ); + + const auto carry = ntk.create_and( a, b ); + const auto w2 = ntk.create_nor( a, b ); + const auto sum = ntk.create_nor( carry, w2 ); + + return { sum, carry }; + } +} + +/*! \brief Creates carry ripple adder structure. + * + * Creates a carry ripple structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_ripple_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + assert( a.size() == b.size() ); + + auto pa = a.begin(); + for ( auto pb = b.begin(); pa != a.end(); ++pa, ++pb ) + { + std::tie( *pa, carry ) = full_adder( ntk, *pa, *pb, carry ); + } +} + +/*! \brief Creates carry ripple subtractor structure. + * + * Creates a carry ripple structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. The inputs in `b` are inverted to realize subtraction with full + * adders. The carry bit must be passed in inverted state to the subtractor. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_ripple_subtractor_inplace( Ntk& ntk, std::vector>& a, const std::vector>& b, signal& carry ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + assert( a.size() == b.size() ); + + auto pa = a.begin(); + for ( auto pb = b.begin(); pa != a.end(); ++pa, ++pb ) + { + std::tie( *pa, carry ) = full_adder( ntk, *pa, ntk.create_not( *pb ), carry ); + } +} + +/*! \brief Creates a classical multiplier using full adders. + * + * The vectors `a` and `b` must not have the same size. The function creates + * the multiplier in `ntk` and returns output signals, whose size is the summed + * sizes of `a` and `b`. + * + * \param ntk Network + * \param a First input operand + * \param b Second input operand + */ +template +inline std::vector> carry_ripple_multiplier( Ntk& ntk, std::vector> const& a, std::vector> const& b ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + auto res = constant_word( ntk, 0, static_cast( a.size() + b.size() ) ); + auto tmp = constant_word( ntk, 0, static_cast( a.size() * 2 ) ); + + for ( auto j = 0u; j < b.size(); ++j ) + { + for ( auto i = 0u; i < a.size(); ++i ) + { + std::tie( i ? tmp[a.size() + i - 1] : res[j], tmp[i] ) = full_adder( ntk, ntk.create_and( a[i], b[j] ), tmp[a.size() + i], tmp[i] ); + } + } + + auto carry = tmp.back() = ntk.get_constant( false ); + for ( auto i = 0u; i < a.size(); ++i ) + { + std::tie( res[b.size() + i], carry ) = full_adder( ntk, tmp[i], tmp[a.size() + i], carry ); + } + + return res; +} + +// CLA implementation based on Alan Mishchenko's implementation in +// https://github.com/berkeley-abc/abc/blob/master/src/base/wlc/wlcBlast.c +namespace detail +{ + +template +inline std::pair, signal> carry_lookahead_adder_inplace_rec( Ntk& ntk, + typename std::vector>::iterator genBegin, + typename std::vector>::iterator genEnd, + typename std::vector>::iterator proBegin, + typename std::vector>::iterator carBegin ) +{ + auto const term_case = [&]( signal const& gen0, signal const& gen1, signal const& pro0, signal const& pro1, signal const& car ) -> std::tuple, signal, signal> { + auto tmp = ntk.create_and( gen0, pro1 ); + auto rPro = ntk.create_and( pro0, pro1 ); + auto rGen = ntk.create_or( ntk.create_or( gen1, tmp ), ntk.create_and( rPro, car ) ); + auto rCar = ntk.create_or( gen0, ntk.create_and( pro0, car ) ); + + return { rGen, rPro, rCar }; + }; + + auto m = std::distance( genBegin, genEnd ); + + if ( m == 2 ) + { + const auto [gen, pro, car] = term_case( *genBegin, *( genBegin + 1 ), *proBegin, *( proBegin + 1 ), *carBegin ); + *( carBegin + 1 ) = car; + return { gen, pro }; + } + else + { + m >>= 1; + const auto [gen0, pro0] = carry_lookahead_adder_inplace_rec( ntk, genBegin, genBegin + m, proBegin, carBegin ); + *( carBegin + m ) = gen0; + const auto [gen1, pro1] = carry_lookahead_adder_inplace_rec( ntk, genBegin + m, genEnd, proBegin + m, carBegin + m ); + + const auto [gen, pro, car] = term_case( gen0, gen1, pro0, pro1, *carBegin ); + *( carBegin + m ) = car; + return { gen, pro }; + } +} + +template +inline void carry_lookahead_adder_inplace_pow2( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + if ( a.size() == 1u ) + { + a[0] = full_adder( ntk, a[0], b[0], carry ).first; + return; + } + + std::vector> gen( a.size() ), pro( a.size() ), car( a.size() + 1 ); + car[0] = carry; + std::transform( a.begin(), a.end(), b.begin(), gen.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_and( f, g ); } ); + std::transform( a.begin(), a.end(), b.begin(), pro.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_xor( f, g ); } ); + + carry_lookahead_adder_inplace_rec( ntk, gen.begin(), gen.end(), pro.begin(), car.begin() ); + std::transform( pro.begin(), pro.end(), car.begin(), a.begin(), [&]( auto const& f, auto const& g ) { return ntk.create_xor( f, g ); } ); +} + +} // namespace detail + +/*! \brief Creates carry lookahead adder structure. + * + * Creates a carry lookahead structure composed of full adders. The vectors `a` + * and `b` must have the same size. The resulting sum bits are eventually + * stored in `a` and the carry bit will be overridden to store the output carry + * bit. + * + * \param a First input operand, will also have the output after the call + * \param b Second input operand + * \param carry Carry bit, will also have the output carry after the call + */ +template +inline void carry_lookahead_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, signal& carry ) +{ + /* extend bitsize to next power of two */ + const auto log2 = static_cast( std::ceil( std::log2( static_cast( a.size() + 1 ) ) ) ); + + std::vector> a_ext( a.begin(), a.end() ); + a_ext.resize( static_cast( 1 ) << log2, ntk.get_constant( false ) ); + std::vector> b_ext( b.begin(), b.end() ); + b_ext.resize( static_cast( 1 ) << log2, ntk.get_constant( false ) ); + + detail::carry_lookahead_adder_inplace_pow2( ntk, a_ext, b_ext, carry ); + + std::copy_n( a_ext.begin(), a.size(), a.begin() ); + carry = a_ext[a.size()]; +} + +/*! \brief Creates a sideways sum adder using half and full adders + * + * The function creates the adder in `ntk` and returns output signals, + * whose size is floor(log2(a.size()))+1. + * + * \param ntk Network + * \param a Input operand + */ +template +inline std::vector> sideways_sum_adder( Ntk& ntk, std::vector> const& a ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + int n = static_cast( a.size() ); + + int out_n = 1; // floor(log2(n) + 1) + int tmpn = n; + while ( tmpn >>= 1 ) + out_n++; + + std::list> sum_bits, carry_bits; + + auto* first_level = &sum_bits; + auto* second_level = &carry_bits; + + auto res = constant_word( ntk, 0, out_n ); + int output_ind = 0; + + for ( int i = 0; i < n; i++ ) + first_level->push_back( a[i] ); + + while ( 1 ) + { + while ( !first_level->empty() ) + { + if ( first_level->size() == 1 ) + { + auto in_sig = first_level->front(); + first_level->pop_front(); + res[output_ind++] = in_sig; + } + else if ( first_level->size() == 2 ) + { + auto in_sig1 = first_level->front(); + first_level->pop_front(); + auto in_sig2 = first_level->front(); + first_level->pop_front(); + signal tmp_sum; + signal tmp_carry; + std::tie( tmp_sum, tmp_carry ) = half_adder( ntk, in_sig1, in_sig2 ); + first_level->push_back( tmp_sum ); + second_level->push_back( tmp_carry ); + } + else // first_level->size() >=3 + { + auto in_sig1 = first_level->front(); + first_level->pop_front(); + auto in_sig2 = first_level->front(); + first_level->pop_front(); + auto in_sig3 = first_level->front(); + first_level->pop_front(); + signal tmp_sum; + signal tmp_carry; + std::tie( tmp_sum, tmp_carry ) = full_adder( ntk, in_sig1, in_sig2, in_sig3 ); + first_level->push_back( tmp_sum ); + second_level->push_back( tmp_carry ); + } + } + + if ( second_level->empty() ) + break; + + // swapping buffers + auto tmp = first_level; + first_level = second_level; + second_level = tmp; + } + + return res; +} + +} // namespace mockturtle diff --git a/include/mockturtle/generators/control.hpp b/include/mockturtle/generators/control.hpp new file mode 100644 index 0000000..8c2fe52 --- /dev/null +++ b/include/mockturtle/generators/control.hpp @@ -0,0 +1,217 @@ +/* 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 control.hpp + \brief Generate control logic networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Creates a word from a constant + * + * Creates a vector of `bitwidth` constants that represent the positive number + * `value`. + */ +template +inline std::vector> constant_word( Ntk& ntk, uint64_t value, uint32_t bitwidth ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + std::vector> word( bitwidth ); + for ( auto i = 0u; i < bitwidth; ++i ) + { + bool bit = false; + if ( i < 64 ) + { + bit = static_cast( ( value >> i ) & 1 ); + } + word[i] = ntk.get_constant( bit ); + } + return word; +} + +/*! \brief Extends a word by leading zeros + * + * Adds leading zeros as most-significant bits to word `a`. The size of `a` + * must be smaller or equal to `bitwidth`, which is the width of the resulting + * word. + */ +template +inline std::vector> zero_extend( Ntk& ntk, std::vector> const& a, uint32_t bitwidth ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + assert( bitwidth >= a.size() ); + + auto ret{ a }; + for ( auto i = a.size(); i < bitwidth; ++i ) + { + ret.emplace_back( ntk.get_constant( false ) ); + } + return ret; +} + +/*! \brief Creates a 2k-k MUX (array of k 2-1 MUXes). + * + * This creates *k* MUXes using `cond` as condition signal and `t` for the then + * signals and `e` for the else signals. The method works in-place and writes + * the outputs of the networ into `t`. + */ +template +inline void mux_inplace( Ntk& ntk, signal const& cond, std::vector>& t, std::vector> const& e ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + + std::transform( t.begin(), t.end(), e.begin(), t.begin(), [&]( auto const& a, auto const& b ) { return ntk.create_ite( cond, a, b ); } ); +} + +template +inline std::vector> mux( Ntk& ntk, signal const& cond, std::vector> const& t, std::vector> const& e ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite method" ); + + std::vector> ret; + std::transform( t.begin(), t.end(), e.begin(), std::back_inserter( ret ), [&]( auto const& a, auto const& b ) { return ntk.create_ite( cond, a, b ); } ); + return ret; +} + +/*! \brief Creates k-to-2^k binary decoder + * + * Given k signals `xs`, this function creates 2^k signals of which exactly one + * input is 1, for each of the 2^k input assignments to `xs`. + */ +template +std::vector> binary_decoder( Ntk& ntk, std::vector> const& xs ) +{ + if ( xs.empty() ) + { + return {}; + } + + if ( xs.size() == 1u ) + { + return { ntk.create_not( xs[0] ), xs[0] }; + } + + // recursion + const auto m = ( xs.size() + 1 ) / 2; + + const auto d1 = binary_decoder( ntk, std::vector>( xs.begin(), xs.begin() + m ) ); + const auto d2 = binary_decoder( ntk, std::vector>( xs.begin() + m, xs.end() ) ); + + std::vector> d( 1 << xs.size() ); + auto it = d.begin(); + + for ( auto const& s2 : d2 ) + { + for ( auto const& s1 : d1 ) + { + *it++ = ntk.create_and( s1, s2 ); + } + } + + return d; +} + +/*! \brief Creates 2^k MUX + * + * Given k select signals `sel` and 2^k data signals `data`, this function + * creates a logic network that outputs `data[i]` when `i` is the encoded + * assignment of `sel`. + * + * This is an iterative construction based on MUX gates. A more efficient + * method may be provided by the Klein-Paterson variant + * `binary_mux_klein_paterson`. + */ +template +signal binary_mux( Ntk& ntk, std::vector> const& sel, std::vector> data ) +{ + for ( auto i = 0u; i < sel.size(); ++i ) + { + for ( auto j = 0u; j < ( 1u << ( sel.size() - i - 1u ) ); ++j ) + { + data[j] = ntk.create_ite( sel[i], data[2 * j + 1], data[2 * j] ); + } + } + + return data[0u]; +} + +/*! \brief Creates 2^k MUX + * + * Given k select signals `sel` and 2^k data signals `data`, this function + * creates a logic network that outputs `data[i]` when `i` is the encoded + * assignment of `sel`. + * + * This Klein-Paterson variant uses fewer gates than the direct method + * `binary_mux` (see Klein, & Paterson. (1980). Asymptotically Optimal Circuit + * for a Storage Access Function. IEEE Transactions on Computers, C-29(8), + * 737–738. doi:10.1109/tc.1980.1675657 ) + */ +template +signal binary_mux_klein_paterson( Ntk& ntk, std::vector> const& sel, std::vector> const& data ) +{ + if ( sel.size() == 1u ) + { + return ntk.create_ite( sel[0u], data[1u], data[0u] ); + } + + // recursion + const auto s = sel.size() / 2u; + const auto r = sel.size() - s; + + const auto ds = binary_decoder( ntk, std::vector>( sel.begin(), sel.begin() + s ) ); + std::vector> s_data( 1u << r ); + for ( auto j = 0u; j < s_data.size(); ++j ) + { + std::vector> and_terms( 1u << s ); + std::transform( ds.begin(), ds.end(), + data.begin() + ( j * ( 1u << s ) ), + and_terms.begin(), + [&]( auto const& f1, auto const& f2 ) { return ntk.create_and( f1, f2 ); } ); + s_data[j] = ntk.create_nary_or( and_terms ); + } + + return binary_mux_klein_paterson( ntk, std::vector>( sel.begin() + s, sel.end() ), s_data ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/generators/legacy.hpp b/include/mockturtle/generators/legacy.hpp new file mode 100644 index 0000000..f278d64 --- /dev/null +++ b/include/mockturtle/generators/legacy.hpp @@ -0,0 +1,209 @@ +/* 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 legacy.hpp + \brief Some older not used routines + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include + +#include "../traits.hpp" +#include "arithmetic.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +namespace legacy +{ + +namespace detail +{ + +template +inline std::pair compute_montgomery_parameters( IntType c, IntType k = 0 ) +{ + if ( k == 0 ) + { + k = 1 << ( static_cast( std::ceil( std::log2( c ) ) ) + 1 ); + } + + // egcd + IntType y = k % c; + IntType x = c; + IntType a{ 0 }, b{ 1 }; + + while ( y ) + { + std::tie( a, b ) = std::pair{ b, a - ( x / y ) * b }; + std::tie( x, y ) = std::pair{ y, x % y }; + } + + const IntType ki = ( a > 0 ) ? ( a % c ) : ( c + ( a % c ) % c ); + const IntType factor = ( k * ki - 1 ) / c; + + return { k, factor }; +} + +template +std::vector> to_montgomery_form( Ntk& ntk, std::vector> const& t, int32_t mod, uint32_t rbits, int64_t np ) +{ + /* bit-width of original mod */ + uint32_t nbits = t.size() - rbits; + + std::vector> t_rpart( t.begin(), t.begin() + rbits ); + auto m = carry_ripple_multiplier( ntk, t_rpart, constant_word( ntk, np, rbits ) ); + assert( m.size() == 2 * rbits ); + m.resize( rbits ); + assert( m.size() == rbits ); + + m = carry_ripple_multiplier( ntk, m, constant_word( ntk, mod, nbits ) ); + assert( m.size() == t.size() ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, m, t, carry ); + + m.erase( m.begin(), m.begin() + rbits ); + assert( m.size() == nbits ); + + std::vector> sum( m.begin(), m.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, sum, constant_word( ntk, mod, nbits ), carry_inv ); + + mux_inplace( ntk, !carry, m, sum ); + return m; +} + +} /* namespace detail */ + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod (2^k - + * c)\f$. The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + /* c must be smaller than 2^k */ + assert( c < ( UINT64_C( 1 ) << a.size() ) ); + + /* refer to simpler case */ + if ( c == 0 ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + const auto word = constant_word( ntk, c, static_cast( a.size() ) ); + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, word, carry ); + + carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, b, carry ); + + std::vector> sum( a.begin(), a.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, word, carry_inv ); + + mux_inplace( ntk, !carry, a, sum ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod (2^k - + * c)\f$. The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + /* c must be smaller than 2^k */ + assert( c < ( UINT64_C( 1 ) << a.size() ) ); + + /* refer to simpler case */ + if ( c == 0 ) + { + modular_subtractor_inplace( ntk, a, b ); + return; + } + + auto carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, b, carry ); + + const auto word = constant_word( ntk, c, static_cast( a.size() ) ); + std::vector> sum( a.begin(), a.end() ); + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, sum, word, carry_inv ); + + mux_inplace( ntk, carry, a, sum ); +} + +/*! \brief Creates modular multiplication based on Montgomery multiplication + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod (2^k - c)\f$. + * The first input word `a` is overridden and stores the output signals. + * + * The implementation is based on Montgomery multiplication and includes the + * encoding and decoding in and from the Montgomery number representation. + * Correct functionality is only ensured if both `a` and `b` are smaller than + * \f$2^k - c\f$. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t c ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + const auto n = ( 1 << a.size() ) - c; + const auto nbits = static_cast( std::ceil( std::log2( n ) ) ); + + auto [r, np] = detail::compute_montgomery_parameters( n ); + const auto rbits = static_cast( std::log2( r ) ); + + const auto f2 = constant_word( ntk, ( r * r ) % n, rbits ); + + const auto ma = detail::to_montgomery_form( ntk, carry_ripple_multiplier( ntk, a, f2 ), n, rbits, np ); + const auto mb = detail::to_montgomery_form( ntk, carry_ripple_multiplier( ntk, b, f2 ), n, rbits, np ); + + assert( ma.size() == nbits ); + assert( mb.size() == nbits ); + + a = detail::to_montgomery_form( ntk, zero_extend( ntk, carry_ripple_multiplier( ntk, ma, mb ), nbits + rbits ), n, rbits, np ); + a = detail::to_montgomery_form( ntk, zero_extend( ntk, a, nbits + rbits ), n, rbits, np ); +} + +} // namespace legacy + +} // namespace mockturtle diff --git a/include/mockturtle/generators/majority.hpp b/include/mockturtle/generators/majority.hpp new file mode 100644 index 0000000..3aa22c2 --- /dev/null +++ b/include/mockturtle/generators/majority.hpp @@ -0,0 +1,136 @@ +/* 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 majority.hpp + \brief Generate majority-n networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +signal fake_majority9( Ntk& ntk, std::array, 9> const& xs ) +{ + return ntk.create_maj( + ntk.create_maj( xs[0], xs[1], xs[2] ), + ntk.create_maj( xs[3], xs[4], xs[5] ), + ntk.create_maj( xs[6], xs[7], xs[8] ) ); +} + +template +signal general_associativity( Ntk& ntk, signal const& y, std::vector> const& xs ) +{ + assert( xs.size() >= 2u ); + + return std::accumulate( xs.rbegin() + 1, xs.rend(), xs.back(), + [&]( auto const& f, auto const& x ) { return ntk.create_maj( x, y, f ); } ); +} + +} // namespace detail + +/*! \brief Implements Majority-5 using 4 MAJ operations. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority5( Ntk& ntk, std::array, 5> const& xs ) +{ + const auto lhs = ntk.create_maj( xs[0], xs[1], xs[2] ); + const auto rhs = detail::general_associativity( ntk, xs[3], { xs[0], xs[1], xs[2] } ); + return ntk.create_maj( lhs, xs[4], rhs ); +} + +/*! \brief Implements Majority-7 using 7 MAJ operations. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority7( Ntk& ntk, std::array, 7> const& xs ) +{ + const auto side = [&]( std::array, 6> const& ws ) { + const auto l1 = ntk.create_maj( ws[0], ws[1], ws[2] ); + return detail::general_associativity( ntk, l1, { ws[3], ws[4], ws[5] } ); + }; + + return ntk.create_maj( + side( { xs[1], xs[2], xs[3], xs[4], xs[5], xs[6] } ), + xs[0], + side( { xs[4], xs[5], xs[6], xs[1], xs[2], xs[3] } ) ); +} + +/*! \brief Implements Majority-9 using 13 MAJ operations. + * + * All majority operations require no inverters. + */ +template +signal majority9_13( Ntk& ntk, std::array, 9> const& xs ) +{ + const auto side = [&]( std::array, 9> const& ws ) { + const auto l1 = ntk.create_maj( ws[3], ws[4], ws[5] ); + const auto l2 = detail::general_associativity( ntk, l1, { ws[0], ws[1], ws[2] } ); + return detail::general_associativity( ntk, l2, { ws[6], ws[7], ws[8] } ); + }; + + return ntk.create_maj( + side( xs ), + detail::fake_majority9( ntk, xs ), + side( { xs[0], xs[1], xs[2], xs[6], xs[7], xs[8], xs[3], xs[4], xs[5] } ) ); +} + +/*! \brief Implements Majority-9 using 12 MAJ operations. + * + * This construction requires one inverter. + */ +template +signal majority9_12( Ntk& ntk, std::array, 9> const& xs ) +{ + const auto side = [&]( std::array, 9> const& ws ) { + const auto bottom = ntk.create_maj( ntk.create_not( ws[0] ), ws[1], ws[2] ); + const auto l1 = ntk.create_maj( ws[3], ws[4], ws[5] ); + const auto l2 = ntk.create_maj( ws[0], l1, bottom ); + return detail::general_associativity( ntk, l2, { ws[6], ws[7], ws[8] } ); + }; + + return ntk.create_maj( + side( xs ), + detail::fake_majority9( ntk, xs ), + side( { xs[0], xs[1], xs[2], xs[6], xs[7], xs[8], xs[3], xs[4], xs[5] } ) ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/generators/majority_n.hpp b/include/mockturtle/generators/majority_n.hpp new file mode 100644 index 0000000..590f781 --- /dev/null +++ b/include/mockturtle/generators/majority_n.hpp @@ -0,0 +1,90 @@ +/* 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 majority_n.hpp + \brief Generate majority-n networks using BDD and sorter network based methods + + \author Dewmini Sudara + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "sorting.hpp" + +#include +#include + +namespace mockturtle +{ +/**! \brief Generates majority-n network on given input signals using the BDD based method + * + * All majority operations are leafy. + */ +template +signal majority_n_bdd( Ntk& ntk, std::array, N> const& xs ) +{ + const auto logic1 = ntk.get_constant( true ); + const auto logic0 = ntk.get_constant( false ); + std::array>, N> dp; + dp[0].push_back( xs[0] ); + for ( auto r = 1u; r <= xs.size() / 2; r++ ) + { + dp[r].push_back( ntk.create_maj( logic0, dp[r - 1][0], xs[r] ) ); + for ( auto c = 1u; c < r; c++ ) + { + dp[r].push_back( ntk.create_maj( dp[r - 1][c - 1], dp[r - 1][c], xs[r] ) ); + } + dp[r].push_back( ntk.create_maj( logic1, dp[r - 1][r - 1], xs[r] ) ); + } + for ( auto r = xs.size() / 2 + 1; r < xs.size(); r++ ) + { + for ( auto c = 0u; c < xs.size() - r; c++ ) + { + dp[r].push_back( ntk.create_maj( dp[r - 1][c], dp[r - 1][c + 1], xs[r] ) ); + } + } + return dp[xs.size() - 1][0]; +} + +/**! \brief Generates majority-n network on given input signals using bubble sort. + * + * All majority operations require no inverters and are leafy. + */ +template +signal majority_n_bubble_sort( Ntk& ntk, std::array, N> const& xs ) +{ + std::vector> sigs( xs.begin(), xs.end() ); + bubble_sorting_network( static_cast( sigs.size() ), [&sigs, &ntk]( auto i, auto j ) { + signal lhs = ntk.create_and( sigs[i], sigs[j] ); + signal rhs = ntk.create_or( sigs[i], sigs[j] ); + sigs[i] = lhs; + sigs[j] = rhs; + } ); + return sigs[sigs.size() / 2]; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/generators/modular_arithmetic.hpp b/include/mockturtle/generators/modular_arithmetic.hpp new file mode 100644 index 0000000..9018a28 --- /dev/null +++ b/include/mockturtle/generators/modular_arithmetic.hpp @@ -0,0 +1,796 @@ +/* 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 modular_arithmetic.hpp + \brief Generate modular arithmetic logic networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include + +#include "../traits.hpp" +#include "arithmetic.hpp" +#include "control.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +inline void invert_modulus( std::vector& m ) +{ + m.flip(); + + for ( auto i = 0u; i < m.size(); ++i ) + { + m[i] = !m[i]; + if ( m[i] ) + break; + } +} + +inline void increment_inplace( std::vector& word ) +{ + auto it = word.begin(); + + while ( it != word.end() ) + { + if ( ( *it++ = !*it ) ) + { + return; + } + } +} + +inline std::vector increment( std::vector const& word ) +{ + auto copy = word; + increment_inplace( copy ); + return copy; +} + +inline void decrement_inplace( std::vector& word ) +{ + auto it = word.begin(); + + while ( it != word.end() ) + { + if ( !( *it++ = !*it ) ) + { + return; + } + } +} + +inline std::vector decrement( std::vector const& word ) +{ + auto copy = word; + decrement_inplace( copy ); + return copy; +} + +} /* namespace detail */ + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod 2^k\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b ) +{ + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a, b, carry ); +} + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + // bit-size for corrected addition + const uint32_t bitsize = static_cast( m.size() ); + assert( bitsize <= a.size() ); + + // corrected registers + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + // 1. Compute (a + b) on bitsize bits + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, a_trim, b_trim, carry ); /* a_trim <- a + b */ + + // store result in sum (and extend it to bitsize + 1 bits) + auto sum = a_trim; /* sum <- a + b */ + sum.emplace_back( ntk.get_constant( false ) ); + + // 2. Compute (a + b) - m (m is represented as word) on (bitsize + 1) bits + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + auto carry_inv = ntk.get_constant( true ); + a_trim.emplace_back( carry ); + carry_ripple_subtractor_inplace( ntk, a_trim, word, carry_inv ); /* a_trim <- a + b - c */ + + // if overflow occurred in step 2, return result from step 2, otherwise, result from step 1. + mux_inplace( ntk, carry_inv, a_trim, sum ); + + // copy corrected register back into input register + std::copy_n( a_trim.begin(), bitsize, a.begin() ); +} + +/*! \brief Creates modular adder + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a + b) \bmod m\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_adder_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected addition + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_adder_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_adder( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_adder_inplace( ntk, w, b, m ); + return w; +} + +template +inline void modular_adder_hiasat_inplace( Ntk& ntk, std::vector>& x, std::vector> const& y, std::vector const& m ) +{ + assert( m.size() <= x.size() ); + assert( x.size() == y.size() ); + + const uint32_t bitsize = static_cast( m.size() ); + + // corrected registers + std::vector> x_trim( x.begin(), x.begin() + bitsize ); + std::vector> y_trim( y.begin(), y.begin() + bitsize ); + + // compute Z-vector from m-vector (Z = 2^bitsize - m) + auto z = m; + detail::invert_modulus( z ); + + /* SAC unit */ + std::vector> A( bitsize ), B( bitsize + 1 ), a( bitsize ), b( bitsize + 1 ); + + B[0] = b[0] = ntk.get_constant( false ); + for ( auto i = 0u; i < bitsize; ++i ) + { + A[i] = ntk.create_xor( x_trim[i], y_trim[i] ); + B[i + 1] = ntk.create_and( x_trim[i], y_trim[i] ); + a[i] = z[i] ? ntk.create_xnor( x_trim[i], y_trim[i] ) : A[i]; + b[i + 1] = z[i] ? ntk.create_or( x_trim[i], y_trim[i] ) : B[i + 1]; + } + + /* CPG unit */ + std::vector> G( bitsize ), P( bitsize + 1 ), g( bitsize ), p( bitsize + 1 ); + for ( auto i = 0u; i < bitsize; ++i ) + { + G[i] = ntk.create_and( A[i], B[i] ); + P[i] = ntk.create_xor( A[i], B[i] ); + g[i] = ntk.create_and( a[i], b[i] ); + p[i] = ntk.create_xor( a[i], b[i] ); + } + P[bitsize] = B[bitsize]; + p[bitsize] = b[bitsize]; + + /* CLA for C_out */ + std::vector> C( bitsize ); + C[0] = p[bitsize]; + for ( auto i = 1u; i < bitsize; ++i ) + { + std::vector> cube; + cube.push_back( g[i] ); + for ( auto j = i + 1u; j < bitsize; ++j ) + { + cube.push_back( p[j] ); + } + C[i] = ntk.create_nary_and( cube ); + } + const auto Cout = ntk.create_nary_or( C ); + // ntk.create_po( Cout ); + + /* MUX store result in p and g */ + p.pop_back(); + P.pop_back(); + mux_inplace( ntk, Cout, g, G ); + mux_inplace( ntk, Cout, p, P ); + + /* CLAS */ + C[0] = ntk.get_constant( false ); + for ( auto i = 1u; i < bitsize; ++i ) + { + C[i] = ntk.create_or( g[i - 1], ntk.create_and( p[i - 1], C[i - 1] ) ); + } + + for ( auto i = 0u; i < bitsize; ++i ) + { + x[i] = ntk.create_xor( p[i], C[i] ); + } +} + +template +inline void modular_adder_hiasat_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_adder_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected addition + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_adder_hiasat_inplace( ntk, a, b, mvec ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod 2^k\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b ) +{ + auto carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a, b, carry ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + // bit-size for corrected addition + const uint32_t bitsize = static_cast( m.size() ); + assert( bitsize <= a.size() ); + + // corrected registers + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + // 1. Compute (a - b) on bitsize bits + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, a_trim, b_trim, carry_inv ); /* a_trim <- a - b */ + + // store result in sum (and extend it to bitsize + 1 bits) + auto sum = a_trim; /* sum <- a - b */ + + sum.emplace_back( ntk.get_constant( false ) ); + + // 2. Compute (a - b) + m (m is represented as word) on (bitsize + 1) bits + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + auto carry = ntk.get_constant( false ); + a_trim.emplace_back( ntk.create_not( carry_inv ) ); + carry_ripple_adder_inplace( ntk, a_trim, word, carry ); /* a_trim <- (a - b) + c */ + + // if overflow occurred in step 2, return result from step 2, otherwise, result from step 1. + mux_inplace( ntk, carry, a_trim, sum ); + + // copy corrected register back into input register + std::copy_n( a_trim.begin(), bitsize, a.begin() ); +} + +/*! \brief Creates modular subtractor + * + * Given two input words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a - b) \bmod m\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_subtractor_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + // simpler case + if ( m == ( UINT64_C( 1 ) << a.size() ) ) + { + modular_subtractor_inplace( ntk, a, b ); + return; + } + + // bit-size for corrected subtraction + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_subtractor_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_subtractor( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_subtractor_inplace( ntk, w, b, m ); + return w; +} + +/*! \brief Creates modular doubling (multiplication by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(2 * a) \bmod m\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_doubling_inplace( Ntk& ntk, std::vector>& a, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + + std::vector> shifted( bitsize + 1u, ntk.get_constant( false ) ); + std::copy( a_trim.begin(), a_trim.end(), shifted.begin() + 1u ); + std::copy_n( shifted.begin(), bitsize, a_trim.begin() ); + + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + auto carry_inv = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, shifted, word, carry_inv ); + + mux_inplace( ntk, ntk.create_not( carry_inv ), a_trim, std::vector>( shifted.begin(), shifted.begin() + bitsize ) ); + std::copy( a_trim.begin(), a_trim.end(), a.begin() ); +} + +/*! \brief Creates modular doubling (multiplication by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(2 * a) \bmod m\f$. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_doubling_inplace( Ntk& ntk, std::vector>& a, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_doubling_inplace( ntk, a, mvec ); +} + +/*! \brief Creates modular halving (corrected division by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a / 2) \bmod m\f$. The + * modulus must be odd, and the function is evaluated to \f$a / 2\f$, if `a` is + * even and to \f$(a + m) / 2\f$, if `a` is odd. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_halving_inplace( Ntk& ntk, std::vector>& a, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + assert( m.size() > 0u ); + assert( m[0] ); + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + + std::vector> extended( bitsize + 1u, ntk.get_constant( false ) ), a_extended( bitsize + 1u, ntk.get_constant( false ) ); + std::copy( a_trim.begin(), a_trim.end(), extended.begin() ); + std::copy( a_trim.begin(), a_trim.end(), a_extended.begin() ); + + std::vector> word( bitsize + 1, ntk.get_constant( false ) ); + std::transform( m.begin(), m.end(), word.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, extended, word, carry ); + + mux_inplace( ntk, a_trim[0], extended, a_extended ); + + std::copy_n( extended.begin() + 1, bitsize, a.begin() ); +} + +/*! \brief Creates modular halving (corrected division by 2) + * + * Given one input word \f$a\f$ of size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(a / 2) \bmod m\f$. The + * modulus must be odd, and the function is evaluated to \f$a / 2\f$, if `a` is + * even and to \f$(a + m) / 2\f$, if `a` is odd. + * The input word `a` is overridden and stores the output signals. + */ +template +inline void modular_halving_inplace( Ntk& ntk, std::vector>& a, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_halving_inplace( ntk, a, mvec ); +} + +/*! \brief Creates modular multiplication + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod c\f$. + * The modulus `m` is passed as a vector of Booleans to support large bitsizes. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, std::vector const& m ) +{ + assert( a.size() >= m.size() ); + assert( a.size() == b.size() ); + + const auto bitsize = m.size(); + std::vector> a_trim( a.begin(), a.begin() + bitsize ); + std::vector> b_trim( b.begin(), b.begin() + bitsize ); + + std::vector> accu( bitsize ); + auto itA = a_trim.rbegin(); + std::transform( b_trim.begin(), b_trim.end(), accu.begin(), [&]( auto const& f ) { return ntk.create_and( *itA, f ); } ); + + while ( ++itA != a_trim.rend() ) + { + modular_doubling_inplace( ntk, accu, m ); + std::vector> summand( bitsize ); + std::transform( b_trim.begin(), b_trim.end(), summand.begin(), [&]( auto const& f ) { return ntk.create_and( *itA, f ); } ); + modular_adder_inplace( ntk, accu, summand, m ); + } + + std::copy( accu.begin(), accu.end(), a.begin() ); +} + +/*! \brief Creates modular multiplier + * + * Given two inputs words of the same size *k*, this function creates a circuit + * that computes *k* output signals that represent \f$(ab) \bmod c\f$. + * The first input word `a` is overridden and stores the output signals. + */ +template +inline void modular_multiplication_inplace( Ntk& ntk, std::vector>& a, std::vector> const& b, uint64_t m ) +{ + const auto bitsize = static_cast( std::ceil( std::log2( m ) ) ); + std::vector mvec( bitsize ); + for ( auto i = 0u; i < bitsize; ++i ) + { + mvec[i] = static_cast( ( m >> i ) & 1 ); + } + + modular_multiplication_inplace( ntk, a, b, mvec ); +} + +template +inline std::vector> modular_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& m ) +{ + auto w = a; + modular_multiplication_inplace( ntk, w, b, m ); + return w; +} + +template +inline std::vector> modular_constant_multiplier_one_bits( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + std::vector> sum( a.size(), ntk.get_constant( false ) ); + + auto it = std::find( constant.begin(), constant.end(), true ); + if ( it != constant.end() ) + { + auto shift = std::distance( constant.begin(), it ); + std::copy_n( a.begin(), a.size() - shift, sum.begin() + shift ); + it = std::find( it + 1, constant.end(), true ); + + while ( it != constant.end() ) + { + shift = std::distance( constant.begin(), it ); + std::vector> summand( a.size(), ntk.get_constant( false ) ); + std::copy_n( a.begin(), a.size() - shift, summand.begin() + shift ); + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, sum, summand, carry ); + it = std::find( it + 1, constant.end(), true ); + } + } + + return sum; +} + +template +inline std::vector> modular_constant_multiplier_csd( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + // constant == 0 + if ( std::find( constant.begin(), constant.end(), true ) == constant.end() ) + { + return std::vector>( a.size(), ntk.get_constant( false ) ); + } + // constant == 1 + else if ( constant.front() && std::find( constant.begin() + 1, constant.end(), true ) == constant.end() ) + { + return a; + } + // constant % 2 == 0 + else if ( !constant.front() ) + { + // constant / 2 + std::vector new_constant( a.size(), false ); + std::copy( constant.begin() + 1, constant.end(), new_constant.begin() ); + auto res = modular_constant_multiplier( ntk, a, new_constant ); + res.insert( res.begin(), ntk.get_constant( false ) ); + res.pop_back(); + return res; + } + // constant % 4 == 1u + else if ( !constant[1] ) + { + auto res = modular_constant_multiplier( ntk, a, detail::decrement( constant ) ); + modular_adder_inplace( ntk, res, a ); + return res; + } + else /* ( constant % 4 == 3u ) */ + { + auto res = modular_constant_multiplier( ntk, a, detail::increment( constant ) ); + modular_subtractor_inplace( ntk, res, a ); + return res; + } +} + +/*! \brief Creates modular constant-multiplier + * + * Given an input word of size *k* and a constant with the same bit-width, + * this function creates a circuit that computes \f$(a\cdot\mathrm{constant}) \bmod 2^k\f$. + */ +template +inline std::vector> modular_constant_multiplier( Ntk& ntk, std::vector> const& a, std::vector const& constant ) +{ + return modular_constant_multiplier_csd( ntk, a, constant ); +} + +/*! \brief Creates vector of Booleans from hex string + * + * This function can be used to create moduli for very large numbers that cannot + * be represented using any of the integer built-in data types. If the vector + * `res` is too small for the value `hex` most-significant digits will be + * ignored. + */ +inline void bool_vector_from_hex( std::vector& res, std::string_view hex, bool shrink_to_fit = true ) +{ + auto itR = res.begin(); + auto itS = hex.rbegin(); + + while ( itR != res.end() && itS != hex.rend() ) + { + uint32_t number{ 0 }; + if ( *itS >= '0' && *itS <= '9' ) + { + number = *itS - '0'; + } + else if ( *itS >= 'a' && *itS <= 'f' ) + { + number = *itS - 'a' + 10; + } + else if ( *itS >= 'A' && *itS <= 'F' ) + { + number = *itS - 'A' + 10; + } + else + { + assert( false && "invalid hex number" ); + } + + for ( auto i = 0u; i < 4u; ++i ) + { + *itR++ = ( number >> i ) & 1; + if ( itR == res.end() ) + { + break; + } + } + + ++itS; + } + + if ( shrink_to_fit ) + { + auto find_last = []( std::vector::const_iterator itFirst, + std::vector::const_iterator itLast, + bool value ) -> std::vector::const_iterator { + auto cur = itLast; + while ( itFirst != itLast ) + { + if ( *itFirst == value ) + { + cur = itFirst; + } + ++itFirst; + } + return cur; + }; + + const auto itLast = find_last( res.begin(), res.end(), true ); + if ( itLast == res.end() ) + { + res.clear(); + } + else + { + res.erase( find_last( res.begin(), res.end(), true ) + 1u, res.end() ); + } + } + else + { + /* in case the hex string was short, fill remaining values with false */ + std::fill( itR, res.end(), false ); + } +} + +inline void bool_vector_from_dec( std::vector& res, uint64_t value ) +{ + auto it = res.begin(); + while ( value && it != res.end() ) + { + *it++ = value % 2; + value >>= 1; + } +} + +inline uint64_t bool_vector_to_long( std::vector const& vec ) +{ + return std::accumulate( vec.begin(), vec.end(), std::make_pair( 0u, 0ul ), + []( auto accu, auto bit ) { + return std::make_pair( accu.first + 1u, accu.second + ( bit ? 1ul << accu.first : 0ul ) ); + } ) + .second; +} + +/*! \brief Creates a multiplier assuming Montgomery numbers as inputs. + * + * This modular multiplication assumes the two inputs *a* and *b* to be + * Montgomery numbers representing \f$a \cdot 2^k \bmod N\f$, where \f$N\f$ is + * the modulus as bit-string, and \f$k\f$ is the bit-width of *a* and *b*. It + * returns a signal of length *b*. The last paramaeter *NN* must be computed + * such that \f$R \cdot 2^k = N \cdot NN\f$ using the extended GCD. + */ +template +inline std::vector> montgomery_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, std::vector const& N, std::vector const& NN ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + assert( a.size() == b.size() ); + + const auto logR = a.size(); + + std::vector> Nbits( logR, ntk.get_constant( false ) ); + std::transform( N.begin(), N.end(), Nbits.begin(), [&]( auto b ) { return ntk.get_constant( b ); } ); + + /* multiply a and b and truncate to least-significant logR bits */ + auto mult1 = carry_ripple_multiplier( ntk, a, b ); + std::vector> mult1_truncated( mult1.begin(), mult1.begin() + logR ); + + /* compute (((a * b) % R) * NN) % R */ + auto mult2 = modular_constant_multiplier( ntk, mult1_truncated, NN ); + + mult2.resize( 2 * logR, ntk.get_constant( false ) ); + auto Ncopy = N; + Ncopy.resize( 2 * logR, false ); + auto summand = modular_constant_multiplier( ntk, mult2, Ncopy ); + + assert( mult1.size() == 2 * logR ); + assert( summand.size() == 2 * logR ); + + auto carry = ntk.get_constant( false ); + carry_ripple_adder_inplace( ntk, mult1, summand, carry ); + mult1.erase( mult1.begin(), mult1.begin() + logR ); + + auto tcopy = mult1; + carry = ntk.get_constant( true ); + carry_ripple_subtractor_inplace( ntk, tcopy, Nbits, carry ); + mux_inplace( ntk, carry, tcopy, mult1 ); + + return tcopy; +} + +/*! \brief Creates a multiplier assuming Montgomery numbers as inputs. + * + * This modular multiplication assumes the two inputs *a* and *b* to be + * Montgomery numbers representing \f$a \cdot 2^k \bmod N\f$, where \f$N\f$ is + * the modulus, and \f$k\f$ is the bit-width of *a* and *b*. It returns a + * signal of length *b*. + */ +template +inline std::vector> montgomery_multiplication( Ntk& ntk, std::vector> const& a, std::vector> const& b, uint64_t N ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + + const auto logR = a.size(); + const auto R = 1 << logR; + + // egcd + int64_t s = 0, old_s = 1, r = R, old_r = N; + while ( r ) + { + const auto q = old_r / r; + std::tie( old_r, r ) = std::pair{ r, old_r - q * r }; + std::tie( old_s, s ) = std::pair{ s, old_s - q * s }; + } + const auto NN = std::abs( old_s ); + + std::vector Nvec( logR ), NNvec( logR ); + for ( auto i = 0u; i < logR; ++i ) + { + Nvec[i] = static_cast( ( N >> i ) & 1 ); + NNvec[i] = static_cast( ( NN >> i ) & 1 ); + } + + // std::cout << fmt::format( "[i] R = {}, NN = {}, N = {}\n", R, NN, N ); + + return montgomery_multiplication( ntk, a, b, Nvec, NNvec ); +} + +} // namespace mockturtle diff --git a/include/mockturtle/generators/random_network.hpp b/include/mockturtle/generators/random_network.hpp new file mode 100644 index 0000000..a0c8ddf --- /dev/null +++ b/include/mockturtle/generators/random_network.hpp @@ -0,0 +1,672 @@ +/* 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 random_network.hpp + \brief Generate random logic networks + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/mig.hpp" +#include "../networks/xag.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * generates networks according to the specified number of PIs + * and number of gates. After generating primary inputs and gates, + * all nodes with no fanout become primary outputs. After generating + * `num_networks_per_configuration` networks (i.e. `generate` being + * called this number of times), the configuration (numbers of PIs + * and gates) will be incremented by `num_pis_increment` and + * `num_gates_increment`, respectively. + */ +struct random_network_generator_params_size +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks of each configuration to generate + * before increasing size. */ + uint32_t num_networks_per_configuration{ 100u }; + + /*! \brief Number of PIs to start with. */ + uint32_t num_pis{ 4u }; + + /*! \brief Number of gates to start with. */ + uint32_t num_gates{ 10u }; + + /*! \brief Number of PIs to increment at each step. */ + uint32_t num_pis_increment{ 0u }; + + /*! \brief Number of gates to increment at each step. */ + uint32_t num_gates_increment{ 0u }; +}; /* random_network_generator_params_size */ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * first enumerates all non-isomorphic connected partial DAGs of + * `num_gates` vertices, randomly shuffles them, and then generates + * `num_networks_per_configuration` random networks of each topology. + * It is guaranteed that all possible topologies are generated for + * the same number of times, provided that `generate` is called for + * enough times. After all topologies have been generated, `num_gates` + * is increased by 1 and the above steps are repeated. + * + * This method currently only supports generating 2-regular (i.e., + * each gate has two fanins), single-output DAGs. + * + * Guideline on how many iterations are needed to visit all topologies: + * - `num_gates` = 2: 1 topology + * - `num_gates` = 3: 3 topologies + * - `num_gates` = 4: 10 topologies + * - `num_gates` = 5: 49 topologies + * - `num_gates` = 6: 302 topologies + * - `num_gates` = 7: 2312 topologies + * - `num_gates` = 8: 21218 topologies + * - `num_gates` = 9: 228249 topologies + * + * For example, starting from `num_gates` = 3 and using + * `num_networks_per_configuration` = 100, to generate networks + * of all topologies with no more than 5 vertices, `generate` + * should be called (3 + 10 + 49) * 100 = 6200 times. + * + * For each topology, a random network is concretized from the partial + * DAG by randomly choosing a (#PIs/#inputs of DAG) ratio and allocating + * the corresponding number of PIs, randomly choosing a PI to be connected + * to each input of the partial DAG, and randomly deciding if each edge + * should be complemented. + */ +struct random_network_generator_params_topology +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks to generate for each topology. */ + uint32_t num_networks_per_configuration{ 100u }; + + /*! \brief Number of gates to start with. */ + uint32_t num_gates{ 3u }; + + /*! \brief Minimum ratio of (#PIs/#inputs of DAG). + * Lower ratio makes more reconvergences. */ + float min_PI_ratio{ 0.5 }; + + /*! \brief Maximum ratio of (#PIs/#inputs of DAG). + * Higher ratio makes it more likely to sample a full tree. */ + float max_PI_ratio{ 1.0 }; +}; /* random_network_generator_params_topology */ + +/*! \brief Parameters for random_network_generator. + * + * When this parameter object is used, random_network_generator + * first enumerates all non-isomorphic connected partial DAGs with + * numbers of vertices between `min_num_gates_component` and + * `max_num_gates_component`. Random networks are generated by + * randomly generating `num_components` DAGs of randomly-sampled + * topologies (repeated topologies are allowed). The first topology + * is concretized by connecting inputs to randomly-chosen PIs, and + * the remaining ones are concretized by connecting inputs to PIs + * or nodes in the previous components. + * + * After generating `num_networks_per_configuration` networks, + * `num_components` is increased by `num_components_increment` + * and `num_pis` is increased by `num_pis_increment`. + */ +struct random_network_generator_params_composed +{ + /*! \brief Seed of the random generator. */ + uint64_t seed{ 0xcafeaffe }; + + /*! \brief Number of networks to generate for each topology. */ + uint32_t num_networks_per_configuration{ 1000u }; + + /*! \brief Minimum number of gates of the components. */ + uint32_t min_num_gates_component{ 3u }; + + /*! \brief Maximum number of gates of the components. */ + uint32_t max_num_gates_component{ 5u }; + + /*! \brief Number of components to start with. */ + uint32_t num_components{ 2u }; + + /*! \brief Number of PIs to start with. */ + uint32_t num_pis{ 4u }; + + /*! \brief Number of components to increment at each step. */ + uint32_t num_components_increment{ 1u }; + + /*! \brief Number of PIs to increment at each step. */ + uint32_t num_pis_increment{ 2u }; +}; /* random_network_generator_params_composed */ + +namespace detail +{ + +template +struct create_gate_rule +{ + using signal = typename Ntk::signal; + + std::function const& )> func; + uint32_t num_args; +}; + +} /* namespace detail */ + +/*! \brief Generates random logic networks + * + * Generate random logic networks with certain parameters. + * + * The constructor takes a vector of construction rules, which are + * used in the algorithm to build the logic network. The constructor + * also takes a parameter object, which can be of various types and + * influences the way networks are generated. + * + * The function `generate` returns a random network and can be called + * repeatedly, each time generating a different network. + * + */ +template +class random_network_generator +{ +}; + +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_size; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_pis( ps.num_pis ), _num_gates( ps.num_gates ) + { + } + + Ntk generate() + { + assert( _num_pis > 0 ); + assert( _num_gates > 0 ); + + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < _num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + std::uniform_int_distribution rule_dist( 0, static_cast( _gens.size() - 1u ) ); + + auto gate_counter = ntk.num_gates(); + while ( gate_counter < _num_gates ) + { + auto const r = _gens.at( rule_dist( _rng ) ); + + std::uniform_int_distribution dist( 0, static_cast( fs.size() - 1 ) ); + std::vector args; + for ( auto i = 0u; i < r.num_args; ++i ) + { + auto const a_compl = dist( _rng ) & 1; + auto const a = fs.at( dist( _rng ) ); + args.emplace_back( a_compl ? !a : a ); + } + + auto const g = r.func( ntk, args ); + if ( ntk.num_gates() > gate_counter ) + { + fs.emplace_back( g ); + ++gate_counter; + } + + assert( ntk.num_gates() == gate_counter ); + } + + /* generate pos */ + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + assert( ntk.num_pis() == _num_pis ); + assert( ntk.num_gates() == _num_gates ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + _num_gates += _ps.num_gates_increment; + _num_pis += _ps.num_pis_increment; + } + + return ntk; + } + +private: + uint32_t _counter; + uint32_t _num_pis; + uint32_t _num_gates; +}; /* random_network_generator */ + +#ifdef ENABLE_NAUTY +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_topology; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_gates( ps.num_gates ), _ith_dag( 0u ), + _rule_dist( 0, _gens.size() - 1u ) + { + } + + Ntk generate() + { + if ( _counter == 0 ) + { + if ( _ith_dag == 0 ) + { + prepare_partial_dags(); + } + auto num_inputs = _dags.at( _ith_dag ).nr_pi_fanins(); + uint32_t min_num_pis = std::ceil( _ps.min_PI_ratio * num_inputs ); + uint32_t max_num_pis = std::ceil( _ps.max_PI_ratio * num_inputs ); + _num_pis_dist = std::uniform_int_distribution( min_num_pis, max_num_pis ); + } + + percy::partial_dag const& pd = _dags.at( _ith_dag ); + uint32_t num_pis = _num_pis_dist( _rng ); + std::uniform_int_distribution pis_dist( 1u, num_pis ); + + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + pd.foreach_vertex( [&]( auto const& v, auto i ) { + uint32_t size_before = ntk.num_gates(); + signal g; + + do + { + auto const r = _gens.at( _rule_dist( _rng ) ); + std::vector args; + + for ( auto fi : v ) + { + bool const inv = _rng() & 1; + if ( fi == percy::FANIN_PI ) + { + auto const& a = fs.at( pis_dist( _rng ) ); + args.emplace_back( inv ? !a : a ); + } + else + { + assert( fi >= 1 && num_pis + fi < fs.size() ); + auto const& a = fs.at( num_pis + fi ); + args.emplace_back( inv ? !a : a ); + } + } + + g = r.func( ntk, args ); + } while ( ntk.num_gates() == size_before ); + + fs.emplace_back( g ); + } ); + + /* generate pos */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + if ( ++_ith_dag >= _dags.size() ) + { + _ith_dag = 0; + ++_num_gates; + } + } + + return ntk; + } + +private: + void prepare_partial_dags() + { + using namespace percy; + + _dags.clear(); + partial_dag g; + partial_dag_generator gen( _num_gates ); + std::set> can_reprs; + pd_iso_checker checker( _num_gates ); + + gen.set_callback( [&]( partial_dag_generator* gen ) { + for ( int i = 0; i < gen->nr_vertices(); i++ ) + { + g.set_vertex( i, gen->_js[i], gen->_ks[i] ); + } + const auto can_repr = checker.crepr( g ); + const auto res = can_reprs.insert( can_repr ); + if ( res.second ) + _dags.push_back( g ); + } ); + gen.gen_type( partial_gen_type::GEN_COLEX ); + g.reset( 2, _num_gates ); + gen.count_dags(); + + std::shuffle( _dags.begin(), _dags.end(), _rng ); + } + +private: + uint32_t _counter; + uint32_t _num_gates; + std::vector _dags; + uint32_t _ith_dag; + std::uniform_int_distribution _num_pis_dist, _rule_dist; +}; /* random_network_generator */ + +template +class random_network_generator +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using rand_engine_t = std::mt19937; + using rules_t = std::vector>; + using params_t = random_network_generator_params_composed; + +private: /* common data members */ + rules_t const _gens; + params_t const _ps; + rand_engine_t _rng; + +public: + explicit random_network_generator( rules_t const& gens, params_t ps = {} ) + : _gens( gens ), _ps( ps ), _rng( static_cast( ps.seed ) ), + _counter( 0u ), _num_components( ps.num_components ), _num_pis( ps.num_pis ), + _rule_dist( 0, _gens.size() - 1u ) + { + prepare_partial_dags(); + _dag_dist = std::uniform_int_distribution( 0, _dags.size() - 1u ); + } + + Ntk generate() + { + std::vector fs; + Ntk ntk; + + /* generate constant */ + fs.emplace_back( ntk.get_constant( false ) ); + + /* generate pis */ + for ( auto i = 0u; i < _num_pis; ++i ) + { + fs.emplace_back( ntk.create_pi() ); + } + + /* generate gates */ + for ( auto i = 0u; i < _num_components; ++i ) + { + concretize( ntk, _dags.at( _dag_dist( _rng ) ), fs ); + // concretize( ntk, _dags.at( 3 ), fs ); // [3] in 4-gate topologies: diamond + } + + /* generate pos */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( ntk.fanout_size( n ) == 0u ) + { + ntk.create_po( ntk.make_signal( n ) ); + } + } ); + + if ( ++_counter >= _ps.num_networks_per_configuration ) + { + _counter = 0; + _num_components += _ps.num_components_increment; + _num_pis += _ps.num_pis_increment; + } + + return ntk; + } + +private: + void concretize( Ntk& ntk, percy::partial_dag& pd, std::vector& fs ) + { + uint32_t num_existing = ntk.size() - 1u; + std::uniform_int_distribution dist( 1u, num_existing ); + // std::uniform_int_distribution dist( 1u, _num_pis ); + + pd.foreach_vertex( [&]( auto const& v, auto i ) { + uint32_t size_before = ntk.num_gates(); + signal g; + + do + { + auto const r = _gens.at( _rule_dist( _rng ) ); + std::vector args; + + for ( auto fi : v ) + { + bool const inv = _rng() & 1; + if ( fi == percy::FANIN_PI ) + { + auto const& a = fs.at( dist( _rng ) ); + args.emplace_back( inv ? !a : a ); + } + else + { + assert( fi >= 1 && num_existing + fi < fs.size() ); + auto const& a = fs.at( num_existing + fi ); + args.emplace_back( inv ? !a : a ); + } + } + + g = r.func( ntk, args ); + } while ( ntk.num_gates() == size_before ); + + fs.emplace_back( g ); + assert( ntk.size() == fs.size() ); + } ); + } + + void prepare_partial_dags() + { + for ( auto num = _ps.min_num_gates_component; num <= _ps.max_num_gates_component; ++num ) + { + prepare_partial_dags( num ); + } + } + + void prepare_partial_dags( uint32_t num_gates ) + { + using namespace percy; + + partial_dag g; + partial_dag_generator gen( num_gates ); + std::set> can_reprs; + pd_iso_checker checker( num_gates ); + + gen.set_callback( [&]( partial_dag_generator* gen ) { + for ( int i = 0; i < gen->nr_vertices(); i++ ) + { + g.set_vertex( i, gen->_js[i], gen->_ks[i] ); + } + const auto can_repr = checker.crepr( g ); + const auto res = can_reprs.insert( can_repr ); + if ( res.second ) + _dags.push_back( g ); + } ); + gen.gen_type( partial_gen_type::GEN_COLEX ); + g.reset( 2, num_gates ); + gen.count_dags(); + } + +private: + uint32_t _counter; + uint32_t _num_components, _num_pis; + std::vector _dags; + std::uniform_int_distribution _rule_dist, _dag_dist; +}; /* random_network_generator */ +#endif + +/*! \brief Generates a random AIG network */ +template +auto random_aig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( aig_network& aig, std::vector const& vs ) -> aig_network::signal { + assert( vs.size() == 2u ); + return aig.create_and( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random XAG network */ +template +auto random_xag_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( xag_network& xag, std::vector const& vs ) -> xag_network::signal { + assert( vs.size() == 2u ); + return xag.create_and( vs[0], vs[1] ); + }, + 2u } ); + rules.emplace_back( rule_t{ []( xag_network& xag, std::vector const& vs ) -> xag_network::signal { + assert( vs.size() == 2u ); + return xag.create_xor( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random MIG network */ +template +auto random_mig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 3u ); + return mig.create_maj( vs[0], vs[1], vs[2] ); + }, + 3u } ); + + return gen_t( rules, ps ); +} + +/*! \brief Generates a random MIG network MAJ-, AND-, and OR-gates */ +template +auto mixed_random_mig_generator( GenParams ps = {} ) +{ + using gen_t = random_network_generator; + using rule_t = typename detail::create_gate_rule; + + std::vector rules; + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 3u ); + return mig.create_maj( vs[0], vs[1], vs[2] ); + }, + 3u } ); + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 2u ); + return mig.create_and( vs[0], vs[1] ); + }, + 2u } ); + rules.emplace_back( rule_t{ []( mig_network& mig, std::vector const& vs ) -> mig_network::signal { + assert( vs.size() == 2u ); + return mig.create_or( vs[0], vs[1] ); + }, + 2u } ); + + return gen_t( rules, ps ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/generators/self_dualize.hpp b/include/mockturtle/generators/self_dualize.hpp new file mode 100644 index 0000000..837df18 --- /dev/null +++ b/include/mockturtle/generators/self_dualize.hpp @@ -0,0 +1,129 @@ +/* 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 self_dualize.hpp + \brief Self-dualize a logic network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#include "../algorithms/reconv_cut.hpp" +#include "../networks/aig.hpp" +#include "../views/cut_view.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Generates a self-dual AIG + * + * Generates the self-dualization of a multi-output logic network N. + * The algorithm iterates over the output functions f0, ..., fm of N + * and computes self-dualized function gi for 0 <= i <= m defined by + * the formula + * + * gi(x0, x1, ..., xn) + * = (x0 * fi(x1, ..., xn)) + (!x0 * !fi(!x1, ..., !xn)). + * + */ +inline aig_network self_dualize_aig( aig_network const& src_aig ) +{ + using node = node; + using signal = signal; + + aig_network dest_aig; + std::unordered_map node_to_signal_one; + std::unordered_map node_to_signal_two; + + /* copy inputs */ + node_to_signal_one[0] = dest_aig.get_constant( false ); + node_to_signal_two[0] = dest_aig.get_constant( false ); + src_aig.foreach_pi( [&]( const auto& n ) { + auto const pi = dest_aig.create_pi(); + node_to_signal_one[n] = pi; + node_to_signal_two[n] = !pi; + } ); + + reconvergence_driven_cut_parameters ps; + ps.max_leaves = 99999999u; + reconvergence_driven_cut_statistics st; + detail::reconvergence_driven_cut_impl cut_generator( src_aig, ps, st ); + + src_aig.foreach_po( [&]( const auto& f ) { + auto leaves = cut_generator.run( { src_aig.get_node( f ) } ).first; + std::stable_sort( std::begin( leaves ), std::end( leaves ) ); + + /* check if all leaves are pis */ + for ( const auto& l : leaves ) + { + (void)l; + assert( src_aig.is_pi( l ) ); + } + + cut_view view( src_aig, leaves, f ); + topo_view topo_view( view ); + + /* create cone once */ + topo_view.foreach_gate( [&]( const auto& g ) { + std::vector new_fanins; + topo_view.foreach_fanin( g, [&]( const auto& fi ) { + auto const n = topo_view.get_node( fi ); + new_fanins.emplace_back( topo_view.is_complemented( fi ) ? !node_to_signal_one[n] : node_to_signal_one[n] ); + } ); + + assert( new_fanins.size() == 2u ); + node_to_signal_one[g] = dest_aig.create_and( new_fanins[0u], new_fanins[1u] ); + } ); + + /* create cone once */ + topo_view.foreach_gate( [&]( const auto& g ) { + std::vector new_fanins; + topo_view.foreach_fanin( g, [&]( const auto& fi ) { + auto const n = topo_view.get_node( fi ); + new_fanins.emplace_back( topo_view.is_complemented( fi ) ? !node_to_signal_two[n] : node_to_signal_two[n] ); + } ); + + assert( new_fanins.size() == 2u ); + node_to_signal_two[g] = dest_aig.create_and( new_fanins[0u], new_fanins[1u] ); + } ); + + auto const output_signal_one = topo_view.is_complemented( f ) ? !node_to_signal_one[topo_view.get_node( f )] : node_to_signal_one[topo_view.get_node( f )]; + auto const output_signal_two = topo_view.is_complemented( f ) ? !node_to_signal_two[topo_view.get_node( f )] : node_to_signal_two[topo_view.get_node( f )]; + + auto const new_pi = dest_aig.create_pi(); + auto const output = dest_aig.create_or( dest_aig.create_and( new_pi, output_signal_one ), dest_aig.create_and( !new_pi, !output_signal_two ) ); + dest_aig.create_po( output ); + } ); + + return dest_aig; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/generators/sorting.hpp b/include/mockturtle/generators/sorting.hpp new file mode 100644 index 0000000..cf68be9 --- /dev/null +++ b/include/mockturtle/generators/sorting.hpp @@ -0,0 +1,148 @@ +/* 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 sorting.hpp + \brief Generate sorting networks + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle +{ + +/*! \brief Generates sorting network based on bubble sort. + * + * The functor is called for every comparator in the network. The arguments + * to the functor are two integers that define on which lines the comparator + * acts. + * + * \param n Number of elements to sort + * \param compare_fn Functor + */ +template +void bubble_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n <= 1 ) + { + return; + } + for ( auto c = n - 1; c >= 1; --c ) + { + for ( auto j = 0u; j < c; ++j ) + { + compare_fn( j, j + 1 ); + } + } +} + +/*! \brief Generates sorting network based on insertion sort. + * + * The functor is called for every comparator in the network. The arguments + * to the functor are two integers that define on which lines the comparator + * acts. + * + * \param n Number of elements to sort + * \param compare_fn Functor + */ +template +void insertion_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n <= 1 ) + { + return; + } + for ( auto c = 1u; c < n; ++c ) + { + for ( int j = c - 1; j >= 0; --j ) + { + compare_fn( j, j + 1 ); + } + } +} + +namespace detail +{ + +template +void batcher_merge( std::vector const& list, Fn&& compare_fn ) +{ + if ( list.size() == 2u ) + { + compare_fn( list[0], list[1] ); + return; + } + + std::vector even, odd; + for ( auto i = 0u; i < list.size(); i += 2 ) + { + even.push_back( list[i] ); + odd.push_back( list[i + 1] ); + } + + batcher_merge( even, compare_fn ); + batcher_merge( odd, compare_fn ); + + for ( auto i = 1u; i < list.size() - 2; i += 2 ) + { + compare_fn( list[i], list[i + 1] ); + } +} + +template +void batcher_sort( uint32_t begin, uint32_t end, Fn&& compare_fn ) +{ + const auto size = end - begin; + if ( size == 2u ) + { + compare_fn( begin, begin + 1 ); + return; + } + + batcher_sort( begin, begin + size / 2, compare_fn ); + batcher_sort( begin + size / 2, end, compare_fn ); + + std::vector list( size ); + std::iota( list.begin(), list.end(), begin ); + batcher_merge( list, compare_fn ); +} + +} // namespace detail + +template +void batcher_sorting_network( uint32_t n, Fn&& compare_fn ) +{ + if ( n < 2 ) + return; + detail::batcher_sort( 0u, n, compare_fn ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/interface.hpp b/include/mockturtle/interface.hpp new file mode 100644 index 0000000..ec012fd --- /dev/null +++ b/include/mockturtle/interface.hpp @@ -0,0 +1,768 @@ +/* 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 interface.hpp + \brief Documentation of network interfaces + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "networks/events.hpp" +#include "traits.hpp" + +namespace mockturtle +{ + +static_assert( false, "file interface.hpp cannot be included, it's only used for documentation" ); + +class network final +{ +public: + /*! \brief Type referring to itself. + * + * The ``base_type`` is the network type itself. It is required, because + * views may extend networks, and this type provides a way to determine the + * underlying network type. + */ + using base_type = network; + + /*! \brief Type representing a node. + * + * A ``node`` is a node in the logic network. It could be a constant, a + * primary input or a logic gate. + */ + struct node + { + }; + + /*! \brief Type representing a signal. + * + * A ``signal`` can be seen as a pointer to a node, or an outgoing edge of + * a node towards its fanout. Depending on the kind of logic network, it + * may carry additional information such as a complement attribute. + */ + struct signal + { + }; + + /*! \brief Type representing the storage. + * + * A ``storage`` is some container that can contain all data necessary to + * store the logic network. It can be constructed outside of the logic network + * and passed as a reference to the constructor. It may be shared among + * several logic networks. A `std::shared_ptr` is a convenient data + * structure to hold a storage in a logic network. + */ + struct storage + { + }; + + /*! \brief Default constructor. + * + * Constructs an empty network. + */ + network(); + + /*! \brief Constructor taking a storage. */ + explicit network( storage s ); + + /*! \brief Default copy assignment operator. + * + * Currently, most network implementations in mockturtle use `std::shared_ptr` + * to hold and share the storage. Thus, the default behavior of copy-assigning + * a network only copies the pointer, but not really duplicating the contents + * in the storage data structure. In other words, it makes a shallow copy + * by default. + */ + network& operator=( const network& other ) = default; + + /*! \brief Explicitly duplicate a network. + * + * Deep copy a network by duplicating the storage. Note that this method + * does not duplicate the network events. + */ + network clone(); + +#pragma region Primary I / O and constants + /*! \brief Gets constant value represented by network. + * + * A constant node is the only node that must be created when initializing + * the network. For this reason, this method has constant access and is not + * called `create_constant`. + * + * \param value Constant value + */ + signal get_constant( bool value ) const; + + /*! \brief Creates a primary input in the network. + * + * Each created primary input is stored in a node and contributes to the size + * of the network. + */ + signal create_pi(); + + /*! \brief Creates a primary output in the network. + * + * A primary output is not stored in terms of a node, and it also does not + * contribute to the size of the network. A primary output is created for a + * signal in the network and it is possible that multiple primary outputs + * point to the same signal. + * + * \param s Signal that drives the created primary output + */ + void create_po( signal const& s ); + + /*! \brief Checks whether a node is a constant node. */ + bool is_constant( node const& n ) const; + + /*! \brief Checks whether a node is a primary input. */ + bool is_pi( node const& n ) const; + + /*! \brief Checks whether a node is a combinational input. + * + * This method should be effectively the same as ``is_pi`` in a + * combinational network. + */ + bool is_ci( node const& n ) const; + + /*! \brief Gets the Boolean value of the constant node. + * + * The method expects that `n` is a constant node. + */ + bool constant_value( node const& n ) const; +#pragma endregion + +#pragma region Create unary functions + /*! \brief Creates signal that computes ``f``. + * + * This method is not required to create a gate in the network. A network + * implementation can also just return ``f``. + * + * \param f Child signal + */ + signal create_buf( signal const& f ); + + /*! \brief Creates a signal that inverts ``f``. + * + * This method is not required to create a gate in the network. If a network + * supports complemented attributes on signals, it can just return the + * complemented signal ``f``. + * + * \param f Child signal + */ + signal create_not( signal const& f ); +#pragma endregion + +#pragma region Create binary functions + /*! \brief Creates a signal that computes the binary AND. */ + signal create_and( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary NAND. */ + signal create_nand( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary OR. */ + signal create_or( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary NOR. */ + signal create_nor( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary less-than. + * + * The signal is true if and only if ``f`` is 0 and ``g`` is 1. + */ + signal create_lt( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary less-than-or-equal. + * + * The signal is true if and only if ``f`` is 0 or ``g`` is 1. + */ + signal create_le( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary greater-than. + * + * The signal is true if and only if ``f`` is 1 and ``g`` is 0. + */ + signal create_gt( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary greater-than-or-equal. + * + * The signal is true if and only if ``f`` is 1 or ``g`` is 0. + */ + signal create_ge( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary XOR. */ + signal create_xor( signal const& f, signal const& g ); + + /*! \brief Creates a signal that computes the binary XNOR. */ + signal create_xnor( signal const& f, signal const& g ); +#pragma endregion + +#pragma region Create ternary functions + /*! \brief Creates a signal that computes the majority-of-3. */ + signal create_maj( signal const& f, signal const& g, signal const& h ); + + /*! \brief Creates a signal that computes the if-then-else operation. + * + * \param cond Condition for ITE operator + * \param f_then Then-case for ITE operator + * \param f_else Else-case for ITE operator + */ + signal create_ite( signal const& cond, signal const& f_then, signal const& f_else ); + + /*! \brief Creates a signal that computes the ternary XOR operation. */ + signal create_xor3( signal const& a, signal const& b, signal const& c ); +#pragma endregion + +#pragma region Create nary functions + /*! \brief Creates a signal that computes the n-ary AND. + * + * If `fs` is empty, it returns constant-1. + */ + signal create_nary_and( std::vector const& fs ); + + /*! \brief Creates a signal that computes the n-ary OR. + * + * If `fs` is empty, it returns constant-0. + */ + signal create_nary_or( std::vector const& fs ); + + /*! \brief Creates a signal that computes the n-ary XOR. + * + * If `fs` is empty, it returns constant-0. + */ + signal create_nary_xor( std::vector const& fs ); +#pragma endregion + +#pragma region Create arbitrary functions + /*! \brief Creates node with arbitrary function. + * + * The number of variables in ``function`` must match the number of fanin + * signals in ``fanin``. ``fanin[0]`` will correspond to the + * least-significant variable in ``function``. + * + * \param fanin Fan-in signals + * \param function Truth table for node function + */ + signal create_node( std::vector const& fanin, kitty::dynamic_truth_table const& function ); + + /*! \brief Clones a node from another network of same type. + * + * This method can clone a node from a different network ``other``, which is + * from the same type. The node ``source`` is a node in the source network + * ``other``, but the signals in ``fanin`` refer to signals in the target + * network, which are assumed to be in the same order as in the source + * network. + * + * \param other Other network of same type + * \param source Node in ``other`` + * \param children Fan-in signals from the current network + * \return New signal representing node in current network + */ + signal clone_node( network const& other, node const& source, std::vector const& fanin ); +#pragma endregion + +#pragma region Restructuring + /*! \brief Replaces one node in a network by another signal. + * + * This method causes all nodes that have ``old_node`` as fanin to have + * `new_signal` as fanin instead. In doing so, a possible polarity of + * `new_signal` is taken into account. Afterwards, the fan-out count of + * ``old_node`` is guaranteed to be 0. + * + * It does not update custom values or visited flags of a node. + * + * \param old_node Node to replace + * \param new_signal Signal to replace ``old_node`` with + */ + void substitute_node( node const& old_node, signal const& new_signal ); + + /*! \brief Perform multiple node-signal replacements in a network. + * + * This method replaces all occurrences of a node with a signal for + * all pairs (node, signal) in the substitution list. + * + * \param substitutions A list of (node, signal) replacement pairs + */ + void substitute_nodes( std::list> substitutions ); + + /*! \brief Replaces a child node by a new signal in a node. + * + * If ``n`` has a child pointing to ``old_node``, then it will be replaced by + * ``new_signal``. If the replacement catches a trivial case, e.g., ``n`` + * becomes a constant, then this will be returned as an optional replacement + * candidate by the function. + * + * The function updates the hash table. If no trivial case was found, it + * updates the hash table according to the new structure of ``n``. + * + * \param n Node which may have ``old_node`` as a child + * \param old_node Child to be replaced + * \param new_signal Signel to replace ``old_node`` with + * \return May return new recursive replacement candidate + */ + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ); + + /*! \brief Replaces a output driver by a new signal. + * + * If ``old_node`` is drive to some output, then it will be replaced by + * ``new_signal``. + * + * \param old_node Driver to be replaced + * \param new_signal Signal replace ``old_node`` with + */ + void replace_in_outputs( node const& old_node, signal const& new_signal ); + + /*! \brief Removes a node (and potentially its fanins) from the hash table. + * + * The node will be marked dead. This status can be checked with + * ``is_dead``. Taking out a node does not change the indexes of + * other nodes. The node will be removed from the hash table. + * The reference counters of all fanin will be decremented and + * ``take_out_node`` will be recursively invoked on all fanins + * if their fanout count reach 0. + * + * \param n Node to be removed + */ + void take_out_node( node const& n ); + + /*! \brief Check if a node is dead. + * + * A dead node is no longer visited in the ``foreach_node`` and + * ``foreach_gate`` methods. It still contributes to the overall + * ``size`` of the network, but ``num_gates`` does not take dead + * nodes into account. + * + * \param n Node to check + * \return Whether ``n`` is dead + */ + bool is_dead( node const& n ) const; +#pragma endregion + +#pragma region Structural properties + /*! \brief Checks whether the network is combinational. */ + bool is_combinational() const; + + /*! \brief Returns the number of nodes (incl. constants and PIs and dead nodes). */ + uint32_t size() const; + + /*! \brief Returns the number of primary inputs. */ + uint32_t num_pis() const; + + /*! \brief Returns the number of primary outputs. */ + uint32_t num_pos() const; + + /*! \brief Returns the number of combinational inputs. + * + * This method should be effectively the same as `num_pis`` in a + * combinational network. + */ + uint32_t num_cis() const; + + /*! \brief Returns the number of combinational outputs. + * + * This method should be effectively the same as `num_pos`` in a + * combinational network. + */ + uint32_t num_cos() const; + + /*! \brief Returns the number of gates (without dead nodes) */ + uint32_t num_gates() const; + + /*! \brief Returns the fanin size of a node. */ + uint32_t fanin_size( node const& n ) const; + + /*! \brief Returns the fanout size of a node. */ + uint32_t fanout_size( node const& n ) const; + + /*! \brief Increments fanout size and returns old value. + * + * This is useful for ref-counting based algorithm. The user of this function + * should make sure to bring the value back to a consistent state. + */ + uint32_t incr_fanout_size( node const& n ) const; + + /*! \brief Decrements fanout size and returns new value. + * + * This is useful for ref-counting based algorithm. The user of this function + * should make sure to bring the value back to a consistent state. + */ + uint32_t decr_fanout_size( node const& n ) const; + + /*! \brief Returns the length of the critical path. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `depth_view`. + */ + uint32_t depth() const; + + /*! \brief Returns the level of a node. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `depth_view`. + */ + uint32_t level( node const& n ) const; + + /*! \brief Returns true if node is a 2-input AND gate. */ + bool is_and( node const& n ) const; + + /*! \brief Returns true if node is a 2-input OR gate. */ + bool is_or( node const& n ) const; + + /*! \brief Returns true if node is a 2-input XOR gate. */ + bool is_xor( node const& n ) const; + + /*! \brief Returns true if node is a majority-of-3 gate. */ + bool is_maj( node const& n ) const; + + /*! \brief Returns true if node is a if-then-else gate. */ + bool is_ite( node const& n ) const; + + /*! \brief Returns true if node is a 3-input XOR gate. */ + bool is_xor3( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary AND gate. */ + bool is_nary_and( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary OR gate. */ + bool is_nary_or( node const& n ) const; + + /*! \brief Returns true if node is a primitive n-ary XOR gate. */ + bool is_nary_xor( node const& n ) const; + + /*! \brief Returns true if node is a general function node. */ + bool is_function( node const& n ) const; +#pragma endregion + +#pragma region Functional properties + /*! \brief Returns the gate function of a node. + * + * Note that this function returns the gate function represented by a node + * in terms of the *intended* gate. For example, in an AIG, all gate + * functions are AND, complemented edges are not taken into account. Also, + * in an MIG, all gate functions are MAJ, independently of complemented edges + * and possible constant inputs. + * + * In order to retrieve a function with respect to complemented edges one can + * use the `compute` function with a truth table as simulation value. + */ + kitty::dynamic_truth_table node_function( node const& n ) const; +#pragma endregion + +#pragma region Nodes and signals + /*! \brief Get the node a signal is pointing to. */ + node get_node( signal const& f ) const; + + /*! \brief Create a signal from a node (without edge attributes). */ + signal make_signal( node const& n ) const; + + /*! \brief Check whether a signal is complemented. + * + * This method may also be provided by network implementations that do not + * have complemented edges. In this case, the method simply returns + * ``false`` for each node. + */ + bool is_complemented( signal const& f ) const; + + /*! \brief Returns the index of a node. + * + * The index of a node must be a unique for each node and must be between 0 + * (inclusive) and the size of a network (exclusive, value returned by + * ``size()``). + */ + uint32_t node_to_index( node const& n ) const; + + /*! \brief Returns the node for an index. + * + * This is the inverse function to ``node_to_index``. + * + * \param index A value between 0 (inclusive) and the size of the network + * (exclusive) + */ + node index_to_node( uint32_t index ) const; + + /*! \brief Returns the primary input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * primary inputs (exclusive). + */ + node pi_at( uint32_t index ) const; + + /*! \brief Returns the primary output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * primary outputs (exclusive). + */ + signal po_at( uint32_t index ) const; + + /*! \brief Returns the combinational input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational inputs (exclusive). + */ + node ci_at( uint32_t index ) const; + + /*! \brief Returns the combinational output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational outputs (exclusive). + */ + signal co_at( uint32_t index ) const; + + /*! \brief Returns the index of a primary input node. + * + * \param n A primary input node. + * \return A value between 0 and num_pis()-1. + */ + uint32_t pi_index( node const& n ) const; + + /*! \brief Returns the index of a primary output signal. + * + * \param n A primary output signal. + * \return A value between 0 and num_pos()-1. + */ + uint32_t po_index( signal const& n ) const; + + /*! \brief Returns the index of a combinational input node. + * + * \param n A combinational input node. + * \return A value between 0 and num_cis()-1. + */ + uint32_t ci_index( node const& n ) const; + + /*! \brief Returns the index of a combinational output signal. + * + * \param n A combinational output signal. + * \return A value between 0 and num_cos()-1. + */ + uint32_t co_index( signal const& n ) const; +#pragma endregion + +#pragma region Node and signal iterators + /*! \brief Calls ``fn`` on every node in network. + * + * The order of nodes depends on the implementation and must not guarantee + * topological order. The parameter ``fn`` is any callable that must have + * one of the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_node( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every gate node in the network. + * + * Calls each node that is not constant and not a combinational input. The + * parameter ``fn`` is any callable that must have one of the following four + * signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_gate( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every primary input node in the network. + * + * The order is in the same order as primary inputs have been created with + * ``create_pi``. The parameter ``fn`` is any callable that must have one of + * the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_pi( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every primary output signal in the network. + * + * The order is in the same order as primary outputs have been created with + * ``create_po``. The function is called on the signal that is driving the + * output and may occur more than once in the iteration, if it drives more + * than one output. The parameter ``fn`` is any callable that must have one + * of the following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_po( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational input node in the network. + * + * This method should be effectively the same as ``foreach_pi`` in a + * combinational network. + */ + template + void foreach_ci( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational output signal in the network. + * + * This method should be effectively the same as ``foreach_po`` in a + * combinational network. + */ + template + void foreach_co( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every fanin of a node. + * + * The order of the fanins is in the same order that was used to create the + * node. The parameter ``fn`` is any callable that must have one of the + * following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_fanin( node const& n, Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every fanout of a node. + * + * The method gives no guarantee on the order of the fanout. The parameter + * ``fn`` is any callable that must have one of the following signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + * + * For efficiency reasons, this interface is often not provided in the + * network implementations, but has to be extended by wrapping with `fanout_view`. + */ + template + void foreach_fanout( node const& n, Fn&& fn ) const; +#pragma endregion + +#pragma region Simulate values + /*! \brief Simulates arbitrary value on a node. + * + * This is a generic simulation method that can be implemented multiple times + * for a network interface for different types. One only needs to change the + * implementation and change the value for the type parameter ``T``, which + * indicates the element type of the iterators. + * + * Examples for simulation types are ``bool``, + * ``kitty::dynamic_truth_table``, bit masks, or BDDs. + * + * The ``begin`` and ``end`` iterator point to values which are assumed to be + * assigned to the fanin of the node. Consequently, the distance from + * ``begin`` to ``end`` must equal the fanin size of the node. + * + * \param n Node to simulate (used to retrieve the node function) + * \param begin Begin iterator to simulation values of fanin + * \param end End iterator to simulation values of fanin + * \return Returns computed simulation value of type ``T`` + */ + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const; +#pragma endregion + +#pragma region Custom node values + /*! \brief Reset all values to 0. */ + void clear_values() const; + + /*! \brief Returns value of a node. */ + uint32_t value( node const& n ) const; + + /*! \brief Sets value of a node. */ + void set_value( node const& n, uint32_t value ) const; + + /*! \brief Increments value of a node and returns *previous* value. */ + uint32_t incr_value( node const& n ) const; + + /*! \brief Decrements value of a node and returns *new* value. */ + uint32_t decr_value( node const& n ) const; +#pragma endregion + +#pragma region Visited flags + /*! \brief Reset all visited values to 0. */ + void clear_visited() const; + + /*! \brief Returns the visited value of a node. */ + uint32_t visited( node const& n ) const; + + /*! \brief Sets the visited value of a node. */ + void set_visited( node const& n, uint32_t v ) const; + + /*! \brief Returns the current traversal id. */ + uint32_t trav_id() const; + + /*! \brief Increment the current traversal id. */ + void incr_trav_id() const; +#pragma endregion + +#pragma region General methods + /*! \brief Returns network events object. + * + * Clients can register callbacks for network events to this object. Events + * include adding nodes, modifying nodes, and deleting nodes. + */ + network_events& events() const; +#pragma endregion +}; + +} /* namespace mockturtle */ diff --git a/include/mockturtle/io/aiger_reader.hpp b/include/mockturtle/io/aiger_reader.hpp new file mode 100644 index 0000000..3a633f1 --- /dev/null +++ b/include/mockturtle/io/aiger_reader.hpp @@ -0,0 +1,233 @@ +/* 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 aiger_reader.hpp + \brief Lorina reader for AIGER files + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/sequential.hpp" +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for Aiger files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_not` + * - `create_and` + * + * **Optional network functions to support sequential networks:** + * - `create_ri` + * - `create_ro` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig; + lorina::read_aiger( "file.aig", aiger_reader( aig ) ); + + mig_network mig; + lorina::read_aiger( "file.aig", aiger_reader( mig ) ); + \endverbatim + */ +template +class aiger_reader : public lorina::aiger_reader +{ +public: + explicit aiger_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + } + + ~aiger_reader() + { + uint32_t output_id{ 0 }; + for ( auto out : outputs ) + { + auto const lit = std::get<0>( out ); + auto signal = signals[lit >> 1]; + if ( lit & 1 ) + { + signal = _ntk.create_not( signal ); + } + if constexpr ( has_set_output_name_v ) + { + if ( !std::get<1>( out ).empty() ) + { + _ntk.set_output_name( output_id++, std::get<1>( out ) ); + } + } + _ntk.create_po( signal ); + } + + if constexpr ( has_create_ri_v ) + { + for ( auto i = 0u; i < latches.size(); ++i ) + { + auto& latch = latches[i]; + auto const lit = std::get<0>( latch ); + auto const reset = std::get<1>( latch ); + + auto signal = signals[lit >> 1]; + if ( lit & 1 ) + { + signal = _ntk.create_not( signal ); + } + + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signal, std::get<2>( latch ) + "_next" ); + } + + _ntk.create_ri( signal ); + register_t reg; + reg.init = reset; + _ntk.set_register( i, reg ); + } + } + } + + void on_header( uint64_t, uint64_t num_inputs, uint64_t num_latches, uint64_t, uint64_t ) const override + { + (void)num_latches; + if constexpr ( !has_create_ri_v || !has_create_ro_v ) + { + assert( num_latches == 0 && "network type does not support the creation of latches" ); + } + + _num_inputs = static_cast( num_inputs ); + + /* constant */ + signals.push_back( _ntk.get_constant( false ) ); + + /* create primary inputs (pi) */ + for ( auto i = 0u; i < num_inputs; ++i ) + { + signals.push_back( _ntk.create_pi() ); + } + + if constexpr ( has_create_ro_v ) + { + /* create latch outputs (ro) */ + for ( auto i = 0u; i < num_latches; ++i ) + { + signals.push_back( _ntk.create_ro() ); + } + } + } + + void on_input_name( unsigned index, const std::string& name ) const override + { + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[1 + index], name ); + } + } + + void on_output_name( unsigned index, const std::string& name ) const override + { + std::get<1>( outputs[index] ) = name; + } + + void on_latch_name( unsigned index, const std::string& name ) const override + { + if constexpr ( has_create_ri_v && has_create_ro_v ) + { + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[1 + _num_inputs + index], name ); + } + std::get<2>( latches[index] ) = name; + } + } + + void on_and( unsigned index, unsigned left_lit, unsigned right_lit ) const override + { + (void)index; + assert( signals.size() == index ); + + auto left = signals[left_lit >> 1]; + if ( left_lit & 1 ) + { + left = _ntk.create_not( left ); + } + + auto right = signals[right_lit >> 1]; + if ( right_lit & 1 ) + { + right = _ntk.create_not( right ); + } + + signals.push_back( _ntk.create_and( left, right ) ); + } + + void on_latch( unsigned index, unsigned next, latch_init_value reset ) const override + { + if constexpr ( has_create_ri_v && has_create_ro_v ) + { + (void)index; + int8_t r = reset == latch_init_value::NONDETERMINISTIC ? -1 : ( reset == latch_init_value::ONE ? 1 : 0 ); + latches.push_back( std::make_tuple( next, r, "" ) ); + } + } + + void on_output( unsigned index, unsigned lit ) const override + { + (void)index; + assert( index == outputs.size() ); + outputs.emplace_back( lit, "" ); + } + +private: + Ntk& _ntk; + + mutable uint32_t _num_inputs{ 0 }; + mutable std::vector> outputs; + mutable std::vector signals; + mutable std::vector> latches; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/bench_reader.hpp b/include/mockturtle/io/bench_reader.hpp new file mode 100644 index 0000000..a3cf95a --- /dev/null +++ b/include/mockturtle/io/bench_reader.hpp @@ -0,0 +1,194 @@ +/* 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 bench_reader.hpp + \brief Lorina reader for BENCH files + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for BENCH files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_node` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + klut_network klut; + lorina::read_bench( "file.bench", bench_reader( klut ) ); + \endverbatim + */ +template +class bench_reader : public lorina::bench_reader +{ +public: + explicit bench_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_node_v, "Ntk does not implement the create_node function" ); + signals["gnd"] = _ntk.get_constant( false ); + signals["vdd"] = _ntk.get_constant( true ); + } + + ~bench_reader() + { + for ( auto const& o : outputs ) + { + _ntk.create_po( signals[o] ); + } + } + + void on_input( const std::string& name ) const override + { + signals[name] = _ntk.create_pi(); + if constexpr ( has_set_name_v ) + { + _ntk.set_name( signals[name], name ); + } + } + + void on_output( const std::string& name ) const override + { + if constexpr ( has_set_output_name_v ) + { + _ntk.set_output_name( outputs.size(), name ); + } + outputs.emplace_back( name ); + } + + void on_assign( const std::string& input, const std::string& output ) const override + { + signals[output] = signals.at( input ); + } + + void on_gate( const std::vector& inputs, const std::string& output, const std::string& type ) const override + { + if ( type.size() > 2 && std::string_view( type ).substr( 0, 2 ) == "0x" && inputs.size() <= 6u ) + { + /* modern-style gate definition */ + kitty::dynamic_truth_table tt( static_cast( inputs.size() ) ); + kitty::create_from_hex_string( tt, type.substr( 2 ) ); + + std::vector> input_signals; + for ( const auto& i : inputs ) + input_signals.push_back( signals[i] ); + + signals[output] = _ntk.create_node( input_signals, tt ); + } + else + { + /* old-style gate definition */ + std::vector> input_signals; + for ( const auto& i : inputs ) + input_signals.push_back( signals[i] ); + + kitty::dynamic_truth_table tt( static_cast( inputs.size() ) ); + + std::vector vs( inputs.size(), tt ); + for ( auto i = 0u; i < inputs.size(); ++i ) + kitty::create_nth_var( vs[i], i ); + + if ( type == "NOT" ) + { + assert( inputs.size() == 1u ); + tt = ~vs.at( 0u ); + } + else if ( type == "BUFF" ) + { + assert( inputs.size() == 1u ); + tt = vs.at( 0u ); + } + else if ( type == "AND" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt &= vs.at( i ); + } + else if ( type == "NAND" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt &= vs.at( i ); + tt = ~tt; + } + else if ( type == "OR" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt |= vs.at( i ); + } + else if ( type == "NOR" ) + { + tt = vs.at( 0u ); + for ( auto i = 1u; i < inputs.size(); ++i ) + tt |= vs.at( i ); + tt = ~tt; + } + else + { + assert( false && "unsupported gate type" ); + } + signals[output] = _ntk.create_node( input_signals, tt ); + } + } + +private: + Ntk& _ntk; + + mutable std::map> signals; + mutable std::vector outputs; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/blif_reader.hpp b/include/mockturtle/io/blif_reader.hpp new file mode 100644 index 0000000..970c088 --- /dev/null +++ b/include/mockturtle/io/blif_reader.hpp @@ -0,0 +1,332 @@ +/* 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 blif_reader.hpp + \brief Lorina reader for BLIF files + + \author Andrea Costamagna + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include "../networks/cover.hpp" +#include "../networks/sequential.hpp" +#include "../traits.hpp" + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for BLIF files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_node` or `create_cover_node` + * - `get_constant` + * + \verbatim embed:rst + Example + .. code-block:: c++ + klut_network klut; + lorina::read_blif( "file.blif", blif_reader( klut ) ); + + cover_network cover; + lorina::read_blif( "file.blif", blif_reader( cover ) ); + \endverbatim + */ +template +class blif_reader : public lorina::blif_reader +{ +public: + explicit blif_reader( Ntk& ntk ) + : ntk_( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_node_v || has_create_cover_node_v, "Ntk does not implement the create_node function or the create_cover_node function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + } + + ~blif_reader() + { + for ( auto const& o : outputs ) + { + ntk_.create_po( signals[o] ); + } + + if constexpr ( has_create_ri_v ) + { + for ( auto const& latch : latches ) + { + auto signal = signals[latch]; + ntk_.create_ri( signal ); + } + } + } + + virtual void on_model( const std::string& model_name ) const override + { + if constexpr ( has_set_network_name_v ) + { + ntk_.set_network_name( model_name ); + } + + (void)model_name; + } + + virtual void on_input( const std::string& name ) const override + { + signals[name] = ntk_.create_pi(); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals[name], name ); + } + } + + virtual void on_output( const std::string& name ) const override + { + if constexpr ( has_set_output_name_v ) + { + ntk_.set_output_name( outputs.size(), name ); + } + outputs.emplace_back( name ); + } + + virtual void on_latch( const std::string& input, const std::string& output, const std::optional& l_type, const std::optional& control, const std::optional& reset ) const override + { + if constexpr ( has_create_ro_v ) + { + std::string type = "re"; + if ( l_type ) + { + switch ( *l_type ) + { + case latch_type::FALLING: + { + type = "fe"; + } + break; + case latch_type::RISING: + { + type = "re"; + } + break; + case latch_type::ACTIVE_HIGH: + { + type = "ah"; + } + break; + case latch_type::ACTIVE_LOW: + { + type = "al"; + } + break; + case latch_type::ASYNC: + { + type = "as"; + } + break; + default: + { + type = ""; + } + break; + } + } + + uint32_t r = 3; + if ( reset ) + { + switch ( *reset ) + { + case latch_init_value::NONDETERMINISTIC: + { + r = 2; + } + break; + case latch_init_value::ONE: + { + r = 1; + } + break; + case latch_init_value::ZERO: + { + r = 0; + } + break; + default: + break; + } + } + + std::string ctrl = control.has_value() ? control.value() : "clock"; + + register_t reg; + reg.control = ctrl; + reg.init = r; + reg.type = type; + + signals[output] = ntk_.create_ro(); + ntk_.set_register( latches.size(), reg ); + + if constexpr ( has_set_name_v && has_set_output_name_v ) + { + ntk_.set_name( signals[output], output ); + ntk_.set_output_name( outputs.size() + latches.size(), input ); + } + + latches.emplace_back( input ); + } + } + + virtual void on_gate( const std::vector& inputs, const std::string& output, const output_cover_t& cover ) const override + { + if ( inputs.size() == 0u ) + { + if ( cover.size() == 0u ) + { + signals[output] = ntk_.get_constant( false ); + return; + } + + assert( cover.size() == 1u ); + assert( cover.at( 0u ).first.size() == 0u ); + assert( cover.at( 0u ).second.size() == 1u ); + + auto const assigned_value = cover.at( 0u ).second.at( 0u ); + auto const const_value = ntk_.get_constant( assigned_value == '1' ? true : false ); + signals[output] = const_value; + return; + } + + assert( cover.size() > 0u ); + assert( cover.at( 0u ).second.size() == 1 ); + auto const first_output_value = cover.at( 0u ).second.at( 0u ); + + if constexpr ( std::is_same::value ) + { + std::vector cubes; + bool is_sop = ( first_output_value == '1' ); + for ( const auto& c : cover ) + { + assert( c.second.size() == 1 ); + + auto const output = c.second[0u]; + assert( output == '0' || output == '1' ); + assert( output == first_output_value ); + (void)first_output_value; + + cubes.emplace_back( kitty::cube( c.first ) ); + } + + std::vector> input_signals; + for ( const auto& i : inputs ) + { + assert( signals.find( i ) != signals.end() ); + input_signals.push_back( signals.at( i ) ); + } + + if ( cubes.size() != 0 ) + { + signals[output] = ntk_.create_cover_node( input_signals, std::make_pair( cubes, is_sop ) ); + } + } + else + { + + std::vector minterms; + std::vector maxterms; + + for ( const auto& c : cover ) + { + assert( c.second.size() == 1 ); + + auto const output = c.second[0u]; + assert( output == '0' || output == '1' ); + assert( output == first_output_value ); + (void)first_output_value; + + if ( output == '1' ) + { + minterms.emplace_back( kitty::cube( c.first ) ); + } + else if ( output == '0' ) + { + maxterms.emplace_back( ~kitty::cube( c.first ) ); + } + } + + assert( minterms.size() == 0u || maxterms.size() == 0u ); + + kitty::dynamic_truth_table tt( int( inputs.size() ) ); + if ( minterms.size() != 0 ) + { + kitty::create_from_cubes( tt, minterms, false ); + } + else if ( maxterms.size() != 0 ) + { + kitty::create_from_clauses( tt, maxterms, false ); + } + std::vector> input_signals; + for ( const auto& i : inputs ) + { + assert( signals.find( i ) != signals.end() ); + input_signals.push_back( signals.at( i ) ); + } + signals[output] = ntk_.create_node( input_signals, tt ); + } + } + + virtual void on_end() const override {} + + virtual void on_comment( const std::string& comment ) const override + { + (void)comment; + } + +private: + Ntk& ntk_; + + mutable std::map> signals; + mutable std::vector outputs; + mutable std::vector latches; +}; /* blif_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/bristol_reader.hpp b/include/mockturtle/io/bristol_reader.hpp new file mode 100644 index 0000000..49e0610 --- /dev/null +++ b/include/mockturtle/io/bristol_reader.hpp @@ -0,0 +1,149 @@ +/* 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 bristol_reader.hpp + \brief Lorina reader for Bristol files + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for Bristol files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_and` + * - `create_xor` + * - `create_not` + * - `get_constant` + * + \verbatim embed:rst + Example + .. code-block:: c++ + xag_network xag; + lorina::read_bristol( "file.txt", bristol_reader( xag ) ); + \endverbatim + */ +template +class bristol_reader : public lorina::bristol_reader +{ +public: + explicit bristol_reader( Ntk& ntk ) + : ntk_( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + } + + ~bristol_reader() + { + } + + virtual void on_header( uint32_t num_gates, uint32_t num_wires, uint32_t num_inputs, std::vector const& num_wires_per_input, uint32_t num_outputs, std::vector const& num_wires_per_output ) const override + { + (void)num_inputs; + (void)num_outputs; + + const auto num_pis = std::accumulate( num_wires_per_input.begin(), num_wires_per_input.end(), 0u ); + num_pos_ = std::accumulate( num_wires_per_output.begin(), num_wires_per_output.end(), 0u ); + num_gates_ = num_gates; + gate_ctr_ = 0u; + signal_.resize( num_wires ); + + for ( auto i = 0u; i < num_pis; ++i ) + { + signal_[i] = ntk_.create_pi(); + } + } + + virtual void on_gate( std::vector const& in, uint32_t out, std::string const& gate ) const override + { + if ( gate == "XOR" ) + { + signal_[out] = ntk_.create_xor( signal_[in[0]], signal_[in[1]] ); + } + else if ( gate == "AND" ) + { + signal_[out] = ntk_.create_and( signal_[in[0]], signal_[in[1]] ); + } + else if ( gate == "INV" ) + { + signal_[out] = ntk_.create_not( signal_[in[0]] ); + } + else if ( gate == "EQW" ) + { + signal_[out] = signal_[in[0]]; + } + else + { + fmt::print( "[e] unknown {} gate {} with {} inputs!", gate, out, in.size() ); + std::abort(); + } + + ++gate_ctr_; + + if ( gate_ctr_ == num_gates_ ) + { + for ( auto i = signal_.size() - num_pos_; i < signal_.size(); ++i ) + { + ntk_.create_po( signal_[i] ); + } + } + else if ( gate_ctr_ > num_gates_ ) + { + fmt::print( "[w] adding dangling {} gate with inputs {} and output {}\n", gate, fmt::join( in, ", " ), out ); + } + } + +private: + Ntk& ntk_; + + mutable uint32_t num_pos_; + mutable uint32_t num_gates_; + mutable uint32_t gate_ctr_; + mutable std::vector> signal_; +}; /* bristol_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/dimacs_reader.hpp b/include/mockturtle/io/dimacs_reader.hpp new file mode 100644 index 0000000..1378472 --- /dev/null +++ b/include/mockturtle/io/dimacs_reader.hpp @@ -0,0 +1,113 @@ +/* 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 dimacs_reader.hpp + \brief Lorina reader for DIMACS files + + \author Bruno Schmitt +*/ + +#pragma once + +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for DIMACS files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `create_nary_and` + * - `create_nary_or` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + xag_network xag; + lorina::read_dimacs( "file.cnf", dimacs_reader( xag ) ); + \endverbatim + */ +template +class dimacs_reader : public lorina::dimacs_reader +{ +public: + explicit dimacs_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_nary_and_v, "Ntk does not implement the create_nary_and function" ); + static_assert( has_create_nary_or_v, "Ntk does not implement the create_nary_or function" ); + } + + void on_number_of_variables( uint64_t number_of_inputs ) const override + { + _pis.resize( number_of_inputs ); + std::generate( _pis.begin(), _pis.end(), [this]() { return _ntk.create_pi(); } ); + } + + void on_clause( const std::vector& clause ) const override + { + std::vector> literals; + + for ( int lit : clause ) + { + uint32_t var = std::abs( lit ) - 1; + if ( lit < 0 ) + { + literals.push_back( !_pis.at( var ) ); + } + else + { + literals.push_back( _pis.at( var ) ); + } + } + + const auto sum = _ntk.create_nary_or( literals ); + _sums.push_back( sum ); + } + + void on_end() const override + { + const auto output = _ntk.create_nary_and( _sums ); + _ntk.create_po( output ); + } + +private: + Ntk& _ntk; + mutable std::vector> _pis; + mutable std::vector> _sums; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/genlib_reader.hpp b/include/mockturtle/io/genlib_reader.hpp new file mode 100644 index 0000000..1a3faae --- /dev/null +++ b/include/mockturtle/io/genlib_reader.hpp @@ -0,0 +1,169 @@ +/* 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 genlib_reader.hpp + \brief Reader visitor for GENLIB files + + \author Alessandro Tempia Calvino + \author Heinz Riener +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +enum class phase_type : uint8_t +{ + INV = 0, + NONINV = 1, + UNKNOWN = 2, +}; + +struct pin +{ + std::string name; + phase_type phase; + double input_load; + double max_load; + double rise_block_delay; + double rise_fanout_delay; + double fall_block_delay; + double fall_fanout_delay; +}; /* pin */ + +struct gate +{ + unsigned int id; + std::string name; + std::string expression; + uint32_t num_vars; + kitty::dynamic_truth_table function; + double area; + std::vector pins; + std::string output_name; +}; /* gate */ + +/*! \brief lorina callbacks for GENLIB files. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + \endverbatim + */ +class genlib_reader : public lorina::genlib_reader +{ +public: + explicit genlib_reader( std::vector& gates ) + : gates( gates ) + {} + + virtual void on_gate( std::string const& name, std::string const& expression, double area, std::vector const& ps, std::string const& output_pin ) const override + { + std::vector pp; + std::vector pin_names; + + if ( ps.size() == 1 && ps[0].name == "*" ) + { + /* pins not defined, use names and appearance order of the expression */ + std::vector tokens; + std::string const delimitators{ " ()!\'*&+|^\t\r\n" }; + std::size_t prev = 0, pos; + while ( ( pos = expression.find_first_of( delimitators, prev ) ) != std::string::npos ) + { + if ( pos > prev ) + { + tokens.emplace_back( expression.substr( prev, pos - prev ) ); + } + prev = pos + 1; + } + if ( prev < expression.length() ) + { + tokens.emplace_back( expression.substr( prev, std::string::npos ) ); + } + for ( auto const& pin_name : tokens ) + { + if ( std::find( pin_names.begin(), pin_names.end(), pin_name ) == pin_names.end() ) + { + pp.emplace_back( pin{ pin_name, + phase_type( static_cast( ps[0].phase ) ), + ps[0].input_load, ps[0].max_load, + ps[0].rise_block_delay, ps[0].rise_fanout_delay, ps[0].fall_block_delay, ps[0].fall_fanout_delay } ); + pin_names.push_back( pin_name ); + } + } + } + else + { + for ( const auto& p : ps ) + { + pp.emplace_back( pin{ p.name, + phase_type( static_cast( p.phase ) ), + p.input_load, p.max_load, + p.rise_block_delay, p.rise_fanout_delay, p.fall_block_delay, p.fall_fanout_delay } ); + pin_names.push_back( p.name ); + } + } + + /* replace possible CONST0 or CONST1 by 0 and 1 */ + std::string formula( expression ); + std::size_t found = formula.find( "CONST" ); + if ( found != std::string::npos ) + { + formula.erase( found, found + 5 ); + } + + uint32_t num_vars = pin_names.size(); + + kitty::dynamic_truth_table tt{ num_vars }; + + if ( !kitty::create_from_formula( tt, formula, pin_names ) ) + { + /* formula error, skip gate */ + return; + } + + gates.emplace_back( gate{ static_cast( gates.size() ), name, + expression, num_vars, tt, area, pp, output_pin } ); + } + +protected: + std::vector& gates; +}; /* genlib_reader */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/io/pla_reader.hpp b/include/mockturtle/io/pla_reader.hpp new file mode 100644 index 0000000..558ed44 --- /dev/null +++ b/include/mockturtle/io/pla_reader.hpp @@ -0,0 +1,154 @@ +/* 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 pla_reader.hpp + \brief Lorina reader for PLA files + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" +#include + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for PLA files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `create_nary_and` + * - `create_nary_or` + * - `create_nary_xor` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_network aig; + lorina::read_pla( "file.pla", pla_reader( aig ) ); + + mig_network mig; + lorina::read_pla( "file.pla", pla_reader( mig ) ); + \endverbatim + */ +template +class pla_reader : public lorina::pla_reader +{ +public: + explicit pla_reader( Ntk& ntk ) : _ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_nary_and_v, "Ntk does not implement the create_nary_and function" ); + static_assert( has_create_nary_or_v, "Ntk does not implement the create_nary_or function" ); + static_assert( has_create_nary_xor_v, "Ntk does not implement the create_nary_xor function" ); + } + + void on_number_of_inputs( uint64_t number_of_inputs ) const override + { + _pis.resize( number_of_inputs ); + std::generate( _pis.begin(), _pis.end(), [this]() { return _ntk.create_pi(); } ); + } + + void on_number_of_outputs( uint64_t number_of_outputs ) const override + { + _products.resize( number_of_outputs ); + } + + bool on_keyword( const std::string& keyword, const std::string& value ) const override + { + if ( keyword == "type" && value == "esop" ) + { + _is_xor = true; + return true; + } + return false; + } + + void on_end() const override + { + for ( auto const& v : _products ) + { + _ntk.create_po( _is_xor ? _ntk.create_nary_xor( v ) : _ntk.create_nary_or( v ) ); + } + } + + void on_term( const std::string& term, const std::string& out ) const override + { + std::vector> literals; + for ( auto i = 0u; i < term.size(); ++i ) + { + switch ( term[i] ) + { + default: + std::cerr << "[w] unknown character '" << term[i] << "' in PLA input term, treat as don't care\n"; + case '-': + break; + + case '0': + literals.push_back( _ntk.create_not( _pis[i] ) ); + break; + case '1': + literals.push_back( _pis[i] ); + break; + } + } + + const auto product = _ntk.create_nary_and( literals ); + for ( auto i = 0u; i < out.size(); ++i ) + { + switch ( out[i] ) + { + default: + std::cerr << "[w] unknown character '" << out[i] << "' in PLA output term, treat is 0\n"; + case '0': + break; + + case '1': + _products[i].push_back( product ); + break; + } + } + } + +private: + Ntk& _ntk; + mutable std::vector> _pis; + mutable std::vector>> _products; + mutable bool _is_xor{ false }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/serialize.hpp b/include/mockturtle/io/serialize.hpp new file mode 100644 index 0000000..b02a1a5 --- /dev/null +++ b/include/mockturtle/io/serialize.hpp @@ -0,0 +1,377 @@ +/* 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 serialize.hpp + \brief Serialize network into a file + + \author Bruno Schmitt + \author Heinz Riener + \author Siang-Yun (Sonia) Lee + + This file implements functions to serialize a (combinational) + `aig_network` into a file. The serializer should be used for + debugging-purpose only. It allows to store the current state of the + network (including dangling and dead nodes), but does not guarantee + platform-independence (use, e.g., `write_verilog` instead). +*/ + +#pragma once + +#include "../networks/aig.hpp" +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +struct serializer +{ +public: + using node_type = typename aig_network::storage::element_type::node_type; + using pointer_type = typename node_type::pointer_type; + +public: + bool operator()( phmap::BinaryOutputArchive& os, uint64_t const& data ) const + { + return os.dump( (char*)&data, sizeof( uint64_t ) ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, uint64_t* data ) const + { + return ar_input.load( (char*)data, sizeof( uint64_t ) ); + } + + template + bool operator()( phmap::BinaryOutputArchive& os, node_pointer const& ptr ) const + { + return os.dump( (char*)&ptr.data, sizeof( ptr.data ) ); + } + + template + bool operator()( phmap::BinaryInputArchive& ar_input, node_pointer* ptr ) const + { + return ar_input.load( (char*)&ptr->data, sizeof( ptr->data ) ); + } + + bool operator()( phmap::BinaryOutputArchive& os, cauint64_t const& data ) const + { + return os.dump( (char*)&data.n, sizeof( data.n ) ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, cauint64_t* data ) const + { + return ar_input.load( (char*)&data->n, sizeof( data->n ) ); + } + + template + bool operator()( phmap::BinaryOutputArchive& os, regular_node const& n ) const + { + uint64_t size = n.children.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + + for ( const auto& c : n.children ) + { + bool result = this->operator()( os, c ); + if ( !result ) + { + return false; + } + } + + size = n.data.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& d : n.data ) + { + bool result = this->operator()( os, d ); + if ( !result ) + { + return false; + } + } + + return true; + } + + template + bool operator()( phmap::BinaryInputArchive& ar_input, const regular_node* n ) const + { + uint64_t size; + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + + for ( uint64_t i = 0; i < size; ++i ) + { + pointer_type ptr; + bool result = this->operator()( ar_input, &ptr ); + if ( !result ) + { + return false; + } + const_cast*>( n )->children[i] = ptr; + } + + ar_input.load( (char*)&size, sizeof( uint64_t ) ); + for ( uint64_t i = 0; i < size; ++i ) + { + cauint64_t data; + bool result = this->operator()( ar_input, &data ); + if ( !result ) + { + return false; + } + const_cast*>( n )->data[i] = data; + } + + return true; + } + + bool operator()( phmap::BinaryOutputArchive& os, std::pair const& value ) const + { + return this->operator()( os, value.first ) && this->operator()( os, value.second ); + } + + bool operator()( phmap::BinaryInputArchive& ar_input, std::pair* value ) const + { + return this->operator()( ar_input, &value->first ) && this->operator()( ar_input, &value->second ); + } + + bool operator()( phmap::BinaryOutputArchive& os, aig_storage const& storage ) const + { + /* nodes */ + uint64_t size = storage.nodes.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& n : storage.nodes ) + { + if ( !this->operator()( os, n ) ) + { + return false; + } + } + + /* inputs */ + size = storage.inputs.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& i : storage.inputs ) + { + if ( !this->operator()( os, i ) ) + { + return false; + } + } + + /* outputs */ + size = storage.outputs.size(); + if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( const auto& o : storage.outputs ) + { + if ( !this->operator()( os, o ) ) + { + return false; + } + } + + /* hash */ + if ( !const_cast( storage ).hash.dump( os ) ) + { + return false; + } + + if ( !os.dump( (char*)&storage.trav_id, sizeof( uint32_t ) ) ) + { + return false; + } + + return true; + } + + bool operator()( phmap::BinaryInputArchive& ar_input, aig_storage* storage ) const + { + /* nodes */ + uint64_t size; + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + node_type n; + if ( !this->operator()( ar_input, &n ) ) + { + return false; + } + storage->nodes.push_back( n ); + } + + /* inputs */ + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + uint64_t value; + if ( !ar_input.load( (char*)&value, sizeof( uint64_t ) ) ) + { + return false; + } + storage->inputs.push_back( value ); + } + + /* outputs */ + if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) ) + { + return false; + } + for ( uint64_t i = 0; i < size; ++i ) + { + pointer_type ptr; + if ( !this->operator()( ar_input, &ptr ) ) + { + return false; + } + storage->outputs.push_back( ptr ); + } + + /* hash */ + if ( !storage->hash.load( ar_input ) ) + { + return false; + } + + if ( !ar_input.load( (char*)&storage->trav_id, sizeof( uint32_t ) ) ) + { + return false; + } + + return true; + } +}; /* struct serializer */ + +} /* namespace detail */ + +/*! \brief Serializes a combinational AIG network to a archive, returning false on failure + * + * \param aig Combinational AIG network + * \param os Output archive + */ +inline bool serialize_network_fallible( aig_network const& aig, phmap::BinaryOutputArchive& os ) +{ + detail::serializer _serializer; + return _serializer( os, *aig._storage ); +} + +/*! \brief Serializes a combinational AIG network to a archive + * + * \param aig Combinational AIG network + * \param os Output archive + */ +inline void serialize_network( aig_network const& aig, phmap::BinaryOutputArchive& os ) +{ + bool const okay = serialize_network_fallible( aig, os ); + (void)okay; + assert( okay && "failed to serialize the network onto stream" ); +} + +/*! \brief Serializes a combinational AIG network in a file + * + * \param aig Combinational AIG network + * \param filename Filename + */ +inline void serialize_network( aig_network const& aig, std::string const& filename ) +{ + phmap::BinaryOutputArchive ar_out( filename.c_str() ); + serialize_network( aig, ar_out ); +} + +/*! \brief Deserializes a combinational AIG network from a input archive, returning nullopt on failure + * + * \param ar_input Input archive + * \return Deserialized AIG network + */ +inline std::optional deserialize_network_fallible( phmap::BinaryInputArchive& ar_input ) +{ + detail::serializer _serializer; + auto storage = std::make_shared(); + storage->nodes.clear(); + storage->inputs.clear(); + storage->outputs.clear(); + storage->hash.clear(); + + if ( _serializer( ar_input, storage.get() ) ) + { + return aig_network{ storage }; + } + + return std::nullopt; +} + +/*! \brief Deserializes a combinational AIG network from a input archive + * + * \param ar_input Input archive + * \return Deserialized AIG network + */ +inline aig_network deserialize_network( phmap::BinaryInputArchive& ar_input ) +{ + auto result = deserialize_network_fallible( ar_input ); + (void)result.has_value(); + assert( result.has_value() && "failed to deserialize the network onto stream" ); + return *result; +} + +/*! \brief Deserializes a combinational AIG network from a file + * + * \param filename Filename + * \return Deserialized AIG network + */ +inline aig_network deserialize_network( std::string const& filename ) +{ + phmap::BinaryInputArchive ar_input( filename.c_str() ); + auto aig = deserialize_network( ar_input ); + return aig; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/super_reader.hpp b/include/mockturtle/io/super_reader.hpp new file mode 100644 index 0000000..335dbf6 --- /dev/null +++ b/include/mockturtle/io/super_reader.hpp @@ -0,0 +1,103 @@ +/* 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 super_reader.hpp + \brief Reader visitor for SUPER files generated by ABC + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +struct supergate_spec +{ + unsigned int id; + std::string name{}; + bool is_super{ false }; + std::vector fanin_id; +}; + +struct super_lib +{ + std::string genlib_name{}; + uint32_t max_num_vars{ 0u }; + uint32_t num_supergates{ 0u }; + uint32_t num_lines{ 0 }; + std::vector supergates{}; +}; + +/*! \brief lorina callbacks for SUPER files. + * + * SUPER files can be generated by ABC with the command `super`. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + \endverbatim + */ +class super_reader : public lorina::super_reader +{ +public: + explicit super_reader( super_lib& lib ) + : lib( lib ) + { + } + + virtual void on_super_info( std::string const& genlib_name, uint32_t max_num_vars, uint32_t max_superGates, uint32_t num_lines ) const override + { + lib.genlib_name = genlib_name; + lib.max_num_vars = max_num_vars; + lib.num_supergates = max_superGates; + lib.num_lines = num_lines; + } + + virtual void on_supergate( std::string const& name, bool const& is_super, std::vector const& fanins_id ) const override + { + + lib.supergates.emplace_back( supergate_spec{ static_cast( lib.supergates.size() ), + name, + is_super, + fanins_id } ); + } + +protected: + super_lib& lib; +}; /* super_reader */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/verilog_reader.hpp b/include/mockturtle/io/verilog_reader.hpp new file mode 100644 index 0000000..1f8c851 --- /dev/null +++ b/include/mockturtle/io/verilog_reader.hpp @@ -0,0 +1,636 @@ +/* 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 verilog_reader.hpp + \brief Lorina reader for VERILOG files + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../generators/arithmetic.hpp" +#include "../generators/modular_arithmetic.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Lorina reader callback for VERILOG files. + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * - `get_constant` + * - `create_not` + * - `create_and` + * - `create_or` + * - `create_xor` + * - `create_maj` + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mig_network mig; + lorina::read_verilog( "file.v", verilog_reader( mig ) ); + \endverbatim + */ +template +class verilog_reader : public lorina::verilog_reader +{ +public: + explicit verilog_reader( Ntk& ntk, std::string const& top_module_name = "top" ) : ntk_( ntk ), top_module_name_( top_module_name ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi function" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not function" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and function" ); + static_assert( has_create_or_v, "Ntk does not implement the create_or function" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor function" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_ite function" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj function" ); + + signals_["0"] = ntk_.get_constant( false ); + signals_["1"] = ntk_.get_constant( true ); + signals_["1'b0"] = ntk_.get_constant( false ); + signals_["1'b1"] = ntk_.get_constant( true ); + } + + void on_module_header( const std::string& module_name, const std::vector& inouts ) const override + { + (void)inouts; + if constexpr ( has_set_network_name_v ) + { + ntk_.set_network_name( module_name ); + } + + name_ = module_name; + } + + void on_inputs( const std::vector& names, std::string const& size = "" ) const override + { + (void)size; + if ( name_ != top_module_name_ ) + return; + + for ( const auto& name : names ) + { + if ( size.empty() ) + { + signals_[name] = ntk_.create_pi(); + input_names_.emplace_back( name, 1u ); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals_[name], name ); + } + } + else + { + std::vector> word; + const auto length = parse_size( size ); + for ( auto i = 0u; i < length; ++i ) + { + const auto sname = fmt::format( "{}[{}]", name, i ); + word.push_back( ntk_.create_pi() ); + signals_[sname] = word.back(); + if constexpr ( has_set_name_v ) + { + ntk_.set_name( signals_[sname], sname ); + } + } + registers_[name] = word; + input_names_.emplace_back( name, length ); + } + } + } + + void on_outputs( const std::vector& names, std::string const& size = "" ) const override + { + (void)size; + if ( name_ != top_module_name_ ) + return; + + for ( const auto& name : names ) + { + if ( size.empty() ) + { + outputs_.emplace_back( name ); + output_names_.emplace_back( name, 1u ); + } + else + { + const auto length = parse_size( size ); + for ( auto i = 0u; i < length; ++i ) + { + outputs_.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + output_names_.emplace_back( name, length ); + } + } + } + + void on_assign( const std::string& lhs, const std::pair& rhs ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( rhs.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", rhs.first ); + + auto r = signals_[rhs.first]; + signals_[lhs] = rhs.second ? ntk_.create_not( r ) : r; + } + + void on_nand( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + signals_[lhs] = ntk_.create_nand( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + + void on_and( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second && !op2.second ) + signals_[lhs] = ntk_.create_and( a, b ); + else if ( !op1.second && op2.second ) + signals_[lhs] = ntk_.create_gt( a, b ); // a & !b + else if ( op1.second && !op2.second ) + signals_[lhs] = ntk_.create_lt( a, b ); // !a & b + else + signals_[lhs] = ntk_.create_nor( a, b ); // !a & !b = !(a | b) + } + else + { + signals_[lhs] = ntk_.create_and( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_or( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second && !op2.second ) + signals_[lhs] = ntk_.create_or( a, b ); + else if ( !op1.second && op2.second ) + signals_[lhs] = ntk_.create_ge( a, b ); // a | !b + else if ( op1.second && !op2.second ) + signals_[lhs] = ntk_.create_le( a, b ); // !a | b + else + signals_[lhs] = ntk_.create_nand( a, b ); // !a | !b = !(a & b) + } + else + { + signals_[lhs] = ntk_.create_or( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_xor( const std::string& lhs, const std::pair& op1, const std::pair& op2 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + if constexpr ( is_crossed_network_type_v ) + { + if ( !op1.second == !op2.second ) + signals_[lhs] = ntk_.create_xor( a, b ); + else + signals_[lhs] = ntk_.create_xnor( a, b ); + } + else + { + signals_[lhs] = ntk_.create_xor( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ); + } + } + + void on_xor3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + + if constexpr ( has_create_xor3_v ) + { + signals_[lhs] = ntk_.create_xor3( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + else + { + signals_[lhs] = ntk_.create_xor( ntk_.create_xor( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b ), op3.second ? ntk_.create_not( c ) : c ); + } + } + + void on_maj3( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + signals_[lhs] = ntk_.create_maj( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + + void on_mux21( const std::string& lhs, const std::pair& op1, const std::pair& op2, const std::pair& op3 ) const override + { + if ( name_ != top_module_name_ ) + return; + + if constexpr ( is_crossed_network_type_v ) + { + assert( false && "3-input gates in crossed_network are not supported (to be implemented)" ); + } + + if ( signals_.find( op1.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op1.first ); + if ( signals_.find( op2.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op2.first ); + if ( signals_.find( op3.first ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", op3.first ); + + auto a = signals_[op1.first]; + auto b = signals_[op2.first]; + auto c = signals_[op3.first]; + signals_[lhs] = ntk_.create_ite( op1.second ? ntk_.create_not( a ) : a, op2.second ? ntk_.create_not( b ) : b, op3.second ? ntk_.create_not( c ) : c ); + } + + void on_module_instantiation( std::string const& module_name, std::vector const& params, std::string const& inst_name, + std::vector> const& args ) const override + { + (void)inst_name; + if ( name_ != top_module_name_ ) + return; + + /* check routines */ + const auto num_args_equals = [&]( uint32_t expected_count ) { + if ( args.size() != expected_count ) + { + fmt::print( stderr, "[e] {} module expects {} arguments\n", module_name, expected_count ); + return false; + } + return true; + }; + + const auto num_params_equals = [&]( uint32_t expected_count ) { + if ( params.size() != expected_count ) + { + fmt::print( stderr, "[e] {} module expects {} parameters\n", module_name, expected_count ); + return false; + } + return true; + }; + + const auto register_exists = [&]( std::string const& name ) { + if ( registers_.find( name ) == registers_.end() ) + { + fmt::print( stderr, "[e] register {} does not exist\n", name ); + return false; + } + return true; + }; + + const auto register_has_size = [&]( std::string const& name, uint32_t size ) { + if ( !register_exists( name ) || registers_[name].size() != size ) + { + fmt::print( stderr, "[e] register {} must have size {}\n", name, size ); + return false; + } + return true; + }; + + const auto add_register = [&]( std::string const& name, std::vector> const& fs ) { + for ( auto i = 0u; i < fs.size(); ++i ) + { + signals_[fmt::format( "{}[{}]", name, i )] = fs[i]; + } + registers_[name] = fs; + }; + + if ( module_name == "ripple_carry_adder" ) + { + if ( !num_args_equals( 3u ) ) + return; + if ( !num_params_equals( 1u ) ) + return; + const auto bitwidth = static_cast( parse_small_value( params[0u] ) ); + if ( !register_has_size( args[0].second, bitwidth ) ) + return; + if ( !register_has_size( args[1].second, bitwidth ) ) + return; + + auto a_copy = registers_[args[0].second]; + const auto& b = registers_[args[1].second]; + auto carry = ntk_.get_constant( false ); + carry_ripple_adder_inplace( ntk_, a_copy, b, carry ); + a_copy.push_back( carry ); + add_register( args[2].second, a_copy ); + } + else if ( module_name == "montgomery_multiplier" ) + { + if ( !num_args_equals( 3u ) ) + return; + if ( !num_params_equals( 3u ) ) + return; + const auto bitwidth = static_cast( parse_small_value( params[0u] ) ); + if ( !register_has_size( args[0].second, bitwidth ) ) + return; + if ( !register_has_size( args[1].second, bitwidth ) ) + return; + + auto N = parse_value( params[1u] ); + auto NN = parse_value( params[2u] ); + + N.resize( bitwidth ); + NN.resize( bitwidth ); + + add_register( args[2].second, montgomery_multiplication( ntk_, registers_[args[0].second], registers_[args[1].second], N, NN ) ); + } + else if ( module_name == "buffer" || module_name == "inverter" ) + { + if constexpr ( is_buffered_network_type_v ) + { + static_assert( has_create_buf_v, "Ntk does not implement the create_buf method" ); + if ( !num_args_equals( 2u ) ) + fmt::print( stderr, "[e] number of arguments of a `{}` instance is not 2\n", module_name ); + + signal fi = ntk_.get_constant( false ); + std::string lhs; + for ( auto const& arg : args ) + { + if ( arg.first == ".i" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi = signals_[arg.second]; + } + else if ( arg.first == ".o" ) + lhs = arg.second; + else + fmt::print( stderr, "[e] unknown argument {} to a `{}` instance\n", arg.first, module_name ); + } + signals_[lhs] = ntk_.create_buf( fi ); + if ( module_name == "inverter" ) + ntk_.invert( ntk_.get_node( signals_[lhs] ) ); + } + } + else if ( module_name == "crossing" ) + { + if constexpr ( is_crossed_network_type_v ) + { + if ( !num_args_equals( 4u ) ) + fmt::print( stderr, "[e] number of arguments of a `{}` instance is not 4\n", module_name ); + + signal fi1 = ntk_.get_constant( false ); + signal fi2 = ntk_.get_constant( false ); + std::string fo1, fo2; + for ( auto const& arg : args ) + { + if ( arg.first == ".i1" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi1 = signals_[arg.second]; + } + else if ( arg.first == ".i2" ) + { + if ( signals_.find( arg.second ) == signals_.end() ) + fmt::print( stderr, "[w] undefined signal {} assigned 0\n", arg.second ); + else + fi2 = signals_[arg.second]; + } + else if ( arg.first == ".o1" ) + fo1 = arg.second; + else if ( arg.first == ".o2" ) + fo2 = arg.second; + else + fmt::print( stderr, "[e] unknown argument {} to a `{}` instance\n", arg.first, module_name ); + } + + if ( signals_.find( fo1 ) == signals_.end() ) // due to lorina bug, each crossing will be instantiated twice... + { + auto p = ntk_.create_crossing( fi1, fi2 ); + signals_[fo1] = p.first; + signals_[fo2] = p.second; + } + } + } + else + { + fmt::print( stderr, "[e] unknown module name {}\n", module_name ); + } + } + + void on_endmodule() const override + { + if ( name_ != top_module_name_ ) + return; + + for ( auto const& o : outputs_ ) + { + ntk_.create_po( signals_[o] ); + } + + if constexpr ( has_set_output_name_v ) + { + uint32_t ctr{ 0u }; + for ( auto const& output_name : output_names_ ) + { + if ( output_name.second == 1u ) + { + ntk_.set_output_name( ctr++, output_name.first ); + } + else + { + for ( auto i = 0u; i < output_name.second; ++i ) + { + ntk_.set_output_name( ctr++, fmt::format( "{}[{}]", output_name.first, i ) ); + } + } + } + assert( ctr == ntk_.num_pos() ); + } + } + + const std::string& name() const + { + return name_; + } + + const std::vector> input_names() + { + return input_names_; + } + + const std::vector> output_names() + { + return output_names_; + } + +private: + std::vector parse_value( const std::string& value ) const + { + std::smatch match; + + if ( std::all_of( value.begin(), value.end(), isdigit ) ) + { + std::vector res( 64u ); + bool_vector_from_dec( res, static_cast( std::stoul( value ) ) ); + return res; + } + else if ( std::regex_match( value, match, hex_string ) ) + { + std::vector res( static_cast( std::stoul( match.str( 1 ) ) ) ); + bool_vector_from_hex( res, match.str( 2 ) ); + return res; + } + else + { + fmt::print( stderr, "[e] cannot parse number '{}'\n", value ); + } + assert( false ); + return {}; + } + + uint64_t parse_small_value( const std::string& value ) const + { + return bool_vector_to_long( parse_value( value ) ); + } + + uint32_t parse_size( const std::string& size ) const + { + if ( size.empty() ) + { + return 1u; + } + + if ( auto const l = size.size(); l > 2 && size[l - 2] == ':' && size[l - 1] == '0' ) + { + return static_cast( parse_small_value( size.substr( 0u, l - 2 ) ) + 1u ); + } + + assert( false ); + return 0u; + } + +private: + Ntk& ntk_; + + std::string const top_module_name_; + + mutable std::map> signals_; + mutable std::map>> registers_; + mutable std::vector outputs_; + mutable std::string name_; + mutable std::vector> input_names_; + mutable std::vector> output_names_; + + std::regex hex_string{ "(\\d+)'h([0-9a-fA-F]+)" }; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_aiger.hpp b/include/mockturtle/io/write_aiger.hpp new file mode 100644 index 0000000..621b364 --- /dev/null +++ b/include/mockturtle/io/write_aiger.hpp @@ -0,0 +1,199 @@ +/* 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 write_aiger.hpp + \brief Write networks to AIGER format + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee + + A detailed description of the (binary) AIGER format and its encoding is available at [1]. + + [1] http://fmv.jku.at/aiger/ +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +inline void encode( std::vector& buffer, uint32_t lit ) +{ + unsigned char ch; + while ( lit & ~0x7f ) + { + ch = ( lit & 0x7f ) | 0x80; + buffer.push_back( ch ); + lit >>= 7; + } + ch = lit; + buffer.push_back( ch ); +} + +} // namespace detail + +/*! \brief Writes a combinational AIG network in binary AIGER format into a file + * + * This function should be only called on "clean" aig_networks, e.g., + * immediately after `cleanup_dangling`. + * + * **Required network functions:** + * - `num_cis` + * - `num_cos` + * - `foreach_gate` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * - `is_complemented` + * - `node_to_index` + * + * \param aig Combinational AIG network + * \param os Output stream + */ +template +inline void write_aiger( Ntk const& aig, std::ostream& os ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_cis_v, "Ntk does not implement the num_cis method" ); + static_assert( has_num_cos_v, "Ntk does not implement the num_cos method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + + assert( aig.is_combinational() && "Network has to be combinational" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + uint32_t const M = aig.num_cis() + aig.num_gates(); + + /* HEADER */ + char string_buffer[1024]; + sprintf( string_buffer, "aig %u %u %u %u %u\n", M, aig.num_pis(), /*latches*/ 0, aig.num_pos(), aig.num_gates() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + + /* POs */ + aig.foreach_po( [&]( signal const& f ) { + sprintf( string_buffer, "%u\n", uint32_t( 2 * aig.node_to_index( aig.get_node( f ) ) + aig.is_complemented( f ) ) ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + + /* GATES */ + std::vector buffer; + aig.foreach_gate( [&]( node const& n ) { + std::vector lits; + lits.push_back( 2 * aig.node_to_index( n ) ); + + aig.foreach_fanin( n, [&]( signal const& fi ) { + lits.push_back( 2 * aig.node_to_index( aig.get_node( fi ) ) + aig.is_complemented( fi ) ); + } ); + + if ( lits[1] > lits[2] ) + { + auto const tmp = lits[1]; + lits[1] = lits[2]; + lits[2] = tmp; + } + + assert( lits[2] < lits[0] ); + detail::encode( buffer, lits[0] - lits[2] ); + detail::encode( buffer, lits[2] - lits[1] ); + } ); + + for ( const auto& b : buffer ) + { + os.put( b ); + } + + /* symbol table */ + if constexpr ( has_has_name_v && has_get_name_v ) + { + aig.foreach_pi( [&]( node const& i, uint32_t index ) { + if ( !aig.has_name( aig.make_signal( i ) ) ) + return; + + sprintf( string_buffer, "i%u %s\n", + uint32_t( index ), + aig.get_name( aig.make_signal( i ) ).c_str() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + } + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + aig.foreach_po( [&]( signal const& f, uint32_t index ) { + if ( !aig.has_output_name( index ) ) + return; + + sprintf( string_buffer, "o%u %s\n", + uint32_t( index ), + aig.get_output_name( index ).c_str() ); + os.write( &string_buffer[0], sizeof( unsigned char ) * std::strlen( string_buffer ) ); + } ); + } + + /* COMMENT */ + os.put( 'c' ); +} + +/*! \brief Writes a combinational AIG network in binary AIGER format into a file + * + * This function should be only called on "clean" aig_networks, e.g., + * immediately after `cleanup_dangling`. + * + * **Required network functions:** + * - `num_cis` + * - `num_cos` + * - `foreach_gate` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * - `is_complemented` + * - `node_to_index` + * + * \param aig Combinational AIG network + * \param filename Filename + */ +template +inline void write_aiger( Ntk const& aig, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_aiger( aig, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_bench.hpp b/include/mockturtle/io/write_bench.hpp new file mode 100644 index 0000000..964d4c3 --- /dev/null +++ b/include/mockturtle/io/write_bench.hpp @@ -0,0 +1,164 @@ +/* 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 write_bench.hpp + \brief Write networks to BENCH format + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Writes network in BENCH format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `get_node` + * - `num_pos` + * - `node_to_index` + * - `node_function` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_bench( Ntk const& ntk, std::ostream& os ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant 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_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_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + ntk.foreach_pi( [&]( auto const& n ) { + os << fmt::format( "INPUT(n{})\n", ntk.node_to_index( n ) ); + } ); + + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + os << fmt::format( "OUTPUT(po{})\n", i ); + } + + os << fmt::format( "n{} = gnd\n", ntk.node_to_index( ntk.get_node( ntk.get_constant( false ) ) ) ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + os << fmt::format( "n{} = vdd\n", ntk.node_to_index( ntk.get_node( ntk.get_constant( true ) ) ) ); + } + + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return; /* continue */ + + auto func = ntk.node_function( n ); + std::string children; + auto first = true; + ntk.foreach_fanin( n, [&]( auto const& c, auto i ) { + if ( ntk.is_complemented( c ) ) + { + kitty::flip_inplace( func, i ); + } + if ( first ) + { + first = false; + } + else + { + children += ", "; + } + + children += fmt::format( "n{}", ntk.node_to_index( ntk.get_node( c ) ) ); + } ); + + os << fmt::format( "n{} = LUT 0x{} ({})\n", + ntk.node_to_index( n ), + kitty::to_hex( func ), children ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( auto const& s, auto i ) { + if ( ntk.is_constant( ntk.get_node( s ) ) ) + { + os << fmt::format( "po{} = {}\n", + i, + ( ntk.constant_value( ntk.get_node( s ) ) ^ ntk.is_complemented( s ) ) ? "vdd" : "gnd" ); + } + else + { + os << fmt::format( "po{} = LUT 0x{} (n{})\n", + i, + ntk.is_complemented( s ) ? 1 : 2, + ntk.node_to_index( ntk.get_node( s ) ) ); + } + } ); + + os << std::flush; +} + +/*! \brief Writes network in BENCH format into a file + * + * **Required network functions:** + * - `is_constant` + * - `is_pi` + * - `is_complemented` + * - `get_node` + * - `num_pos` + * - `node_to_index` + * - `node_function` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_bench( Ntk const& ntk, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_bench( ntk, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_blif.hpp b/include/mockturtle/io/write_blif.hpp new file mode 100644 index 0000000..bf0de34 --- /dev/null +++ b/include/mockturtle/io/write_blif.hpp @@ -0,0 +1,430 @@ +/* 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 write_blif.hpp + \brief Write networks to BLIF format + + \author Heinz Riener + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/sequential.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace mockturtle +{ + +struct write_blif_params +{ + /** + * ## `ps.rename_ri_using_node` + * + * Rename registers using node name if `rename_ri_using_node` is set to 1 ( default: 0 ) + * + * A register is represented by a `ro_to_ri` mapping: + * + * ``` + * ri_node --> ro_signal + * ``` + * where `ri_node` is the register input (a combinational output node), and `ro_signal` is + * the register output (a combinational input signal). + * + * If `rename_ri_using_node` is set to 1, then `ri_node` will be renamed (from its default + * name) using the node name. We write the following to the BLIF file: + * + * ``` + * .latch ri_node ro_signal + * ``` + * + * Otherwise, if `rename_ri_using_node` is set to 1, `ri_node` will be named by its default + * name, `li_`, where `idx` is the index of the register (based on the definition order + * when calling `create_ro`). Then we write: + * + * ``` + * .latch li_ ro_signal + * .names ri_node li_ + * 1 1 + * ``` + */ + uint32_t rename_ri_using_node = 0u; +}; + +/*! \brief Writes network in BLIF format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_pi` + * - `foreach_po` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `node_function` + * - `node_to_index` + * - `num_pis` + * - `num_pos` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_blif( Ntk const& ntk, std::ostream& os, write_blif_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanin_size_v, "Ntk does not implement the fanin_size method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_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_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + uint32_t num_latches{ 0 }; + if constexpr ( has_num_registers_v ) + { + num_latches = ntk.num_registers(); + } + + topo_view topo_ntk{ ntk }; + std::unordered_set defined_names; + + /* write model */ + os << ".model top\n"; + + /* write inputs */ + if ( topo_ntk.num_pis() > 0u ) + { + os << ".inputs "; + topo_ntk.foreach_ci( [&]( auto const& n, auto index ) { + if ( ( ( index + 1 ) <= topo_ntk.num_cis() - num_latches ) ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + signal const s = topo_ntk.make_signal( topo_ntk.node_to_index( n ) ); + std::string const input_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "pi{}", topo_ntk.get_node( s ) ); + os << input_name << ' '; + defined_names.insert( input_name ); /* we should not have collision here */ + } + else + { + std::string const input_name = fmt::format( "pi{}", topo_ntk.node_to_index( n ) ); + os << input_name << ' '; + defined_names.insert( input_name ); /* we should not have collision here */ + } + } + } ); + os << "\n"; + } + + /* write outputs */ + if ( topo_ntk.num_pos() > 0u ) + { + os << ".outputs "; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + (void)f; + if ( index < topo_ntk.num_cos() - num_latches ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + std::string const output_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : fmt::format( "po{}", index ); + os << output_name << ' '; + } + else + { + std::string const output_name = fmt::format( "po{}", index ); + os << output_name << ' '; + } + } + } ); + os << "\n"; + } + + if constexpr ( has_num_registers_v ) + { + if ( num_latches > 0u ) + { + uint32_t latch_idx = 0; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + if ( index >= topo_ntk.num_cos() - num_latches ) + { + os << ".latch "; + auto const ro_signal = topo_ntk.make_signal( topo_ntk.ro_at( latch_idx ) ); + auto const ri_signal = topo_ntk.ri_at( latch_idx ); + register_t latch_info = topo_ntk.register_at( latch_idx ); + if constexpr ( has_has_name_v && has_get_name_v ) + { + std::string const node_name = topo_ntk.has_name( ri_signal ) ? + topo_ntk.get_name( ri_signal ) + : fmt::format( "new_n{}", topo_ntk.get_node( ri_signal ) ); + std::string const latch_name = ps.rename_ri_using_node ? + node_name + : fmt::format( "li{}", latch_idx ); + std::string const ri_name = topo_ntk.has_output_name( index ) ? + topo_ntk.get_output_name( index ) + : latch_name; + std::string const ro_name = topo_ntk.has_name( ro_signal ) ? topo_ntk.get_name( ro_signal ) : fmt::format( "new_n{}", topo_ntk.get_node( ro_signal ) ); + os << fmt::format( "{} {} {} {} {}\n", ri_name, ro_name, latch_info.type, latch_info.control, latch_info.init ); + defined_names.insert( ro_name ); /* we should not have collision here */ + } + else + { + std::string const ri_name = ps.rename_ri_using_node? + fmt::format( "new_n{}", topo_ntk.get_node( topo_ntk.ri_at( latch_idx ) ) ) + : fmt::format( "li{}", latch_idx ); + std::string const ro_name = fmt::format( "new_n{}", topo_ntk.get_node( ro_signal ) ); + os << fmt::format( "{} {} {} {} {}\n", ri_name, ro_name, latch_info.type, latch_info.control, latch_info.init ); + defined_names.insert( ro_name ); /* we should not have collision here */ + } + latch_idx++; + } + } ); + } + } + + /* write constants */ + os << ".names new_n0\n"; + os << "0\n"; + defined_names.insert( "new_n0" ); /* we should not have collision here */ + + if ( topo_ntk.get_constant( false ) != topo_ntk.get_constant( true ) ) + { + os << ".names new_n1\n"; + os << "1\n"; + defined_names.insert( "new_n1" ); /* we should not have collision here */ + } + + /* write nodes */ + topo_ntk.foreach_node( [&]( auto const& n ) { + if ( topo_ntk.is_constant( n ) || topo_ntk.is_ci( n ) ) + return; /* continue */ + + /* write truth table of node */ + auto func = topo_ntk.node_function( n ); + + if ( isop( func ).size() == 0 ) /* constants */ + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + auto const s = topo_ntk.make_signal( n ); + std::string const constant_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "new_n{}", topo_ntk.get_node( s ) ); + os << fmt::format( ".names {}\n", constant_name ); + os << "0" << '\n'; + defined_names.insert( constant_name ); /* we should not have collision here */ + } + else + { + std::string const constant_name = fmt::format( "new_n{}", n ); + os << fmt::format( ".names {}\n", constant_name ); + os << "0" << '\n'; + defined_names.insert( constant_name ); /* we should not have collision here */ + } + return; + } + + os << fmt::format( ".names " ); + + /* write fanins of node */ + topo_ntk.foreach_fanin( n, [&]( auto const& f ) { + auto f_node = topo_ntk.get_node( f ); + if constexpr ( has_has_name_v && has_get_name_v ) + { + signal const s = topo_ntk.make_signal( f_node ); + std::string const fanin_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + os << fanin_name << ' '; + } + else + { + std::string const fanin_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{} ", f_node ) : fmt::format( "new_n{} ", f_node ); + os << fanin_name; + } + } ); + + /* write fanout of node */ + if constexpr ( has_has_name_v && has_get_name_v ) + { + auto const s = topo_ntk.make_signal( n ); + std::string const fanout_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : fmt::format( "new_n{}", topo_ntk.get_node( s ) ); + os << fanout_name << '\n'; + defined_names.insert( fanout_name ); /* we should not have collision here */ + } + else + { + std::string const fanout_name = fmt::format( "new_n{}", n ); + os << fanout_name << '\n'; + defined_names.insert( fanout_name ); /* we should not have collision here */ + } + + int count = 0; + for ( auto cube : isop( func ) ) + { + topo_ntk.foreach_fanin( n, [&]( auto const& f, auto index ) { + if ( cube.get_mask( index ) && topo_ntk.is_complemented( f ) ) + cube.flip_bit( index ); + } ); + + cube.print( topo_ntk.fanin_size( n ), os ); + os << " 1\n"; + count++; + } + } ); + + auto latch_idx = 0; + topo_ntk.foreach_co( [&]( auto const& f, auto index ) { + auto f_node = topo_ntk.get_node( f ); + auto const minterm_string = topo_ntk.is_complemented( f ) ? "0" : "1"; + + if ( index < topo_ntk.num_cos() - num_latches ) /* the signal f is a PO */ + { + /* the default name depends on whether the signal is a PI or a regular signal */ + std::string const node_default_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + + if constexpr ( has_has_name_v && has_get_name_v && has_has_output_name_v && has_get_output_name_v ) /* with name view */ + { + signal const s = topo_ntk.make_signal( topo_ntk.get_node( f ) ); + + /* then we overwrite the default name if we assigned names from name_view */ + std::string const node_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : node_default_name; + + /* get the default name of PO */ + std::string default_output_name = fmt::format( "po{}", index ); + + /* over write the name if we have name view */ + std::string const output_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : default_output_name; + + /* we need to bridge the nodes */ + if ( node_name != output_name && defined_names.find( output_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, output_name, minterm_string ); + defined_names.insert( output_name ); + } + } + else /* without name view */ + { + std::string const node_name = node_default_name; + std::string const output_name = fmt::format( "po{}", index ); + if ( node_name != output_name && defined_names.find( output_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, output_name, minterm_string ); + defined_names.insert( output_name ); + } + } + } + else /* the signal f is a RI */ + { + /* the default name depends on whether the signal is a PI or a regular signal */ + std::string const node_default_name = topo_ntk.is_pi( f_node ) ? fmt::format( "pi{}", f_node ) : fmt::format( "new_n{}", f_node ); + + if constexpr ( has_has_name_v && has_get_name_v && has_has_output_name_v && has_get_output_name_v ) /* with name view */ + { + signal const s = topo_ntk.make_signal( topo_ntk.get_node( f ) ); + + /* then we overwrite the default name if we assigned names from name_view */ + std::string const node_name = topo_ntk.has_name( s ) ? topo_ntk.get_name( s ) : node_default_name; + + /* get the default name of RI */ + std::string default_ri_name = ps.rename_ri_using_node ? node_name : fmt::format( "li{}", latch_idx ); + + /* overwrite the name if we have name view */ + std::string const ri_name = topo_ntk.has_output_name( index ) ? topo_ntk.get_output_name( index ) : default_ri_name; + + /* we need to bridge the nodes */ + if ( node_name != ri_name && defined_names.find( ri_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, ri_name, minterm_string ); + defined_names.insert( ri_name ); + } + } + else /* without name view */ + { + std::string const node_name = node_default_name; + + /* get the default name of RI */ + std::string ri_name = ps.rename_ri_using_node ? node_name : fmt::format( "li{}", latch_idx ); + + if ( node_name != ri_name && defined_names.find( ri_name ) == defined_names.end() ) + { + os << fmt::format( ".names {} {}\n{} 1\n", node_name, ri_name, minterm_string ); + defined_names.insert( ri_name ); + } + } + + latch_idx++; + } + } ); + + os << ".end\n"; + os << std::flush; +} + +/*! \brief Writes network in BLIF format into a file + * + * **Required network functions:** + * - `fanin_size` + * - `foreach_fanin` + * - `foreach_pi` + * - `foreach_po` + * - `get_node` + * - `is_constant` + * - `is_pi` + * - `node_function` + * - `node_to_index` + * - `num_pis` + * - `num_pos` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_blif( Ntk const& ntk, std::string const& filename, write_blif_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_blif( ntk, os, ps ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_dimacs.hpp b/include/mockturtle/io/write_dimacs.hpp new file mode 100644 index 0000000..092c40c --- /dev/null +++ b/include/mockturtle/io/write_dimacs.hpp @@ -0,0 +1,126 @@ +/* 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. + */ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 EPFL 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 write_dimacs.hpp + \brief Write networks CNF encoding to DIMACS format + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../algorithms/cnf.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Writes network into CNF DIMACS format + * + * It also adds unit clauses for the outputs. Therefore a satisfying solution + * is one that makes all outputs 1. + * + * \param ntk Logic network + * \param out Output stream + */ +template +void write_dimacs( Ntk const& ntk, std::ostream& out = std::cout ) +{ + std::stringstream clauses; + uint32_t num_clauses = 0u; + + const auto lits = generate_cnf( ntk, [&]( std::vector const& clause ) { + for ( auto lit : clause ) + { + const auto var = ( lit / 2 ) + 1; + const auto pol = lit % 2; + clauses << fmt::format( "{}{} ", pol ? "-" : "", var ); + } + clauses << fmt::format( "0\n" ); + ++num_clauses; + } ); + + for ( auto lit : lits ) + { + const auto var = ( lit / 2 ) + 1; + const auto pol = lit % 2; + clauses << fmt::format( "{}{} 0\n", pol ? "-" : "", var ); + ++num_clauses; + } + + out << fmt::format( "p cnf {} {}\n{}", ntk.size(), num_clauses, clauses.str() ); +} + +/*! \brief Writes network into CNF DIMACS format + * + * It also adds unit clauses for the outputs. Therefore a satisfying solution + * is one that makes all outputs 1. + * + * \param ntk Logic network + * \param filename Filename + */ +template +void write_dimacs( Ntk const& ntk, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_dimacs( ntk, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_dot.hpp b/include/mockturtle/io/write_dot.hpp new file mode 100644 index 0000000..edbed29 --- /dev/null +++ b/include/mockturtle/io/write_dot.hpp @@ -0,0 +1,456 @@ +/* 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 write_dot.hpp + \brief Write graphical representation of networks to DOT format + + \author Heinz Riener + \author Mathias Soeken + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "../traits.hpp" +#include "../views/depth_view.hpp" + +namespace mockturtle +{ + +template +class default_dot_drawer +{ +public: + virtual ~default_dot_drawer() + { + } + +public: /* callbacks */ + virtual std::string node_label( Ntk const& ntk, node const& n ) const + { + return std::to_string( ntk.node_to_index( n ) ); + } + + virtual std::string node_shape( Ntk const& ntk, node const& n ) const + { + if ( ntk.is_constant( n ) ) + { + return "box"; + } + else if ( ntk.is_ci( n ) ) + { + return "triangle"; + } + else + { + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + return "box"; + } + } + return "ellipse"; + } + } + + virtual uint32_t node_level( Ntk const& ntk, node const& n ) const + { + if ( !_depth_ntk ) + { + _depth_ntk = std::make_shared>( ntk ); + } + + return _depth_ntk->level( n ); + } + + virtual std::string po_shape( Ntk const& ntk, uint32_t i ) const + { + (void)ntk; + (void)i; + return "invtriangle"; + } + + virtual std::string node_fillcolor( Ntk const& ntk, node const& n ) const + { + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + if ( ntk.fanout_size( n ) > 1 ) + return "lightcoral"; + else + return "lightskyblue"; + } + } + return ( ntk.is_constant( n ) || ntk.is_ci( n ) ) ? "snow2" : "white"; + } + + virtual std::string po_fillcolor( Ntk const& ntk, uint32_t i ) const + { + (void)ntk; + (void)i; + return "snow2"; + } + + virtual bool draw_signal( Ntk const& ntk, node const& n, signal const& f ) const + { + (void)ntk; + (void)n; + (void)f; + if constexpr ( is_buffered_network_type_v ) + { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return false; + } + return true; + } + + virtual std::string signal_style( Ntk const& ntk, signal const& f ) const + { + return ntk.is_complemented( f ) ? "dashed" : "solid"; + } + +private: + mutable std::shared_ptr> _depth_ntk; +}; + +template +class gate_dot_drawer : public default_dot_drawer +{ +public: + virtual std::string node_label( Ntk const& ntk, node const& n ) const override + { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + return "AND"; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + return "OR"; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + std::string label{ "MAJ" }; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + const auto v = ntk.constant_value( ntk.get_node( f ) ) != ntk.is_complemented( f ); + label = v ? "OR" : "AND"; + return false; + } + return true; + } ); + return label; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + return "AND"; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + return "OR"; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return "XOR"; + } + } + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) && !ntk.is_ci( n ) ) + { + return "BUF"; + } + } + + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return "CROSS"; + } + } + + return default_dot_drawer::node_label( ntk, n ); + } + + virtual std::string node_fillcolor( Ntk const& ntk, node const& n ) const override + { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + return "lightcoral"; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + return "palegreen2"; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + std::string color{ "lightsalmon" }; + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + const auto v = ntk.constant_value( ntk.get_node( f ) ) != ntk.is_complemented( f ); + color = v ? "palegreen2" : "lightcoral"; + return false; + } + return true; + } ); + return color; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + return "lightcoral"; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + return "palegreen2"; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return "lightskyblue"; + } + } + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) && !ntk.is_ci( n ) ) + { + return "palegoldenrod"; + } + } + + if constexpr ( has_is_crossing_v ) + { + if ( ntk.is_crossing( n ) ) + { + return "palegoldenrod"; + } + } + + return default_dot_drawer::node_fillcolor( ntk, n ); + } + + virtual bool draw_signal( Ntk const& ntk, node const& n, signal const& f ) const override + { + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + return !ntk.is_constant( ntk.get_node( f ) ); + } + } + + return default_dot_drawer::draw_signal( ntk, n, f ); + } +}; + +/*! \brief Writes network in DOT format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - is_constant + * - is_ci + * - foreach_node + * - foreach_fanin + * - foreach_po + * + * \param ntk Network + * \param os Output stream + */ +template> +void write_dot( Ntk const& ntk, std::ostream& os, Drawer const& drawer = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + + std::stringstream nodes, edges, levels; + + std::vector> level_to_node_indexes; + + ntk.foreach_node( [&]( auto const& n ) { + nodes << fmt::format( "{} [label=\"{}\",shape={},style=filled,fillcolor={}]\n", + ntk.node_to_index( n ), + drawer.node_label( ntk, n ), + drawer.node_shape( ntk, n ), + drawer.node_fillcolor( ntk, n ) ); + if ( !ntk.is_constant( n ) && !ntk.is_ci( n ) ) + { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( !drawer.draw_signal( ntk, n, f ) ) + return true; + edges << fmt::format( "{} -> {} [style={}]\n", + ntk.node_to_index( ntk.get_node( f ) ), + ntk.node_to_index( n ), + drawer.signal_style( ntk, f ) ); + return true; + } ); + } + + const auto lvl = drawer.node_level( ntk, n ); + if ( level_to_node_indexes.size() <= lvl ) + { + level_to_node_indexes.resize( lvl + 1 ); + } + level_to_node_indexes[lvl].push_back( ntk.node_to_index( n ) ); + } ); + + for ( auto const& indexes : level_to_node_indexes ) + { + levels << "{rank = same; "; + std::copy( indexes.begin(), indexes.end(), std::ostream_iterator( levels, "; " ) ); + levels << "}\n"; + } + + levels << "{rank = same; "; + ntk.foreach_po( [&]( auto const& f, auto i ) { + nodes << fmt::format( "po{} [shape={},style=filled,fillcolor={}]\n", i, drawer.po_shape( ntk, i ), drawer.po_fillcolor( ntk, i ) ); + edges << fmt::format( "{} -> po{} [style={}]\n", + ntk.node_to_index( ntk.get_node( f ) ), + i, + drawer.signal_style( ntk, f ) ); + levels << fmt::format( "po{}; ", i ); + } ); + levels << "}\n"; + + os << "digraph {\n" + << "rankdir=BT;\n" + << nodes.str() << edges.str() << levels.str() << "}\n"; +} + +/*! \brief Writes network in DOT format into a file + * + * **Required network functions:** + * - is_constant + * - is_ci + * - foreach_node + * - foreach_fanin + * - foreach_po + * + * \param ntk Network + * \param filename Filename + */ +template> +void write_dot( Ntk const& ntk, std::string const& filename, Drawer const& drawer = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_dot( ntk, os, drawer ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_genlib.hpp b/include/mockturtle/io/write_genlib.hpp new file mode 100644 index 0000000..9ef31ee --- /dev/null +++ b/include/mockturtle/io/write_genlib.hpp @@ -0,0 +1,105 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file write_genlib.hpp + \brief Writes library of gates to GENLIB format + + \author Alessandro tempia Calvino +*/ + +#pragma once + +#include "genlib_reader.hpp" +#include "../traits.hpp" + +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Writes library of gates to GENLIB format + * + * An overloaded variant exists that writes the network into a file. + * + * \param gates List of gates + * \param os Output stream + */ +void write_genlib( std::vector const& gates, std::ostream& os ) +{ + /* compute pad spaces */ + size_t max_name_size = 0; + for ( gate const& g : gates ) + { + max_name_size = std::max( max_name_size, g.name.size() ); + } + ++max_name_size; + + for ( gate const& g : gates ) + { + os << "GATE "; + std::string name = g.name + std::string( max_name_size - g.name.size(), ' ' ); + os << fmt::format( "{} {:>5.4f} {}={};\n", name, g.area, g.output_name, g.expression ); + + for ( pin const& p : g.pins ) + { + std::string phase; + if ( p.phase == phase_type::INV ) + phase = "INV"; + else if ( p.phase == phase_type::NONINV ) + phase = "NONINV"; + else + phase = "UNKNOWN"; + + os << fmt::format( "\tPIN {} {} {:>3} {:>3} {:>6.4f} {:>6.4f} {:>6.4f} {:>6.4f}\n", + p.name, + phase, + ( uint32_t )p.input_load, + ( uint32_t )p.max_load, + p.rise_block_delay, + p.rise_fanout_delay, + p.fall_block_delay, + p.fall_fanout_delay ); + } + } +} + +/*! \brief Write library of gates into a GENLIB file + * + * \param gates List of gates + * \param filename Filename + */ +void write_genlib( std::vector const& gates, std::string const& filename ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_genlib( gates, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_patterns.hpp b/include/mockturtle/io/write_patterns.hpp new file mode 100644 index 0000000..f46ad9e --- /dev/null +++ b/include/mockturtle/io/write_patterns.hpp @@ -0,0 +1,87 @@ +/* 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 write_patterns.hpp + \brief Write simulation patterns + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../algorithms/simulation.hpp" + +namespace mockturtle +{ + +/*! \brief Writes simulation patterns + * + * The output contains `num_pis()` lines, each line contains a stream of + * simulation values of a primary input, represented in hexadecimal. + * + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns + * \param out Output stream + */ +template +void write_patterns( Simulator const& sim, std::ostream& out = std::cout ) +{ + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + auto const& patterns = sim.get_patterns(); + for ( auto i = 0u; i < patterns.size(); ++i ) + { + out << kitty::to_hex( patterns.at( i ) ) << "\n"; + } +} + +/*! \brief Writes simulation patterns + * + * The output contains `num_pis()` lines, each line contains a stream of + * simulation values of a primary input, represented in hexadecimal. + * + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns + * \param filename Filename + */ +template +void write_patterns( Simulator const& sim, std::string const& filename ) +{ + static_assert( std::is_same_v || std::is_same_v, "This function is specialized for partial_simulator or bit_packed_simulator" ); + + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_patterns( sim, os ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/io/write_verilog.hpp b/include/mockturtle/io/write_verilog.hpp new file mode 100644 index 0000000..607b41d --- /dev/null +++ b/include/mockturtle/io/write_verilog.hpp @@ -0,0 +1,1315 @@ +/* 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 write_verilog.hpp + \brief Write networks to structural Verilog format + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/string_utils.hpp" +#include "../views/binding_view.hpp" +#include "../views/topo_view.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +using namespace std::string_literals; + +namespace detail +{ + +template +std::vector> +format_fanin( Ntk const& ntk, node const& n, node_map& node_names ) +{ + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if constexpr ( is_crossed_network_type_v ) + { + std::string postfix = ntk.is_crossing( ntk.get_node( f ) ) ? ( ntk.is_second( f ) ? "_2" : "_1" ) : ""; + children.emplace_back( std::make_pair( ntk.get_fanin_negations( n )[i], node_names[f] + postfix ) ); + } + else + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f] ) ); + } + } ); + return children; +} + +template +std::vector> +format_fanin( Ntk const& ntk, node const& n, node_map, Ntk>& node_names ) +{ + std::vector> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { + if constexpr ( is_crossed_network_type_v ) + { + std::string postfix = ntk.is_crossing( ntk.get_node( f ) ) ? ( ntk.is_second( f ) ? "_2" : "_1" ) : ""; + children.emplace_back( std::make_pair( ntk.get_fanin_negations( n )[i], node_names[f].front() + postfix ) ); + } + else if constexpr ( has_is_multioutput_v ) + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f][ntk.get_output_pin( f )] ) ); + } + else + { + children.emplace_back( std::make_pair( ntk.is_complemented( f ), node_names[f].front() ) ); + } + } ); + return children; +} + +template +struct verilog_writer_signal_hash +{ + uint64_t operator()( const Signal& f ) const + { + return f.data; + } +}; + +} // namespace detail + +struct write_verilog_params +{ + std::optional module_name{ std::nullopt }; + std::vector> input_names; + std::vector> output_names; + bool verbose{ false }; +}; + +/*! \brief Writes network in structural Verilog format into output stream + * + * An overloaded variant exists that writes the network into a file. + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `is_and` + * - `is_or` + * - `is_xor` + * - `is_xor3` + * - `is_maj` + * - `is_ite` + * - `node_to_index` + * + * \param ntk Network + * \param os Output stream + */ +template +void write_verilog( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant 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_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_is_or_v, "Ntk does not implement the is_or method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_ite_v, "Ntk does not implement the is_ite method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + if constexpr ( is_buffered_network_type_v ) + { + writer.on_module_begin( "buffer", { "i" }, { "o" } ); + writer.on_input( "i" ); + writer.on_output( "o" ); + writer.on_module_end(); + + writer.on_module_begin( "inverter", { "i" }, { "o" } ); + writer.on_input( "i" ); + writer.on_output( "o" ); + writer.on_module_end(); + } + if constexpr ( is_crossed_network_type_v ) + { + writer.on_module_begin( "crossing", { "i1", "i2" }, { "o1", "o2" } ); + writer.on_input( std::vector( { "i1", "i2" } ) ); + writer.on_output( std::vector( { "o1", "o2" } ) ); + writer.on_module_end(); + } + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + (void)i; + xs.emplace_back( fmt::format( "x{}", index ) ); + } ); + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + (void)o; + ys.emplace_back( fmt::format( "y{}", index ) ); + } ); + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + std::vector ws; + + if constexpr ( is_buffered_network_type_v ) + { + static_assert( has_is_buf_v, "Ntk does not implement the is_buf method" ); + ntk.foreach_node( [&]( auto const& n ) { + if ( ntk.fanin_size( n ) > 0 ) + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } ); + } + else + { + ntk.foreach_gate( [&]( auto const& n ) { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } ); + } + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + node_map node_names( ntk ); + node_names[ntk.get_constant( false )] = "1'b0"; + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + node_names[ntk.get_constant( true )] = "1'b1"; + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n] = xs[i]; + } ); + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + return true; + + /* assign a name */ + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + + if constexpr ( has_is_buf_v ) + { + if ( ntk.is_buf( n ) ) + { + auto const fanin = detail::format_fanin( ntk, n, node_names ); + assert( fanin.size() == 1 ); + std::vector> args; + if ( fanin[0].first ) /* input negated */ + { + args.emplace_back( std::make_pair( "i", fanin[0].second ) ); + args.emplace_back( std::make_pair( "o", node_names[n] ) ); + writer.on_module_instantiation( "inverter", {}, "inv_" + node_names[n], args ); + } + else + { + args.emplace_back( std::make_pair( "i", fanin[0].second ) ); + args.emplace_back( std::make_pair( "o", node_names[n] ) ); + writer.on_module_instantiation( "buffer", {}, "buf_" + node_names[n], args ); + } + return true; + } + } + + if constexpr ( is_crossed_network_type_v ) + { + if ( ntk.is_crossing( n ) ) + { + auto const fanin = detail::format_fanin( ntk, n, node_names ); + assert( fanin.size() == 2 ); + std::vector> args; + args.emplace_back( std::make_pair( "i1", fanin[0].second ) ); + args.emplace_back( std::make_pair( "i2", fanin[1].second ) ); + args.emplace_back( std::make_pair( "o1", node_names[n] + "_1" ) ); + args.emplace_back( std::make_pair( "o2", node_names[n] + "_2" ) ); + writer.on_module_instantiation( "crossing", {}, "cross_" + node_names[n], args ); + return true; + } + } + + if ( ntk.is_and( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "&" ); + } + else if ( ntk.is_or( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "|" ); + } + else if ( ntk.is_xor( n ) || ntk.is_xor3( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "^" ); + } + else if ( ntk.is_maj( n ) ) + { + std::array, 3> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { children[i] = f; } ); + + if ( ntk.is_constant( ntk.get_node( children[0u] ) ) ) + { + std::vector> vs; + vs.emplace_back( std::make_pair( ntk.is_complemented( children[1u] ), node_names[ntk.get_node( children[1u] )] ) ); + vs.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + + if ( ntk.is_complemented( children[0u] ) ) + { + // or + writer.on_assign( node_names[n], { vs[0u], vs[1u] }, "|" ); + } + else + { + // and + writer.on_assign( node_names[n], { vs[0u], vs[1u] }, "&" ); + } + } + else + { + writer.on_assign_maj3( node_names[n], detail::format_fanin( ntk, n, node_names ) ); + } + } + else if ( ntk.is_ite( n ) ) + { + std::array, 3> children; + ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { children[i] = f; } ); + + if ( ntk.is_constant( ntk.get_node( children[1u] ) ) ) + { + assert( children[1u] == ntk.get_constant( false ) ); + // a ? 0 : c = ~a & c + std::vector> ins; + ins.emplace_back( std::make_pair( !ntk.is_complemented( children[0u] ), node_names[ntk.get_node( children[0u] )] ) ); + ins.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + writer.on_assign( node_names[n], ins, "&" ); + } + else if ( ntk.get_node( children[1u] ) == ntk.get_node( children[2u] ) ) + { + assert( !ntk.is_complemented( children[1u] ) && ntk.is_complemented( children[2u] ) ); + // a ? b : ~b = a ^ ~b + std::vector> ins; + ins.emplace_back( std::make_pair( ntk.is_complemented( children[0u] ), node_names[ntk.get_node( children[0u] )] ) ); + ins.emplace_back( std::make_pair( ntk.is_complemented( children[2u] ), node_names[ntk.get_node( children[2u] )] ) ); + writer.on_assign( node_names[n], ins, "^" ); + } + else + { + writer.on_assign_mux21( node_names[n], detail::format_fanin( ntk, n, node_names ) ); + } + } + else + { + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "&" ); + return true; + } + } + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "|" ); + return true; + } + } + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + writer.on_assign( node_names[n], detail::format_fanin( ntk, n, node_names ), "^" ); + return true; + } + } + if constexpr ( has_is_function_v ) + { + fmt::print( stderr, "[w] unknown node function {}\n", kitty::to_hex( ntk.node_function( n ) ) ); + } + writer.on_assign_unknown_gate( node_names[n] ); + } + + return true; + } ); + + ntk.foreach_po( [&]( auto const& f, auto i ) { + writer.on_assign_po( ys[i], std::make_pair( ntk.is_complemented( f ), node_names[f] ) ); + } ); + + writer.on_module_end(); +} + +/*! \brief Writes mapped network in structural Verilog format into output stream + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_binding` + * - `get_binding_index` + * + * \param ntk Mapped network + * \param os Output stream + * \param ps Verilog parameters + */ +template +void write_verilog_with_binding( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_has_binding_v, "Ntk does not implement the has_binding method" ); + static_assert( has_get_binding_index_v, "Ntk does not implement the get_binding_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + xs.emplace_back( fmt::format( "x{}", i ) ); + } + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + ys.emplace_back( fmt::format( "y{}", i ) ); + } + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + /* compute which nodes are POs and register index */ + node_map, Ntk, std::unordered_map>> po_nodes( ntk ); + ntk.foreach_po( [&]( auto const& f, auto i ) { + po_nodes[f].push_back( i ); + } ); + + std::vector ws; + node_map node_names( ntk ); + + /* constants */ + if ( ntk.has_binding( ntk.get_constant( false ) ) ) + { + node_names[ntk.get_constant( false )] = fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( false ) ) ); + if ( !po_nodes.has( ntk.get_constant( false ) ) ) + { + ws.emplace_back( node_names[ntk.get_constant( false )] ); + } + } + else + { + node_names[ntk.get_constant( false )] = "1'b0"; + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( ntk.has_binding( ntk.get_constant( true ) ) ) + { + node_names[ntk.get_constant( true )] = fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( true ) ) ); + if ( !po_nodes.has( ntk.get_constant( true ) ) ) + { + ws.emplace_back( node_names[ntk.get_constant( true )] ); + } + } + else + { + node_names[ntk.get_constant( true )] = "1'b1"; + } + } + + /* add wires */ + ntk.foreach_gate( [&]( auto const& n ) { + if ( !po_nodes.has( n ) ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } ); + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n] = xs[i]; + } ); + + auto const& gates = ntk.get_library(); + + int nDigits = (int)std::floor( std::log10( ntk.num_gates() ) ); + unsigned int length = 0; + unsigned counter = 0; + + for ( auto const& gate : gates ) + { + length = std::max( length, static_cast( gate.name.length() ) ); + } + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + if ( po_nodes.has( n ) ) + { + node_names[n] = ys[po_nodes[n][0]]; + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + } + + if ( ntk.has_binding( n ) ) + { + auto const& gate = gates[ntk.get_binding_index( n )]; + std::string name = gate.name; + + int digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + auto fanin_names = detail::format_fanin( ntk, n, node_names ); + std::vector> args; + + auto i = 0; + for ( auto pair : fanin_names ) + { + args.emplace_back( std::make_pair( gate.pins[i++].name, pair.second ) ); + } + args.emplace_back( std::make_pair( gate.output_name, node_names[n] ) ); + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + + /* if node drives multiple POs, duplicate */ + if ( po_nodes.has( n ) && po_nodes[n].size() > 1 ) + { + if ( ps.verbose ) + { + std::cerr << "[i] node " << n << " driving multiple POs has been duplicated.\n"; + } + auto const& po_list = po_nodes[n]; + for ( auto i = 1u; i < po_list.size(); ++i ) + { + digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( gate.output_name, ys[po_list[i]] ); + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + std::cerr << "[e] internal node " << n << " is not mapped.\n"; + } + + return true; + } ); + + writer.on_module_end(); +} + +/*! \brief Writes mapped network in structural Verilog format into output stream + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_cell` + * - `get_cell_index` + * + * \param ntk Mapped network + * \param os Output stream + * \param ps Verilog parameters + */ +template +void write_verilog_with_cell( Ntk const& ntk, std::ostream& os, write_verilog_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_has_cell_v, "Ntk does not implement the has_cell method" ); + static_assert( has_get_cell_index_v, "Ntk does not implement the get_cell_index method" ); + + assert( ntk.is_combinational() && "Network has to be combinational" ); + + lorina::verilog_writer writer( os ); + + std::vector xs, inputs; + if ( ps.input_names.empty() ) + { + if constexpr ( has_has_name_v && has_get_name_v ) + { + ntk.foreach_pi( [&]( auto const& i, uint32_t index ) { + if ( ntk.has_name( ntk.make_signal( i ) ) ) + { + xs.emplace_back( ntk.get_name( ntk.make_signal( i ) ) ); + } + else + { + xs.emplace_back( fmt::format( "x{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pis(); ++i ) + { + xs.emplace_back( fmt::format( "x{}", i ) ); + } + } + inputs = xs; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.input_names ) + { + inputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + xs.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pis() ) + { + std::cerr << "[e] input names do not partition all inputs\n"; + } + } + + std::vector ys, outputs; + if ( ps.output_names.empty() ) + { + if constexpr ( has_has_output_name_v && has_get_output_name_v ) + { + ntk.foreach_po( [&]( auto const& o, uint32_t index ) { + if ( ntk.has_output_name( index ) ) + { + ys.emplace_back( ntk.get_output_name( index ) ); + } + else + { + ys.emplace_back( fmt::format( "y{}", index ) ); + } + } ); + } + else + { + for ( auto i = 0u; i < ntk.num_pos(); ++i ) + { + ys.emplace_back( fmt::format( "y{}", i ) ); + } + } + outputs = ys; + } + else + { + uint32_t ctr{ 0u }; + for ( auto const& [name, width] : ps.output_names ) + { + outputs.emplace_back( name ); + ctr += width; + for ( auto i = 0u; i < width; ++i ) + { + ys.emplace_back( fmt::format( "{}[{}]", name, i ) ); + } + } + if ( ctr != ntk.num_pos() ) + { + std::cerr << "[e] output names do not partition all outputs\n"; + } + } + + /* compute which nodes are POs and register index */ + uint32_t additional_buffers = 0; + std::unordered_map, detail::verilog_writer_signal_hash> po_nodes; + ntk.foreach_po( [&]( auto const& f, auto i ) { + po_nodes[f ^ ntk.is_complemented( f )].push_back( i ); + additional_buffers += po_nodes[f ^ ntk.is_complemented( f )].size() > 1 ? 1 : 0; + } ); + + std::vector ws; + node_map, Ntk> node_names( ntk ); + + /* constants */ + if ( ntk.has_cell( ntk.get_node( ntk.get_constant( false ) ) ) ) + { + if ( po_nodes.find( ntk.get_constant( false ) ) == po_nodes.end() ) + { + node_names[ntk.get_node( ntk.get_constant( false ) )].push_back( fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( false ) ) ) ); + ws.emplace_back( node_names[ntk.get_constant( false )].front() ); + } + } + else + { + node_names[ntk.get_node( ntk.get_constant( false ) )].push_back( "1'b0" ); + } + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + if ( ntk.has_cell( ntk.get_node( ntk.get_constant( true ) ) ) ) + { + if ( po_nodes.find( ntk.get_constant( true ) ) == po_nodes.end() ) + { + node_names[ntk.get_node( ntk.get_constant( true ) )].push_back( fmt::format( "n{}", ntk.node_to_index( ntk.get_constant( true ) ) ) ); + ws.emplace_back( node_names[ntk.get_constant( true )].front() ); + } + } + else + { + node_names[ntk.get_constant( true )].push_back( "1'b1" ); + } + } + + /* add wires */ + ntk.foreach_gate( [&]( auto const& n ) { + if constexpr ( has_is_multioutput_v ) + { + /* create wire for each individual output */ + if ( !ntk.is_multioutput( n ) && po_nodes.find( ntk.make_signal( n ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + return; + } + + for ( uint32_t i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( po_nodes.find( ntk.make_signal( n, i ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}_{}", ntk.node_to_index( n ), i ) ); + } + } + + return; + } + + if ( po_nodes.find( ntk.make_signal( n ) ) == po_nodes.end() ) + { + ws.emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } ); + + std::string module_name = "top"; + if ( ps.module_name ) + { + module_name = *ps.module_name; + } + else + { + if constexpr ( has_get_network_name_v ) + { + if ( ntk.get_network_name().length() > 0 ) + { + module_name = ntk.get_network_name(); + } + } + } + writer.on_module_begin( module_name, inputs, outputs ); + if ( ps.input_names.empty() ) + { + writer.on_input( xs ); + } + else + { + for ( auto const& [name, width] : ps.input_names ) + { + writer.on_input( width, name ); + } + } + if ( ps.output_names.empty() ) + { + writer.on_output( ys ); + } + else + { + for ( auto const& [name, width] : ps.output_names ) + { + writer.on_output( width, name ); + } + } + if ( !ws.empty() ) + { + writer.on_wire( ws ); + } + + ntk.foreach_pi( [&]( auto const& n, auto i ) { + node_names[n].push_back( xs[i] ); + } ); + + auto const& cells = ntk.get_library(); + + /* get buffer */ + uint32_t buf_id = UINT32_MAX; + double buf_area = std::numeric_limits::max(); + for ( uint32_t i = 0; i < cells.size(); ++i ) + { + auto const& g = cells[i].gates.front(); + if ( cells[i].gates.size() > 1 || g.num_vars != 1 ) + continue; + if ( g.function._bits[0] != 0x2 ) + continue; + + if ( buf_id == UINT32_MAX || g.area < buf_area ) + { + buf_id = i; + buf_area = g.area; + } + } + + int nDigits = (int)std::floor( std::log10( ntk.num_gates() + additional_buffers ) ); + unsigned int length = 0; + unsigned counter = 0; + + for ( auto const& cell : cells ) + { + length = std::max( length, static_cast( cell.name.length() ) ); + } + + topo_view ntk_topo{ ntk }; + + ntk_topo.foreach_node( [&]( auto const& n ) { + /* load names of n */ + if constexpr ( has_is_multioutput_v ) + { + /* create wire for each individual output */ + if ( !ntk.is_multioutput( n ) ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() ) + { + node_names[n].emplace_back( ys[el->second.front()] ); + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n].emplace_back( fmt::format( "n{}", ntk.node_to_index( n ) ) ); + } + } + else + { + for ( uint32_t i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n, i ) ); el != po_nodes.end() ) + { + node_names[n].emplace_back( ys[el->second.front()] ); + } + else + { + node_names[n].emplace_back( fmt::format( "n{}_{}", ntk.node_to_index( n ), i ) ); + } + } + } + } + else + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() ) + { + node_names[n] = ys[el->second.front()]; + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + node_names[n] = fmt::format( "n{}", ntk.node_to_index( n ) ); + } + } + + if ( ntk.has_cell( n ) ) + { + auto const& cell = cells[ntk.get_cell_index( n )]; + std::string name = cell.name; + + int digits = counter == 0 ? 0 : (int)std::floor( std::log10( counter ) ); + auto fanin_names = detail::format_fanin( ntk, n, node_names ); + std::vector> args; + + auto i = 0; + for ( auto pair : fanin_names ) + { + args.emplace_back( std::make_pair( cell.gates[0].pins[i++].name, pair.second ) ); + } + + assert( cell.gates.size() == node_names[n].size() ); + + i = 0; + for ( auto const& ogate : cell.gates ) + { + args.emplace_back( std::make_pair( ogate.output_name, node_names[n][i++] ) ); + } + + writer.on_module_instantiation( name.append( std::string( length - name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + + /* if node drives multiple POs, buffer */ + if constexpr ( has_is_multioutput_v ) + { + i = 0; + for ( i = 0; i < ntk.num_outputs( n ); ++i ) + { + if ( auto el = po_nodes.find( ntk.make_signal( n, i ) ); el != po_nodes.end() && el->second.size() > 1 ) + { + if ( buf_id == UINT32_MAX ) + { + std::cerr << "[e] Error: cell library does not contain a buffer cell\n"; + return false; + } + + if ( ps.verbose ) + { + std::cerr << "[i] Buffering node " << n << " driving multiple POs.\n"; + } + + gate const& g = cells[buf_id].gates.front(); + std::string buf_name = g.name; + auto const& po_list = el->second; + args.clear(); + args.emplace_back( std::make_pair( g.pins.front().name, node_names[n].at( i ) ) ); + args.emplace_back( std::make_pair( "", "" ) ); + for ( uint32_t j = 1u; j < po_list.size(); ++j ) + { + digits = counter == (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( g.output_name, ys[po_list[j]] ); + + writer.on_module_instantiation( buf_name.append( std::string( length - buf_name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + } + else + { + if ( auto el = po_nodes.find( ntk.make_signal( n ) ); el != po_nodes.end() && el->second.size() > 1 ) + { + if ( buf_id == UINT32_MAX ) + { + std::cerr << "[e] Error: cell library does not contain a buffer cell\n"; + return false; + } + + std::cerr << "[i] Buffering node " << n << " driving multiple POs.\n"; + + gate const& g = cells[buf_id].gates.front(); + std::string buf_name = g.name; + auto const& po_list = el->second; + args.clear(); + args.emplace_back( std::make_pair( g.pins.front().name, node_names[n].front() ) ); + args.emplace_back( std::make_pair( "", "" ) ); + for ( i = 1u; i < po_list.size(); ++i ) + { + digits = counter == (int)std::floor( std::log10( counter ) ); + args[args.size() - 1] = std::make_pair( g.output_name, ys[po_list[i]] ); + + writer.on_module_instantiation( buf_name.append( std::string( length - buf_name.length(), ' ' ) ), + {}, + std::string( "g" ) + std::string( nDigits - digits, '0' ) + std::to_string( counter ), + args ); + ++counter; + } + } + } + } + else if ( !ntk.is_constant( n ) && !ntk.is_pi( n ) ) + { + std::cerr << "[e] internal node " << n << " is not mapped.\n"; + } + + return true; + } ); + + writer.on_module_end(); +} + +/*! \brief Writes network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `is_and` + * - `is_or` + * - `is_xor` + * - `is_xor3` + * - `is_maj` + * - `node_to_index` + * + * \param ntk Network + * \param filename Filename + */ +template +void write_verilog( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog( ntk, os, ps ); + os.close(); +} + +/*! \brief Writes mapped network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_binding` + * - `get_binding_index` + * + * \param ntk Network (binding_view) + * \param filename Filename + */ +template +void write_verilog_with_binding( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog_with_binding( ntk, os, ps ); + os.close(); +} + +/*! \brief Writes mapped network in structural Verilog format into a file + * + * **Required network functions:** + * - `num_pis` + * - `num_pos` + * - `foreach_pi` + * - `foreach_node` + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `is_pi` + * - `node_to_index` + * - `has_cell` + * - `get_cell_index` + * + * \param ntk Network (cell_view) + * \param filename Filename + */ +template +void write_verilog_with_cell( Ntk const& ntk, std::string const& filename, write_verilog_params const& ps = {} ) +{ + std::ofstream os( filename.c_str(), std::ofstream::out ); + write_verilog_with_cell( ntk, os, ps ); + os.close(); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/mockturtle.hpp b/include/mockturtle/mockturtle.hpp new file mode 100644 index 0000000..030bfdc --- /dev/null +++ b/include/mockturtle/mockturtle.hpp @@ -0,0 +1,218 @@ +/* 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 mockturtle.hpp + \brief Main header file for mockturtle +*/ + +#pragma once + +#include "mockturtle/algorithms/aig_resub.hpp" +#include "mockturtle/algorithms/akers_synthesis.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_assumptions.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_db.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_fanout_resyn.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_node_resyn.hpp" +#include "mockturtle/algorithms/aqfp/aqfp_resynthesis.hpp" +#include "mockturtle/algorithms/aqfp/buffer_insertion.hpp" +#include "mockturtle/algorithms/aqfp/buffer_verification.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_cost.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_gen.hpp" +#include "mockturtle/algorithms/aqfp/detail/dag_util.hpp" +#include "mockturtle/algorithms/aqfp/detail/db_builder.hpp" +#include "mockturtle/algorithms/aqfp/detail/db_utils.hpp" +#include "mockturtle/algorithms/aqfp/detail/npn_cache.hpp" +#include "mockturtle/algorithms/aqfp/detail/partial_dag.hpp" +#include "mockturtle/algorithms/aqfp/mig_algebraic_rewriting_splitters.hpp" +#include "mockturtle/algorithms/aqfp/mig_resub_splitters.hpp" +#include "mockturtle/algorithms/balancing.hpp" +#include "mockturtle/algorithms/balancing/esop_balancing.hpp" +#include "mockturtle/algorithms/balancing/sop_balancing.hpp" +#include "mockturtle/algorithms/balancing/utils.hpp" +#include "mockturtle/algorithms/bi_decomposition.hpp" +#include "mockturtle/algorithms/cell_window.hpp" +#include "mockturtle/algorithms/circuit_validator.hpp" +#include "mockturtle/algorithms/cleanup.hpp" +#include "mockturtle/algorithms/cnf.hpp" +#include "mockturtle/algorithms/collapse_mapped.hpp" +#include "mockturtle/algorithms/cover_to_graph.hpp" +#include "mockturtle/algorithms/cut_enumeration.hpp" +#include "mockturtle/algorithms/cut_enumeration/cnf_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/exact_map_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/gia_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/mf_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/spectr_cut.hpp" +#include "mockturtle/algorithms/cut_enumeration/tech_map_cut.hpp" +#include "mockturtle/algorithms/cut_rewriting.hpp" +#include "mockturtle/algorithms/decomposition.hpp" +#include "mockturtle/algorithms/detail/database_generator.hpp" +#include "mockturtle/algorithms/detail/mffc_utils.hpp" +#include "mockturtle/algorithms/detail/minmc_xags.hpp" +#include "mockturtle/algorithms/detail/resub_utils.hpp" +#include "mockturtle/algorithms/detail/switching_activity.hpp" +#include "mockturtle/algorithms/dont_cares.hpp" +#include "mockturtle/algorithms/dsd_decomposition.hpp" +#include "mockturtle/algorithms/equivalence_checking.hpp" +#include "mockturtle/algorithms/equivalence_classes.hpp" +#include "mockturtle/algorithms/exact_mc_synthesis.hpp" +#include "mockturtle/algorithms/exorcism.hpp" +#include "mockturtle/algorithms/experimental/boolean_optimization.hpp" +#include "mockturtle/algorithms/experimental/cost_generic_resub.hpp" +#include "mockturtle/algorithms/experimental/cost_resyn.hpp" +#include "mockturtle/algorithms/experimental/sim_resub.hpp" +#include "mockturtle/algorithms/experimental/window_resub.hpp" +#include "mockturtle/algorithms/extract_linear.hpp" +#include "mockturtle/algorithms/functional_reduction.hpp" +#include "mockturtle/algorithms/gates_to_nodes.hpp" +#include "mockturtle/algorithms/klut_to_graph.hpp" +#include "mockturtle/algorithms/linear_resynthesis.hpp" +#include "mockturtle/algorithms/lut_mapping.hpp" +#include "mockturtle/algorithms/mapper.hpp" +#include "mockturtle/algorithms/mig_algebraic_rewriting.hpp" +#include "mockturtle/algorithms/mig_resub.hpp" +#include "mockturtle/algorithms/miter.hpp" +#include "mockturtle/algorithms/network_fuzz_tester.hpp" +#include "mockturtle/algorithms/node_resynthesis.hpp" +#include "mockturtle/algorithms/node_resynthesis/akers.hpp" +#include "mockturtle/algorithms/node_resynthesis/bidecomposition.hpp" +#include "mockturtle/algorithms/node_resynthesis/cached.hpp" +#include "mockturtle/algorithms/node_resynthesis/composed.hpp" +#include "mockturtle/algorithms/node_resynthesis/davio.hpp" +#include "mockturtle/algorithms/node_resynthesis/direct.hpp" +#include "mockturtle/algorithms/node_resynthesis/dsd.hpp" +#include "mockturtle/algorithms/node_resynthesis/exact.hpp" +#include "mockturtle/algorithms/node_resynthesis/mig_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/null.hpp" +#include "mockturtle/algorithms/node_resynthesis/shannon.hpp" +#include "mockturtle/algorithms/node_resynthesis/traits.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_minmc.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp" +#include "mockturtle/algorithms/node_resynthesis/xag_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/xmg3_npn.hpp" +#include "mockturtle/algorithms/node_resynthesis/xmg_npn.hpp" +#include "mockturtle/algorithms/pattern_generation.hpp" +#include "mockturtle/algorithms/reconv_cut.hpp" +#include "mockturtle/algorithms/refactoring.hpp" +#include "mockturtle/algorithms/resubstitution.hpp" +#include "mockturtle/algorithms/resyn_engines/aig_enumerative.hpp" +#include "mockturtle/algorithms/resyn_engines/mig_enumerative.hpp" +#include "mockturtle/algorithms/resyn_engines/mig_resyn.hpp" +#include "mockturtle/algorithms/resyn_engines/xag_resyn.hpp" +#include "mockturtle/algorithms/satlut_mapping.hpp" +#include "mockturtle/algorithms/sim_resub.hpp" +#include "mockturtle/algorithms/simulation.hpp" +#include "mockturtle/algorithms/testcase_minimizer.hpp" +#include "mockturtle/algorithms/window_rewriting.hpp" +#include "mockturtle/algorithms/xag_optimization.hpp" +#include "mockturtle/algorithms/xag_resub_withDC.hpp" +#include "mockturtle/algorithms/xmg_algebraic_rewriting.hpp" +#include "mockturtle/algorithms/xmg_optimization.hpp" +#include "mockturtle/algorithms/xmg_resub.hpp" +#include "mockturtle/generators/arithmetic.hpp" +#include "mockturtle/generators/control.hpp" +#include "mockturtle/generators/legacy.hpp" +#include "mockturtle/generators/majority.hpp" +#include "mockturtle/generators/majority_n.hpp" +#include "mockturtle/generators/modular_arithmetic.hpp" +#include "mockturtle/generators/random_network.hpp" +#include "mockturtle/generators/self_dualize.hpp" +#include "mockturtle/generators/sorting.hpp" +#include "mockturtle/io/aiger_reader.hpp" +#include "mockturtle/io/bench_reader.hpp" +#include "mockturtle/io/blif_reader.hpp" +#include "mockturtle/io/bristol_reader.hpp" +#include "mockturtle/io/dimacs_reader.hpp" +#include "mockturtle/io/genlib_reader.hpp" +#include "mockturtle/io/pla_reader.hpp" +#include "mockturtle/io/serialize.hpp" +#include "mockturtle/io/super_reader.hpp" +#include "mockturtle/io/verilog_reader.hpp" +#include "mockturtle/io/write_aiger.hpp" +#include "mockturtle/io/write_bench.hpp" +#include "mockturtle/io/write_blif.hpp" +#include "mockturtle/io/write_dimacs.hpp" +#include "mockturtle/io/write_dot.hpp" +#include "mockturtle/io/write_patterns.hpp" +#include "mockturtle/io/write_verilog.hpp" +#include "mockturtle/networks/abstract_xag.hpp" +#include "mockturtle/networks/aig.hpp" +#include "mockturtle/networks/aqfp.hpp" +#include "mockturtle/networks/buffered.hpp" +#include "mockturtle/networks/cover.hpp" +#include "mockturtle/networks/detail/foreach.hpp" +#include "mockturtle/networks/events.hpp" +#include "mockturtle/networks/klut.hpp" +#include "mockturtle/networks/mig.hpp" +#include "mockturtle/networks/muxig.hpp" +#include "mockturtle/networks/sequential.hpp" +#include "mockturtle/networks/storage.hpp" +#include "mockturtle/networks/tig.hpp" +#include "mockturtle/networks/xag.hpp" +#include "mockturtle/networks/xmg.hpp" +#include "mockturtle/networks/crossed.hpp" +#include "mockturtle/properties/aqfpcost.hpp" +#include "mockturtle/properties/mccost.hpp" +#include "mockturtle/properties/migcost.hpp" +#include "mockturtle/properties/xmgcost.hpp" +#include "mockturtle/traits.hpp" +#include "mockturtle/utils/algorithm.hpp" +#include "mockturtle/utils/cost_functions.hpp" +#include "mockturtle/utils/cuts.hpp" +#include "mockturtle/utils/debugging_utils.hpp" +#include "mockturtle/utils/hash_functions.hpp" +#include "mockturtle/utils/include/percy.hpp" +#include "mockturtle/utils/index_list.hpp" +#include "mockturtle/utils/json_utils.hpp" +#include "mockturtle/utils/mixed_radix.hpp" +#include "mockturtle/utils/name_utils.hpp" +#include "mockturtle/utils/network_cache.hpp" +#include "mockturtle/utils/network_utils.hpp" +#include "mockturtle/utils/node_map.hpp" +#include "mockturtle/utils/progress_bar.hpp" +#include "mockturtle/utils/recursive_cost_functions.hpp" +#include "mockturtle/utils/stopwatch.hpp" +#include "mockturtle/utils/string_utils.hpp" +#include "mockturtle/utils/super_utils.hpp" +#include "mockturtle/utils/tech_library.hpp" +#include "mockturtle/utils/truth_table_cache.hpp" +#include "mockturtle/utils/truth_table_utils.hpp" +#include "mockturtle/utils/window_utils.hpp" +#include "mockturtle/views/binding_view.hpp" +#include "mockturtle/views/cnf_view.hpp" +#include "mockturtle/views/color_view.hpp" +#include "mockturtle/views/cost_view.hpp" +#include "mockturtle/views/cut_view.hpp" +#include "mockturtle/views/depth_view.hpp" +#include "mockturtle/views/fanout_limit_view.hpp" +#include "mockturtle/views/fanout_view.hpp" +#include "mockturtle/views/immutable_view.hpp" +#include "mockturtle/views/mapping_view.hpp" +#include "mockturtle/views/mffc_view.hpp" +#include "mockturtle/views/names_view.hpp" +#include "mockturtle/views/topo_view.hpp" +#include "mockturtle/views/window_view.hpp" +#include "mockturtle/views/rank_view.hpp" diff --git a/include/mockturtle/networks/abstract_xag.hpp b/include/mockturtle/networks/abstract_xag.hpp new file mode 100644 index 0000000..632f109 --- /dev/null +++ b/include/mockturtle/networks/abstract_xag.hpp @@ -0,0 +1,892 @@ +/* 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 abstract_xag.hpp + \brief Abstract XAG logic network implementation + + This network type is (for now) only meant for experimental use cases. + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" + +namespace mockturtle +{ + +struct abstract_xag_storage +{ + struct node_type + { + uint32_t* fanin{}; + uint32_t fanin_size{}; + uint32_t fanout_size{}; + uint32_t value{}; + uint32_t visited{}; + uint32_t level{}; + }; + + abstract_xag_storage() + { + /* constant 0 node */ + nodes.emplace_back(); + + // nodes.reserve( 10000u ); + } + + ~abstract_xag_storage() + { + for ( auto& n : nodes ) + { + if ( n.fanin ) + { + delete[] n.fanin; + } + } + } + + struct abstract_xag_node_eq + { + bool operator()( abstract_xag_storage::node_type const& a, abstract_xag_storage::node_type const& b ) const + { + return a.fanin_size == b.fanin_size && std::equal( a.fanin, a.fanin + a.fanin_size, b.fanin ); + } + }; + + struct abstract_xag_node_hash + { + uint64_t operator()( abstract_xag_storage::node_type const& n ) const + { + return std::accumulate( n.fanin, n.fanin + n.fanin_size, UINT64_C( 0 ), [&]( auto accu, auto c ) { return accu + std::hash{}( c ); } ); + } + }; + + std::vector nodes; + std::vector children; + std::vector inputs; + std::vector> outputs; + phmap::flat_hash_map hash; + + uint32_t trav_id = 0u; + uint32_t depth = 0u; +}; + +class abstract_xag_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1u; + static constexpr auto max_fanin_size = std::numeric_limits::max(); + + using base_type = abstract_xag_network; + using storage_type = abstract_xag_storage; + using storage = std::shared_ptr; + using node = uint32_t; + + struct signal + { + signal() = default; + signal( uint32_t index, bool complement = false ) : index( index ), complement( complement ) {} + + uint32_t index; + bool complement; + + signal operator!() const + { + return { index, !complement }; + } + + signal operator+() const + { + return { index, false }; + } + + signal operator-() const + { + return { index, true }; + } + + signal operator^( bool complement ) const + { + return { index, this->complement != complement }; + } + + bool operator==( signal const& other ) const + { + return index == other.index && complement == other.complement; + } + + bool operator!=( signal const& other ) const + { + return index != other.index || complement != other.complement; + } + + bool operator<( signal const& other ) const + { + return index < other.index || ( index == other.index && !complement && other.complement ); + } + }; + + abstract_xag_network() + : _storage( std::make_shared() ) + { + } + + abstract_xag_network( std::shared_ptr storage ) + : _storage( storage ) + { + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, value }; + } + + signal create_pi() + { + const auto index = static_cast( _storage->nodes.size() ); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].fanout_size++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + _storage->depth = std::max( _storage->depth, level( f.index ) ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_pi( node const& n ) const + { + return n > 0 && _storage->nodes[n].fanin_size == 0u; + } + + bool is_ci( node const& n ) const + { + return n > 0 && _storage->nodes[n].fanin_size == 0u; + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal _create_node( std::vector const& fanin, uint32_t level_offset ) + { + storage::element_type::node_type node; + node.fanin = new uint32_t[fanin.size()]; + node.fanin_size = fanin.size(); + std::copy( fanin.begin(), fanin.end(), node.fanin ); + + /* structural hashing */ + if ( const auto it = _storage->hash.find( node ); it != _storage->hash.end() ) + { + delete[] node.fanin; + return { it->second, 0 }; + } + + const auto index = static_cast( _storage->nodes.size() ); + _storage->nodes.push_back( node ); + _storage->hash.emplace( node, index ); + + /* increase ref-count to children */ + uint32_t _level = 0u; + for ( auto const& f : fanin ) + { + _storage->nodes[f].fanout_size++; + _level = std::max( _level, level( f ) ); + } + _storage->nodes[index].level = _level + level_offset; + + return { index, 0u }; + } + + signal create_and( signal a, signal b ) + { + /* order inputs a > b it is a AND */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + /* trivial cases */ + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( b.index == 0 ) + { + return b.complement == false ? get_constant( false ) : a; + } + /* constant propagation */ + else if ( a.complement && b.complement ) + { + return !create_nary_xor( { +a, +b, create_and( +a, +b ) } ); + } + else if ( a.complement ) + { + return create_xor( create_and( +a, b ), b ); + } + else if ( b.complement ) + { + return create_xor( create_and( a, +b ), a ); + } + + /* subset resolution */ + const auto& anode = _storage->nodes[a.index]; + const auto& bnode = _storage->nodes[b.index]; + + const auto a_begin = is_nary_xor( a.index ) ? anode.fanin : &a.index; + const auto a_end = is_nary_xor( a.index ) ? anode.fanin + anode.fanin_size : &a.index + 1; + const auto b_begin = is_nary_xor( b.index ) ? bnode.fanin : &b.index; + const auto b_end = is_nary_xor( b.index ) ? bnode.fanin + bnode.fanin_size : &b.index + 1; + + if ( std::includes( a_begin, a_end, b_begin, b_end ) ) + { + std::vector set_anew; + std::set_difference( a_begin, a_end, b_begin, b_end, std::back_inserter( set_anew ) ); + return create_xor( b, create_and( b, _create_nary_xor( set_anew ) ) ); + } + else if ( std::includes( b_begin, b_end, a_begin, a_end ) ) + { + std::vector set_bnew; + std::set_difference( b_begin, b_end, a_begin, a_end, std::back_inserter( set_bnew ) ); + return create_xor( a, create_and( a, _create_nary_xor( set_bnew ) ) ); + } + + return _create_node( std::vector{ { a.index, b.index } }, 1u ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + return create_nary_xor( { a, b } ); + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_xor( create_and( !cond, create_xor( f_then, f_else ) ), f_then ) ^ f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + auto c1 = create_xor( a, b ); + auto c2 = create_xor( a, c ); + auto c3 = create_and( c1, c2 ); + return create_xor( a, c3 ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_nary_xor( { a, b, c } ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal _create_nary_xor( std::vector const& fs ) + { + std::vector _fs; + + const auto merge_one = [&]( uint32_t f ) { + const auto it = std::lower_bound( _fs.begin(), _fs.end(), f ); + if ( it != _fs.end() && *it == f ) + { + _fs.erase( it ); + } + else + { + _fs.insert( it, f ); + } + }; + + const auto merge_many = [&]( uint32_t* begin, uint32_t* end ) { + std::vector tmp; + std::set_symmetric_difference( _fs.begin(), _fs.end(), begin, end, std::back_inserter( tmp ) ); + _fs = std::move( tmp ); + }; + + for ( auto const& f : fs ) + { + auto const& node = _storage->nodes[f]; + if ( node.fanin_size == 0u ) + { + merge_one( f ); + } + else if ( node.fanin[0] > node.fanin[1] ) + { + merge_one( f ); + } + else + { + merge_many( node.fanin, node.fanin + node.fanin_size ); + } + } + + if ( _fs.empty() ) + { + return get_constant( false ); + } + else if ( _fs.size() == 1u ) + { + return { _fs.front(), false }; + } + else + { + return _create_node( _fs, 0u ); + } + } + + signal create_nary_xor( std::vector const& fs ) + { + std::vector _fs; + + const auto merge_one = [&]( uint32_t f ) { + const auto it = std::lower_bound( _fs.begin(), _fs.end(), f ); + if ( it != _fs.end() && *it == f ) + { + _fs.erase( it ); + } + else + { + _fs.insert( it, f ); + } + }; + + const auto merge_many = [&]( uint32_t* begin, uint32_t* end ) { + std::vector tmp; + std::set_symmetric_difference( _fs.begin(), _fs.end(), begin, end, std::back_inserter( tmp ) ); + _fs = std::move( tmp ); + }; + + bool complement{ false }; + for ( auto const& f : fs ) + { + complement ^= f.complement; + auto const& node = _storage->nodes[f.index]; + if ( f.index == 0 ) + { + // do nothing + } + else if ( node.fanin_size == 0u ) + { + merge_one( f.index ); + } + else if ( node.fanin[0] > node.fanin[1] ) + { + merge_one( f.index ); + } + else + { + merge_many( node.fanin, node.fanin + node.fanin_size ); + } + } + + if ( _fs.empty() ) + { + return get_constant( complement ); + } + else if ( _fs.size() == 1u ) + { + return { _fs.front(), complement }; + } + else + { + return _create_node( _fs, 0u ) ^ complement; + } + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( abstract_xag_network const& other, node const& source, std::vector const& children ) + { + if ( other.is_and( source ) ) + { + return create_and( children[0u], children[1u] ); + } + else + { + return create_nary_xor( children ); + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return { n, false }; + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return n; + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return _storage->inputs[index]; + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + const auto po = _storage->outputs[index]; + return { po.first, po.second }; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using Iterator = decltype( _storage->outputs.begin() ); + using ElementType = signal; + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto const& po ) -> signal { return { po.first, po.second }; }, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using Iterator = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto const& po ) -> signal { return { po.first, po.second }; }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_pi( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_pi( n ) ) + return; + + const auto& node = _storage->nodes[n]; + detail::foreach_element_transform( + node.fanin, node.fanin + node.fanin_size, []( auto c ) -> signal { return { c, false }; }, fn ); + } +#pragma endregion + +#pragma region Structural properties + uint32_t size() const + { + return num_gates() + num_pis() + 1u; + } + + uint32_t num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + uint32_t num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + return _storage->nodes[n].fanin_size; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].fanout_size; + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].fanout_size++; + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].fanout_size; + } + + uint32_t depth() const + { + return _storage->depth; + } + + uint32_t level( node const& n ) const + { + return _storage->nodes[n].level; + } + + bool is_and( node const& n ) const + { + const auto& node = _storage->nodes[n]; + return node.fanin_size == 2 && ( node.fanin[0] > node.fanin[1] ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + const auto& node = _storage->nodes[n]; + return node.fanin_size != 0u && ( node.fanin[0] < node.fanin[1] ); + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( n != 0 && !is_pi( n ) ); + + const auto& node = _storage->nodes[n]; + + if ( node.fanin[0] > node.fanin[1] ) + { + auto v1 = *begin++; + auto v2 = *begin++; + return v1 && v2; + } + else + { + auto v = *begin++; + while ( begin != end ) + { + v ^= *begin++; + } + return v; + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_pi( n ) ); + + const auto& node = _storage->nodes[n]; + + if ( node.fanin[0] > node.fanin[1] ) + { + auto v1 = *begin++; + auto v2 = *begin++; + return v1 & v2; + } + else + { + auto v = *begin++; + while ( begin != end ) + { + v ^= *begin++; + } + return v; + } + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.value = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].value; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].value = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].value++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].value; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.visited = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].visited; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].visited = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +public: + storage _storage; +}; + +} // namespace mockturtle + +template<> +struct fmt::formatter +{ + constexpr auto parse( format_parse_context& ctx ) + { + auto it = ctx.begin(), end = ctx.end(); + if ( it != end && *it != '}' ) + { + throw format_error( "invalid format" ); + } + return it; + } + + template + auto format( const mockturtle::abstract_xag_network::signal& f, FormatContext& ctx ) + { + return format_to( ctx.out(), "{}{}", f.complement ? "~" : "", f.index ); + } +}; + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::abstract_xag_network::signal const& s ) const noexcept + { + uint64_t k = s.index; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + k ^= s.complement; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/networks/aig.hpp b/include/mockturtle/networks/aig.hpp new file mode 100644 index 0000000..c8c4643 --- /dev/null +++ b/include/mockturtle/networks/aig.hpp @@ -0,0 +1,1265 @@ +/* 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 aig.hpp + \brief AIG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Hash function for AIGs (from ABC) */ +template +struct aig_hash +{ + uint64_t operator()( Node const& n ) const + { + uint64_t seed = -2011; + seed += n.children[0].index * 7937; + seed += n.children[1].index * 2971; + seed += n.children[0].weight * 911; + seed += n.children[1].weight * 353; + return seed; + } +}; + +/*! \brief AIG storage container + + AIGs have nodes with fan-in 2. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `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, + aig_hash>>; + +class aig_network +{ +public: +#pragma region Types and constructors + static constexpr bool is_aig_network_type = true; + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = aig_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( aig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator aig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( aig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + aig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + aig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + aig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + 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 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = _storage->outputs.size(); + _storage->outputs.emplace_back( f.index, f.complement ); + return static_cast( po_index ); + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement ? b : get_constant( false ); + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return { it->second, 0 }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Createy ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + return create_or( create_and( a, b ), create_and( c, !create_and( !a, !b ) ) ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( create_xor( a, b ), c ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 2u ); + return create_and( children[0u], children[1u] ); + } +#pragma endregion + +#pragma region Has node + std::optional has_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, 0 ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return std::nullopt; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, diff_pol ? get_constant( false ) : child1 ); + } + else if ( child0.index == 0 ) /* constant child */ + { + return std::make_pair( n, child0.complement ? child1 : get_constant( false ) ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return; + } + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into the hash table + node.children[0] = child0; + node.children[1] = child1; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + /* delete the node (ignoring its current fanout_size) */ + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + /* recursively reset old node */ + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_nodes( std::list> substitutions ) + { + auto clean_substitutions = [&]( node const& n ) { + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ) { + if ( s.first == n ) + { + node const nn = get_node( s.second ); + if ( is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( fanout_size( nn ) > 0 ) + { + decr_fanout_size( nn ); + } + /* remove the node if it's fanout_size becomes 0 */ + if ( fanout_size( nn ) == 0 ) + { + take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; + + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + auto clean_sub_event = _events->register_delete_event( clean_substitutions ); + + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + incr_fanout_size( get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + for ( auto index = 1u; index < _storage->nodes.size(); ++index ) + { + /* skip CIs and dead nodes */ + if ( is_ci( index ) || is_dead( index ) ) + continue; + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ) { return s.first == index; } ) != std::end( substitutions ) ) + continue; + + /* replace in node */ + if ( const auto repl = replace_in_node( index, old_node, new_signal ); repl ) + { + incr_fanout_size( get_node( repl->second ) ); + substitutions.emplace_back( *repl ); + } + } + + /* replace in outputs */ + replace_in_outputs( old_node, new_signal ); + + /* replace in substitutions */ + for ( auto& s : substitutions ) + { + if ( get_node( s.second ) == old_node ) + { + s.second = is_complemented( s.second ) ? !new_signal : new_signal; + incr_fanout_size( get_node( new_signal ) ); + } + } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !is_dead( old_node ) ); + take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + decr_fanout_size( get_node( new_signal ) ); + } + + _events->release_delete_event( clean_sub_event ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 2; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + (void)n; + kitty::dynamic_truth_table _and( 2 ); + _and._bits[0] = 0x8; + return _and; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::aig_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/networks/aqfp.hpp b/include/mockturtle/networks/aqfp.hpp new file mode 100644 index 0000000..e739860 --- /dev/null +++ b/include/mockturtle/networks/aqfp.hpp @@ -0,0 +1,1121 @@ +/* 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 aqfp.hpp + \brief AQFP network implementation + + \author Alessandro Tempia Calvino + \author Dewmini Sudara Marakkalage + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +struct aqfp_storage_data +{ + std::unordered_map node_fn_cache; +}; + +/*! \brief AQFP storage container + + We use one bit of the index pointer to store a complemented attribute. + Every node has 64-bit of additional data used for the following purposes: + + `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 +*/ +using aqfp_storage = storage, aqfp_storage_data>; + +class aqfp_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 5u; + + using base_type = aqfp_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( aqfp_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator aqfp_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( aqfp_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + aqfp_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _storage->nodes[0].children.resize( 3u ); + _storage->nodes[0].children[0].data = _storage->nodes[0].children[0].data = _storage->nodes[0].children[0].data = static_cast( 0u ); + } + + aqfp_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children.resize( 3u ); + node.children[0].data = node.children[1].data = node.children[2].data = ~static_cast( 0 ); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + 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; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].children[0].data == ~static_cast( 0 ) && _storage->nodes[n].children[1].data == ~static_cast( 0 ) && _storage->nodes[n].children[2].data == ~static_cast( 0 ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + static_cast( c.complement ) >= 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + + node.children.resize( 3u ); + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + const auto index = _storage->nodes.size(); + + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_maj( std::vector children ) + { + assert( children.size() > 0u ); + assert( children.size() % 2 == 1u ); + + if ( children.size() == 1u ) + { + return children[0u]; + } + + std::stable_sort( children.begin(), children.end(), []( auto f, auto s ) { return f.index < s.index; } ); + + for ( auto i = 1u; i < children.size(); i++ ) + { + if ( children[i - 1].index == children[i].index && children[i - 1].complement != children[i].complement ) + { + children.erase( children.begin() + ( i - 1 ), children.begin() + ( i + 1 ) ); + return create_maj( children ); + } + } + + for ( auto i = 0u; i < children.size(); i++ ) + { + const auto index = children[i].index; + const auto complement = children[i].complement; + auto j = i + 1; + while ( j < children.size() && children[j].index == index && children[j].complement == complement ) + { + j++; + } + if ( j - i > children.size() / 2 ) + { + return { index, complement }; + } + } + + auto node_complement = false; + + auto num_complemented = 0u; + for ( const auto& c : children ) + { + num_complemented += static_cast( c.complement ); + } + + if ( num_complemented > children.size() / 2 ) + { + node_complement = true; + for ( auto& c : children ) + { + c.complement = !c.complement; + } + } + + storage::element_type::node_type node; + + for ( const auto& c : children ) + { + node.children.push_back( c ); + } + + const auto index = _storage->nodes.size(); + + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( const auto& c : children ) + { + _storage->nodes[c.index].data[0].h1++; + } + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aqfp_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() > 1 && children.size() % 2 == 1 ); + return create_maj( children ); + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + std::vector old_children; + + bool replacement = false; + for ( size_t i = 0u; i < node.children.size(); ++i ) + { + old_children.push_back( signal{ node.children[i] } ); + + if ( node.children[i].index == old_node ) + { + node.children[i] = node.children[i].weight ? !new_signal : new_signal; + replacement = true; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + } + } + + if ( !replacement ) + { + return std::nullopt; + } + + /* TODO: Do the simplifications if possible and ordering */ + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < nobj.children.size(); ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + take_out_node( _old ); + } + } + +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - 1u - _storage->inputs.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return _storage->nodes[n].children.size(); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + const auto num_fanin = _storage->nodes[n].children.size(); + + if ( num_fanin == 3u ) + { + kitty::dynamic_truth_table _maj( 3u ); + _maj._bits[0] = 0xe8; + return _maj; + } + else if ( num_fanin == 5u ) + { + kitty::dynamic_truth_table _maj( 5u ); + _maj._bits[0] = 0xfee8e880; + return _maj; + } + else + { + if ( _storage->data.node_fn_cache.count( num_fanin ) ) + { + return _storage->data.node_fn_cache[num_fanin]; + } + + std::vector> dp; + for ( auto i = 0u; i <= num_fanin; i++ ) + { + dp.push_back( { ~kitty::dynamic_truth_table( num_fanin ) } ); + if ( i == 0u ) + continue; + auto ith_var = kitty::nth_var( num_fanin, i - 1 ); + for ( auto j = 1u; j <= i && j <= ( num_fanin / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( _storage->data.node_fn_cache[num_fanin] = dp[num_fanin][( num_fanin / 2 ) + 1] ); + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] } ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] }, i ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] } ); + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] }, i ); + } + } + } +#pragma endregion + + template + iterates_over_t + compute_majority_n_with_bool( Iterator begin, Iterator end ) const + { + std::vector> dp; + std::vector v( begin, end ); + + const auto n = v.size(); + typename Iterator::value_type one = true; + + for ( auto i = 0u; i <= n; i++ ) + { + dp.push_back( { one } ); + if ( i == 0u ) + continue; + auto ith_var = v[i - 1]; + for ( auto j = 1u; j <= i && j <= ( n / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) || dp[i - 1][j] : ( ith_var && dp[i - 1][j - 1] ) ); + } + } + + return ( dp[n][( n / 2 ) + 1] ); + } + + template + auto compute_majority_n( Iterator begin, Iterator end ) const + { + std::vector> dp; + std::vector v( begin, end ); + + const auto n = v.size(); + typename Iterator::value_type one = ~( v[0] ^ v[0] ); + + for ( auto i = 0u; i <= n; i++ ) + { + dp.push_back( { one } ); + if ( i == 0u ) + continue; + auto ith_var = v[i - 1]; + for ( auto j = 1u; j <= i && j <= ( n / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( dp[n][( n / 2 ) + 1] ); + } + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( ( *it ) ^ _storage->nodes[n].children[i].weight ); + } + return compute_majority_n_with_bool( v.begin(), v.end() ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( *it ) : ( *it ) ); + } + + return compute_majority_n( v.begin(), v.end() ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + assert( begin->num_bits() > 0 && "truth tables must not be empty" ); + for ( auto it = begin; it != end; it++ ) + { + assert( begin->num_bits() == it->num_bits() ); + } + assert( begin->num_bits() >= result.num_bits() ); + assert( result.num_blocks() == begin->num_blocks() || ( result.num_blocks() == begin->num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( begin->num_bits() ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( it->_bits.back() ) : ( it->_bits.back() ) ); + } + + result._bits.back() = compute_majority_n( v.begin(), v.end() ); + + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::aqfp_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/networks/block.hpp b/include/mockturtle/networks/block.hpp new file mode 100644 index 0000000..0097e5a --- /dev/null +++ b/include/mockturtle/networks/block.hpp @@ -0,0 +1,1078 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file block.hpp + \brief Block logic network implementation with multi-output support + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct block_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief Block node + * + * `data[0].h1` : Application-specific value + * `data[1].h1` : Visited flags + * `data[1].h2` : Total fan-out size (we use MSB to indicate whether a node is dead) + * `data[2+i].h1`: Function literal in truth table cache for the fanout + * `data[2+i].h2`: Fan-out size + * + */ +struct block_storage_node : block_fanin_node<2> +{ + block_storage_node() + { + data = decltype( data )( 3 ); + } + + bool operator==( block_storage_node const& other ) const + { + if ( data.size() != other.data.size() ) + return false; + + for ( auto i = 2; i < data.size() + 2; ++i ) + if ( ( data[i].h1 != other.data[i].h1 ) || ( children != other.children ) ) + return false; + + return true; + } +}; + +/*! \brief Block storage container + + ... +*/ +using block_storage = storage_no_hash; + +class block_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + static constexpr auto min_gate_output_size = 1; + static constexpr auto max_gate_output_size = 2; + static constexpr auto output_signal_bits = 1; + + using base_type = block_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), output( 0 ), index( index ) + { + } + + signal( uint32_t index ) + : complement( 0 ), output( 0 ), index( index ) + { + } + + signal( uint64_t index, uint64_t complement, uint64_t output ) + : complement( complement ), output( output ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( block_storage::node_type::pointer_type const& p ) + : complement( p.weight & 1 ), output( p.weight >> 1 ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t output : output_signal_bits; + uint64_t index : 63 - output_signal_bits; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, output, 0 }; + } + + signal operator-() const + { + return { index, output, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator block_storage::node_type::pointer_type() const + { + return { index, ( output << 1 ) | complement }; + } + + operator uint64_t() const + { + return data; + } + +#if __cplusplus > 201703L + bool operator==( block_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + block_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + block_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + block_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[2].h1 = 0; + _storage->nodes[1].data[2].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[2].h1 = 2; + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[1].h2++; + _storage->nodes[f.index].data[2 + f.output].h2++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, ( f.output << 1 ) | f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_multioutput( node const& n ) const + { + return _storage->nodes[n].data.size() > 3; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool is_pi( node const& n ) const + { + return n > 1 && _storage->nodes[n].children.size() == 0u; + } + + bool constant_value( node const& n ) const + { + return n != 0; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } + + signal create_ha( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 4, 12 } ); + } + + signal create_hai( signal a, signal b ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b }, { 5, 13 } ); + } + + signal create_fa( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 14, 18 } ); + } + + signal create_fai( signal a, signal b, signal c ) + { + /* PO0: carry, PO1: sum */ + return _create_node( { a, b, c }, { 15, 19 } ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[2].h1 = literal; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; /* TODO: increase fanout count for output */ + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal _create_node( std::vector const& children, std::vector const& literals ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + + node.data = decltype( node.data )( 2 + literals.size() ); + + for ( auto i = 0; i < literals.size(); ++i ) + node.data[2 + i].h1 = literals[i]; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[1].h2++; + _storage->nodes[c.index].data[2 + c.output].h2++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal create_node( std::vector const& children, std::vector const& functions ) + { + assert( functions.size() > 0 ); + + if ( children.size() == 0u ) + { + assert( functions[0].num_vars() == 0u ); + return get_constant( !kitty::is_const0( functions[0] ) ); + } + std::vector literals; + for ( auto const& tt : functions ) + literals.push_back( _storage->data.cache.insert( tt ) ); + + return _create_node( children, literals ); + } + + signal clone_node( block_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + if ( other.is_multioutput( source ) ) + { + std::vector tts; + for ( auto i = 2; i < other._storage->nodes[source].data.size(); ++i ) + tts.push_back( other._storage->data.cache[other._storage->nodes[source].data[i].h1] ); + return create_node( children, tts ); + } + else + { + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[2].h1]; + return create_node( children, tt ); + } + } +#pragma endregion + +#pragma region Restructuring + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + bool in_fanin = false; + auto& nobj = _storage->nodes[n]; + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + in_fanin = true; + break; + } + } + + if ( !in_fanin ) + return; + + // remember before + std::vector old_children( nobj.children.size() ); + std::transform( nobj.children.begin(), nobj.children.end(), old_children.begin(), []( auto c ) { return signal{ c }; } ); + + /* replace in node */ + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + child = signal{ new_signal.data ^ ( child.data & 1 ) }; + // increment fan-out of new node + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + replace_in_node( n, old_node, new_signal ); + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output = signal{ new_signal.data ^ ( output.data & 1 ) }; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n < 2 || is_ci( n ) ) + return; + + /* delete the node */ + auto& nobj = _storage->nodes[n]; + nobj.data[1].h2 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove fanout count over output pins */ + for ( uint32_t i = 2; i < _storage->nodes[n].data.size(); ++i ) + { + nobj.data[i].h2 = 0; + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0; i < nobj.children.size(); ++i ) + { + auto& child = nobj.children[i]; + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + + decr_fanout_size_pin( nobj.children[i].index, signal{ child }.output ); + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto idx = 2u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + substitute_node( old_node, new_signal ); + } + + inline bool is_dead( node const& n ) const + { + /* A dead node is simply a dangling node */ + return ( _storage->nodes[n].data[1].h2 >> 31 ) & 1; + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t num_outputs( node const& n ) const + { + return static_cast( _storage->nodes[n].data.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[1].h2++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2++; + } + + uint32_t decr_fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return --_storage->nodes[n].data[2 + pin_index].h2; + } + + uint32_t fanout_size_pin( node const& n, uint32_t pin_index ) const + { + return _storage->nodes[n].data[2 + pin_index].h2; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } + + bool is_and( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 4; + } + + bool is_and( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 4; + } + + bool is_or( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 6; + } + + bool is_or( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 6; + } + + bool is_xor( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 12; + } + + bool is_xor( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 12; + } + + bool is_maj( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 14; + } + + bool is_maj( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 14; + } + + bool is_ite( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 16; + } + + bool is_ite( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 16; + } + + bool is_xor3( node const& n ) const + { + return n > 1 && _storage->nodes[n].data.size() == 3 && _storage->nodes[n].data[2].h1 == 18; + } + + bool is_xor3( signal const& f ) const + { + return f.index > 1 && _storage->nodes[f.index].data[2 + f.output].h1 == 18; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[2].h1]; + } + + kitty::dynamic_truth_table node_function_pin( const node& n, uint32_t pin_index ) const + { + return _storage->data.cache[_storage->nodes[n].data[2 + pin_index].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return { n, 0 }; + } + + signal make_signal( node const& n, uint32_t output_pin ) const + { + return { n, 0, output_pin }; + } + + bool is_complemented( signal const& f ) const + { + return f.complement ? true : false; + } + + uint32_t get_output_pin( signal const& f ) const + { + return static_cast( f.output ); + } + + signal next_output_pin( signal const& f ) const + { + return { f.index, f.complement, f.output + 1 }; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto f ) { return signal( f ); }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return signal( f ); }, fn ); + } +#pragma endregion + +#pragma region Simulate values // (Works on single-output gates only) + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + auto it = _storage->nodes[n].children.begin(); + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? ( ~( it->weight ) & 1 ) : ( ( it->weight ) & 1 ); + ++it; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[2].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* adjust polarities */ + for ( auto j = 0u; j < nfanin; ++j ) + { + if ( _storage->nodes[n].children[j].weight & 1 ) + tts[j] = ~tts[j]; + } + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[2].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h1 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h1 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h1++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h1 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/networks/buffered.hpp b/include/mockturtle/networks/buffered.hpp new file mode 100644 index 0000000..eef214f --- /dev/null +++ b/include/mockturtle/networks/buffered.hpp @@ -0,0 +1,1030 @@ +/* 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 buffered.hpp + \brief Buffered networks implementation + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "aig.hpp" +#include "aqfp.hpp" +#include "crossed.hpp" +#include "mig.hpp" + +#include + +namespace mockturtle +{ + +class buffered_aig_network : public aig_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0] = a; + node.children[1] = !a; + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + } + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + _storage->nodes[n].children[0].weight ^= 1; + _storage->nodes[n].children[1].weight ^= 1; + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_and( source ) ); + assert( children.size() == 2u ); + return create_and( children[0u], children[1u] ); + } +#pragma endregion + +#pragma region Restructuring + // disable restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) = delete; + void replace_in_outputs( node const& old_node, signal const& new_signal ) = delete; + void take_out_node( node const& n ) = delete; + void substitute_node( node const& old_node, signal const& new_signal ) = delete; + void substitute_nodes( std::list> substitutions ) = delete; +#pragma endregion + +#pragma region Structural properties + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + else if ( is_buf( n ) ) + return 1; + else + return 2; + } + + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children[0].index == _storage->nodes[n].children[1].index && _storage->nodes[n].children[0].weight != _storage->nodes[n].children[1].weight; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children[0].weight; + } + + bool is_and( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + kitty::dynamic_truth_table _and( 2 ); + _and._bits[0] = 0x8; + return _and; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if ( is_buf( n ) ) + { + if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + } + else + { + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + result.mask_bits(); + } +#pragma endregion +}; /* buffered_aig_network */ + +class buffered_mig_network : public mig_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + node.children[0] = a; + node.children[1] = !a; + // node.children[2] = a; // not used + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + } + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + _storage->nodes[n].children[0].weight ^= 1; + _storage->nodes[n].children[1].weight ^= 1; + _storage->nodes[n].children[2].weight ^= 1; + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_maj( source ) ); + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Restructuring + // disable restructuring + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + assert( is_buf( old_node ) ); + auto& node = _storage->nodes[n]; + + if ( is_buf( n ) ) + { + assert( node.children[0].index == old_node ); + new_signal.complement ^= node.children[0].weight; + node.children[0] = new_signal; + node.children[1] = !new_signal; + _storage->nodes[new_signal.index].data[0].h1++; + return; + } + + uint32_t fanin = 3u; + for ( auto i = 0u; i < 3u; ++i ) + { + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + assert( fanin < 3 ); + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + _storage->hash.erase( node ); + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + } + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + assert( !is_dead( old_node ) ); + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + void take_out_node( node const& n ) + { + assert( is_buf( n ) ); + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + if ( decr_fanout_size( nobj.children[0].index ) == 0 ) + { + take_out_node( nobj.children[0].index ); + } + } + void substitute_node( node const& old_node, signal const& new_signal ) = delete; + void substitute_nodes( std::list> substitutions ) = delete; +#pragma endregion + +#pragma region Structural properties + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + else if ( is_buf( n ) ) + return 1; + else + return 3; + } + + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children[0].index == _storage->nodes[n].children[1].index && _storage->nodes[n].children[0].weight != _storage->nodes[n].children[1].weight; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children[0].weight; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + kitty::dynamic_truth_table _maj( 3 ); + _maj._bits[0] = 0xe8; + return _maj; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if ( is_buf( n ) ) + { + if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + } + } + else + { + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + result.mask_bits(); + } +#pragma endregion +}; /* buffered_mig_network */ + +class buffered_aqfp_network : public aqfp_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Primary I / O and constants + bool is_ci( node const& n ) const + { + if ( is_buf( n ) ) + return false; + + 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; + } + + bool is_pi( node const& n ) const + { + if ( is_buf( n ) ) + return false; + + return _storage->nodes[n].children[0].data == ~static_cast( 0 ) && _storage->nodes[n].children[1].data == ~static_cast( 0 ) && _storage->nodes[n].children[2].data == ~static_cast( 0 ); + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + if ( is_constant( get_node( a ) ) ) + return a; + + const auto index = _storage->nodes.size(); + auto& node = _storage->nodes.emplace_back(); + + node.children.resize( 1u ); + node.children[0] = a; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + void invert( node const& n ) + { + assert( !is_constant( n ) && !is_pi( n ) ); + assert( fanout_size( n ) == 0 ); + for ( auto& s : _storage->nodes[n].children ) + { + s.weight ^= 1; + } + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( aqfp_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( other.is_maj( source ) ); + assert( children.size() > 1 && children.size() % 2 == 1 ); + return create_maj( children ); + } +#pragma endregion + +#pragma region Structural properties + /* redefinition of num_gates counting the gates */ + auto num_gates() const + { + uint32_t gate_count = 0; + foreach_gate( [&gate_count]( auto const& n ) { + ++gate_count; + } ); + return gate_count; + } + + bool is_buf( node const& n ) const + { + return _storage->nodes[n].children.size() == 1; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].children.size() == 1 && _storage->nodes[n].children[0].weight; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ) && !is_buf( n ); + } + +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + if ( is_buf( n ) ) + { + kitty::dynamic_truth_table _buf( 1 ); + _buf._bits[0] = 0x2; + return _buf; + } + + const auto num_fanin = _storage->nodes[n].children.size(); + + if ( num_fanin == 3u ) + { + kitty::dynamic_truth_table _maj( 3u ); + _maj._bits[0] = 0xe8; + return _maj; + } + else if ( num_fanin == 5u ) + { + kitty::dynamic_truth_table _maj( 5u ); + _maj._bits[0] = 0xfee8e880; + return _maj; + } + else + { + if ( _storage->data.node_fn_cache.count( num_fanin ) ) + { + return _storage->data.node_fn_cache[num_fanin]; + } + + std::vector> dp; + for ( auto i = 0u; i <= num_fanin; i++ ) + { + dp.push_back( { ~kitty::dynamic_truth_table( num_fanin ) } ); + if ( i == 0u ) + continue; + auto ith_var = kitty::nth_var( num_fanin, i - 1 ); + for ( auto j = 1u; j <= i && j <= ( num_fanin / 2 ) + 1; j++ ) + { + dp[i].push_back( ( j < i ) ? ( ith_var & dp[i - 1][j - 1] ) | dp[i - 1][j] : ( ith_var & dp[i - 1][j - 1] ) ); + } + } + + return ( _storage->data.node_fn_cache[num_fanin] = dp[num_fanin][( num_fanin / 2 ) + 1] ); + } + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ) && !is_buf( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] } ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + if ( !fn( signal{ _storage->nodes[n].children[i] }, i ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] } ); + } + } + else if constexpr ( detail::is_callable_with_index_v ) + { + for ( auto i = 0u; i < _storage->nodes[n].children.size(); i++ ) + { + fn( signal{ _storage->nodes[n].children[i] }, i ); + } + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? !( *begin ) : *begin; + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( ( *it ) ^ _storage->nodes[n].children[i].weight ); + } + return compute_majority_n_with_bool( v.begin(), v.end() ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + return is_complemented( _storage->nodes[n].children[0] ) ? ~( *begin ) : *begin; + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( *it ) : ( *it ) ); + } + + return compute_majority_n( v.begin(), v.end() ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + if ( is_buf( n ) ) + { + result.resize( begin->num_bits() ); + result._bits.back() = is_complemented( _storage->nodes[n].children[0] ) ? ~( begin->_bits.back() ) : begin->_bits.back(); + result.mask_bits(); + return; + } + + assert( begin->num_bits() > 0 && "truth tables must not be empty" ); + for ( auto it = begin; it != end; it++ ) + { + assert( begin->num_bits() == it->num_bits() ); + } + assert( begin->num_bits() >= result.num_bits() ); + assert( result.num_blocks() == begin->num_blocks() || ( result.num_blocks() == begin->num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( begin->num_bits() ); + + std::vector v; + auto i = 0u; + for ( auto it = begin; it != end; it++, i++ ) + { + v.push_back( _storage->nodes[n].children[i].weight ? ~( it->_bits.back() ) : ( it->_bits.back() ) ); + } + + result._bits.back() = compute_majority_n( v.begin(), v.end() ); + + result.mask_bits(); + } +#pragma endregion +}; /* buffered_aqfp_network */ + +class buffered_crossed_klut_network : public crossed_klut_network +{ +public: + static constexpr bool is_buffered_network_type = true; + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + void invert( node const& n ) + { + if ( _storage->nodes[n].data[1].h1 == 2 ) + _storage->nodes[n].data[1].h1 = 3; + else if ( _storage->nodes[n].data[1].h1 == 3 ) + _storage->nodes[n].data[1].h1 = 2; + else + assert( false ); + } +#pragma endregion + +#pragma region Crossings + /*! \brief Merges two buffer nodes into a crossing cell + * + * After this operation, the network will not be in a topological order. Additionally, buf1 and buf2 will be dangling. + * + * \param buf1 First buffer node. + * \param buf2 Second buffer node. + * \return The created crossing cell. + */ + node merge_into_crossing( node const& buf1, node const& buf2 ) + { + assert( is_buf( buf1 ) && is_buf( buf2 ) ); + + auto const& in_buf1 = _storage->nodes[buf1].children[0]; + auto const& in_buf2 = _storage->nodes[buf2].children[0]; + + node out_buf1{}, out_buf2{}; + uint32_t fanin_index1 = std::numeric_limits::max(), fanin_index2 = std::numeric_limits::max(); + foreach_node( [&]( node const& n ) { + foreach_fanin( n, [&]( auto const& f, auto i ) { + if ( auto const fin = get_node( f ); fin == buf1 ) + { + out_buf1 = n; + fanin_index1 = i; + } + else if ( fin == buf2 ) + { + out_buf2 = n; + fanin_index2 = i; + } + } ); + } ); + assert( out_buf1 != 0 && out_buf2 != 0 ); + assert( fanin_index1 != std::numeric_limits::max() && fanin_index2 != std::numeric_limits::max() ); + + auto const [fout1, fout2] = create_crossing( in_buf1, in_buf2 ); + + _storage->nodes[out_buf1].children[fanin_index1] = fout1; + _storage->nodes[out_buf2].children[fanin_index2] = fout2; + + /* decrease ref-count to children (was increased in `create_crossing`) */ + _storage->nodes[in_buf1.index].data[0].h1--; + _storage->nodes[in_buf2.index].data[0].h1--; + + _storage->nodes[buf1].children.clear(); + _storage->nodes[buf2].children.clear(); + + return get_node( fout1 ); + } + +#pragma endregion + +#pragma region Structural properties + // including buffers, splitters, and inverters + bool is_buf( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == 2 || _storage->nodes[n].data[1].h1 == 3; + } + + bool is_not( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == 3; + } +#pragma endregion + +#pragma region Node and signal iterators + /* Note: crossings are included; buffers, splitters, inverters are not */ + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_buf( n ); }, + fn ); + } +#pragma endregion +}; /* buffered_crossed_klut_network */ + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/networks/cover.hpp b/include/mockturtle/networks/cover.hpp new file mode 100644 index 0000000..376f4e2 --- /dev/null +++ b/include/mockturtle/networks/cover.hpp @@ -0,0 +1,722 @@ +/* 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 cover.hpp + \brief single output cover logic network implementation + + \author Andrea Costamagna + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" + +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include +#include +#include + +#include + +namespace mockturtle { +/*! \brief cover storage data + * + * This struct contains the constituents of the network and its main features. + * + * The constituents of the network are the covers representing the boolean + * functions stored in each node. These are stored in a vector of pairs. Each + * element is the cover of a function and a boolean value indicating whether the + * cover indicates the ON-set or the OFF set. More precisely: + * `covers` : Vector of pairs for covers storage + * `covers[i].first` : Cubes i-th cover + * `covers[i].second` : Boolean true (false) if ON set (OFF set) + * This data structure directly originates from the k-LUT one and, for this + * reason, it inherits from it the vast majority of the features. The main + * difference is the way the nodes are stored and future improvements could + * include the substitution of the current covers storage with a cache, to avoid + * the redundant storage of some recurrent boolean functions. + */ +struct cover_storage_data { + uint64_t insert(std::pair, bool> const &cover) { + const auto index = covers.size(); + covers.emplace_back(cover); + return index; + } + + std::vector, bool>> covers; +}; + +/*! \brief cover node + * + * The cover node is a mixed fanin node with the following attributes: + * `children` : vector of pointers to children + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Index of the cover of the node in the covers container + * `data[1].h2`: Visited flags + */ +struct cover_storage_node : mixed_fanin_node<2> { + bool operator==(cover_storage_node const &other) const { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief cover storage container + * + * The network as a storage entity is defined by combining the node structure + * with the cover_storage structure. The attributes of this storage unit are + * listed in the following: `nodes` : Vector of cover storage nodes + * `inputs` : Vector of indices to inputs nodes + * `outputs` : Vector of pointers to node types + * `hash` : maps a node to its index in the nodes vector + * `data` : cover storage data + */ +using cover_storage = storage; + +/*! \brief cover_network + * + * This class implements a data structure for a cover based network. + * In this representation, each node is represented by specifying its ON set or + its OFF set, that in both cases are stored as a vector of cubes. + * The information related to the set to which the node refers to is contained + in a boolean variable, that is true (false) if the + * ON set (OFF set) is considered. + * + * This data structure is primarily meant to be used for reading .blif files in + which the number of variables would make it unfeasible the reading via a k-LUT + network. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + cover_network cover; + + const auto a = cover.create_pi(); + const auto b = cover.create_pi(); + const auto c = cover.create_pi(); + + kitty::cube _11 = kitty::cube("11"); + + std::vector nand_from_offset { _11 }; + const auto n1 = cover.create_cover_node( {a, b}, std::make_pair( + nand_from_offset, false ) ); + + const auto y1 = cover.create_and( n1, c ); + cover.create_po( y1 ); + + \endverbatim + */ + +class cover_network { +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = cover_network; + using cover_type = std::pair, bool>; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + cover_network() + : _storage(std::make_shared()), + _events(std::make_shared()) { + _init(); + } + + cover_network(std::shared_ptr storage) + : _storage(storage), + _events(std::make_shared()) { + _init(); + } + +protected: + inline void _init() { + uint64_t index; + + std::vector cube_dc = {kitty::cube()}; + + /* first node reserved for constant 0 */ + index = _storage->data.insert(std::make_pair(cube_dc, false)); + cover_storage_node &node_0 = _storage->nodes[0]; + node_0.data[1].h1 = index; + _storage->hash[node_0] = 0; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + index = _storage->data.insert(std::make_pair(cube_dc, true)); + cover_storage_node &node_1 = _storage->nodes[1]; + node_1.data[1].h1 = index; + _storage->hash[node_1] = 1; + + /* reserve the third node for the identity (inputs)*/ + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant(bool value = false) const { return value ? 1 : 0; } + + signal create_pi() { + const auto index_node = _storage->nodes.size(); + std::vector cube_I{kitty::cube("1")}; + const auto index_covers = + _storage->data.insert(std::make_pair(cube_I, true)); + + _storage->nodes.emplace_back(); + cover_storage_node &node_in = _storage->nodes[index_node]; + node_in.data[1].h1 = index_node; + _storage->hash[node_in] = index_node; + _storage->inputs.emplace_back(index_node); + + return index_node; + } + + uint32_t create_po(signal const &f) { + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const po_index = static_cast(_storage->outputs.size()); + _storage->outputs.emplace_back(f); + return po_index; + } + + bool is_combinational() const { return true; } + + bool is_constant(node const &n) const { return n <= 1; } + + bool is_ci(node const &n) const { + return std::find(_storage->inputs.begin(), _storage->inputs.end(), n) != + _storage->inputs.end(); + } + + bool is_pi(node const &n) const { + return std::find(_storage->inputs.begin(), _storage->inputs.end(), n) != + _storage->inputs.end(); + } + + bool constant_value(node const &n) const { return n == 1; } +#pragma endregion + +#pragma region Create unary functions + signal create_buf(signal const &a) { return a; } + + signal create_not(signal const &a) { + std::vector _not{kitty::cube("0")}; + return _create_cover_node({a}, std::make_pair(_not, true)); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and(signal a, signal b) { + std::vector _and{kitty::cube("11")}; + return _create_cover_node({a, b}, std::make_pair(_and, true)); + } + + signal create_nand(signal a, signal b) { + std::vector _nand{kitty::cube("11")}; + return _create_cover_node({a, b}, std::make_pair(_nand, false)); + } + + signal create_or(signal a, signal b) { + std::vector _or{kitty::cube("00")}; + return _create_cover_node({a, b}, std::make_pair(_or, false)); + } + + signal create_nor(signal a, signal b) { + std::vector _nor{kitty::cube("00")}; + return _create_cover_node({a, b}, std::make_pair(_nor, true)); + } + + signal create_lt(signal a, signal b) { + std::vector _lt{kitty::cube("01")}; + return _create_cover_node({a, b}, std::make_pair(_lt, true)); + } + + signal create_le(signal a, signal b) { + std::vector _le{kitty::cube("10")}; + return _create_cover_node({a, b}, std::make_pair(_le, false)); + } + + signal create_gt(signal a, signal b) { + std::vector _gt{kitty::cube("10")}; + return _create_cover_node({a, b}, std::make_pair(_gt, true)); + } + + signal create_ge(signal a, signal b) { + std::vector _ge{kitty::cube("01")}; + return _create_cover_node({a, b}, std::make_pair(_ge, false)); + } + + signal create_xor(signal a, signal b) { + std::vector _xor{kitty::cube("01"), kitty::cube("10")}; + return _create_cover_node({a, b}, std::make_pair(_xor, true)); + } + + signal create_xnor(signal a, signal b) { + std::vector _xnor{kitty::cube("00"), kitty::cube("11")}; + return _create_cover_node({a, b}, std::make_pair(_xnor, true)); + } +#pragma endregion + +#pragma region Create ternary functions + + signal create_maj(signal a, signal b, signal c) { + std::vector _maj{kitty::cube("011"), kitty::cube("101"), + kitty::cube("110"), kitty::cube("111")}; + return _create_cover_node({a, b, c}, std::make_pair(_maj, true)); + } + + signal create_ite(signal a, signal b, signal c) { + std::vector _ite{kitty::cube("11-"), kitty::cube("0-1")}; + return _create_cover_node({a, b, c}, std::make_pair(_ite, true)); + } + + signal create_xor3(signal a, signal b, signal c) { + std::vector _xor3{kitty::cube("001"), kitty::cube("010"), + kitty::cube("100"), kitty::cube("111")}; + return _create_cover_node({a, b, c}, std::make_pair(_xor3, true)); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and(std::vector const &fs) { + return tree_reduce( + fs.begin(), fs.end(), get_constant(true), + [this](auto const &a, auto const &b) { return create_and(a, b); }); + } + + signal create_nary_or(std::vector const &fs) { + return tree_reduce( + fs.begin(), fs.end(), get_constant(false), + [this](auto const &a, auto const &b) { return create_or(a, b); }); + } + + signal create_nary_xor(std::vector const &fs) { + return tree_reduce( + fs.begin(), fs.end(), get_constant(false), + [this](auto const &a, auto const &b) { return create_xor(a, b); }); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_cover_node(std::vector const &children, + cover_type const &new_cover) { + + uint64_t literal = _storage->data.insert(new_cover); + storage::element_type::node_type node; + std::copy(children.begin(), children.end(), + std::back_inserter(node.children)); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find(node); + if (it != _storage->hash.end()) { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(node); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for (auto c : children) { + _storage->nodes[c].data[0].h1++; + } + + set_value(index, 0); + + for (auto const &fn : _events->on_add) { + (*fn)(index); + } + + return index; + } + + /*! \brief Creates node with arbitrary cover (SOP or POS). + * + * `cover_type` is `std::pair, bool>`, where the + * first element defines the cubes (clauses) of the onset (offset) and the + * second element selects between the onset mode (true) or offset mode + * (false). + * + * \param children Fanin signals + * \param cover Cover for node function + */ + signal create_cover_node(std::vector const &children, + cover_type cover) { + if (children.size() == 0u) { + return get_constant(cover.second); + } + + return _create_cover_node(children, cover); + } + + signal create_node(std::vector const &children, + kitty::dynamic_truth_table const &function) { + if (children.size() == 0u) { + return get_constant(!kitty::is_const0(function)); + } + + cover_type new_cover; + bool is_sop = (kitty::count_ones(function) <= kitty::count_zeros(function)); + new_cover.second = is_sop; + uint32_t mask = 1u; + for (uint32_t i{1}; i < children.size(); ++i) + mask |= mask << 1; + for (uint32_t i{0}; i < pow(2, children.size()); ++i) { + if (kitty::get_bit(function, i) == is_sop) { + auto cb = kitty::cube(i, mask); + new_cover.first.push_back(cb); + } + } + + return _create_cover_node(children, new_cover); + } + + signal clone_node(cover_network const &other, node const &source, + std::vector const &children) { + assert(!children.empty()); + cover_type cb = + other._storage->data.covers[other._storage->nodes[source].data[1].h1]; + return create_cover_node(children, cb); + } +#pragma endregion + +#pragma region Restructuring + void substitute_node(node const &old_node, signal const &new_signal) { + /* find all parents from old_node */ + for (auto i = 0u; i < _storage->nodes.size(); ++i) { + auto &n = _storage->nodes[i]; + for (auto &child : n.children) { + if (child == old_node) { + std::vector old_children(n.children.size()); + std::transform(n.children.begin(), n.children.end(), + old_children.begin(), [](auto c) { return c.index; }); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for (auto const &fn : _events->on_modified) { + (*fn)(i, old_children); + } + } + } + } + + /* check outputs */ + for (auto &output : _storage->outputs) { + if (output == old_node) { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + + // reset fan-out of old node + _storage->nodes[old_node].data[0].h1 = 0; + } + + inline bool is_dead(node const &n) const { return false; } +#pragma endregion + +#pragma region Structural properties + auto size() const { return static_cast(_storage->nodes.size()); } + + auto num_cis() const { + return static_cast(_storage->inputs.size()); + } + + auto num_cos() const { + return static_cast(_storage->outputs.size()); + } + + auto num_pis() const { + return static_cast(_storage->inputs.size()); + } + + auto num_pos() const { + return static_cast(_storage->outputs.size()); + } + + auto num_gates() const { + return static_cast(_storage->nodes.size() - + _storage->inputs.size() - 2); + } + + uint32_t fanin_size(node const &n) const { + return static_cast(_storage->nodes[n].children.size()); + } + + uint32_t fanout_size(node const &n) const { + return _storage->nodes[n].data[0].h1; + } + + bool is_function(node const &n) const { return n > 1 && !is_ci(n); } +#pragma endregion + +#pragma region Functional properties + cover_type node_cover(const node &n) const { + return _storage->data.covers[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node(signal const &f) const { return f; } + + signal make_signal(node const &n) const { return n; } + + bool is_complemented(signal const &f) const { + (void)f; + return false; + } + + uint32_t node_to_index(node const &n) const { + return static_cast(n); + } + + node index_to_node(uint32_t index) const { return index; } + + node ci_at(uint32_t index) const { + assert(index < _storage->inputs.size()); + return *(_storage->inputs.begin() + index); + } + + signal co_at(uint32_t index) const { + assert(index < _storage->outputs.size()); + return (_storage->outputs.begin() + index)->index; + } + + node pi_at(uint32_t index) const { + assert(index < _storage->inputs.size()); + return *(_storage->inputs.begin() + index); + } + + signal po_at(uint32_t index) const { + assert(index < _storage->outputs.size()); + return (_storage->outputs.begin() + index)->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template void foreach_node(Fn &&fn) const { + auto r = range(_storage->nodes.size()); + detail::foreach_element(r.begin(), r.end(), fn); + } + + template void foreach_ci(Fn &&fn) const { + detail::foreach_element(_storage->inputs.begin(), _storage->inputs.end(), + fn); + } + + template void foreach_co(Fn &&fn) const { + using IteratorType = decltype(_storage->outputs.begin()); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), + [](auto o) { return o.index; }, fn); + } + + template void foreach_pi(Fn &&fn) const { + detail::foreach_element(_storage->inputs.begin(), _storage->inputs.end(), + fn); + } + + template void foreach_po(Fn &&fn) const { + using IteratorType = decltype(_storage->outputs.begin()); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), + [](auto o) { return o.index; }, fn); + } + + template void foreach_gate(Fn &&fn) const { + auto r = range( + 2u, _storage->nodes.size()); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), [this](auto n) { return !is_ci(n); }, fn); + } + + template void foreach_fanin(node const &n, Fn &&fn) const { + if (n == 0 || is_ci(n)) + return; + + using IteratorType = decltype(_storage->outputs.begin()); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), + [](auto f) { return f.index; }, fn); + } + +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t compute(node const &n, Iterator begin, + Iterator end) const { + uint32_t index{0}; + uint32_t mask{0}; + while (begin != end) { + mask = (mask << 1) | 1u; + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + auto cb_input = kitty::cube(index, mask); + cover_type &cubes_cover = + _storage->data.covers[_storage->nodes[n].data[1].h1]; + for (auto cb : cubes_cover.first) { + if ((cb._bits & cb._mask) == (cb_input._bits & cb._mask)) + return (cubes_cover.second == 1); + } + + return (cubes_cover.second == 0); + } + + template + iterates_over_truth_table_t compute(node const &n, Iterator begin, + Iterator end) const { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector tts(begin, end); + + assert(nfanin != 0); + assert(tts.size() == nfanin); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + cover_type &cubes_cover = + _storage->data.covers[_storage->nodes[n].data[1].h1]; + bool is_found = false; + for (uint32_t i = 0u; i < static_cast(result.num_bits()); ++i) { + is_found = false; + uint32_t pattern = 0u; + uint32_t mask = 0u; + for (auto j = 0u; j < nfanin; ++j) { + pattern |= kitty::get_bit(tts[j], i) << j; + mask |= 1u << j; + } + auto cb_input = kitty::cube(pattern, mask); + for (auto cb : cubes_cover.first) { + if ((cb._bits & cb._mask) == (cb_input._bits & cb._mask)) { + is_found = true; + if (cubes_cover.second == 1) { + kitty::set_bit(result, i); + } + break; + } + } + if (!is_found && (cubes_cover.second == 0)) + kitty::set_bit(result, i); + } + + return result; + } + +#pragma endregion + +#pragma region Custom node values + void clear_values() const { + std::for_each(_storage->nodes.begin(), _storage->nodes.end(), + [](auto &n) { n.data[0].h2 = 0; }); + } + + uint32_t value(node const &n) const { return _storage->nodes[n].data[0].h2; } + + void set_value(node const &n, uint32_t v) const { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value(node const &n) const { + return static_cast(_storage->nodes[n].data[0].h2++); + } + + uint32_t decr_value(node const &n) const { + return static_cast(--_storage->nodes[n].data[0].h2); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const { + std::for_each(_storage->nodes.begin(), _storage->nodes.end(), + [](auto &n) { n.data[1].h2 = 0; }); + } + + auto visited(node const &n) const { return _storage->nodes[n].data[1].h2; } + + void set_visited(node const &n, uint32_t v) const { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const { return _storage->trav_id; } + + void incr_trav_id() const { ++_storage->trav_id; } +#pragma endregion + +#pragma region General methods + auto &events() const { return *_events; } +#pragma endregion + + std::vector signal_map; + + void set_name(uint32_t signal, std::string name) { + signal_map.emplace_back(name); + } + + void set_output_name(uint32_t signal, std::string name) { + signal_map.emplace_back(name); + } + + std::string module_name; + + void set_network_name(std::string name){ + module_name = name; + } + +public: + std::string get_module_name(){ + return module_name; + } + + std::string get_signal_name(uint32_t signal) const { + // - 2 because 0 and 1 is reserved for terminal nodes + if (signal - 2 >= signal_map.size()) { + std::cerr << "[ERROR] signal index is out of bounds\n"; + } + return signal_map[signal - 2]; + } + + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/networks/crossed.hpp b/include/mockturtle/networks/crossed.hpp new file mode 100644 index 0000000..0e50305 --- /dev/null +++ b/include/mockturtle/networks/crossed.hpp @@ -0,0 +1,867 @@ +/* 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 crossed.hpp + \brief Implements networks with crossing cells + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "klut.hpp" + +namespace mockturtle +{ + +/* Version 1: annotate information of crossing fanout ordereing with different signals + * Share the same `klut_storage_data` as regular k-LUT network, + * but define a different `crossed_klut_storage_node` with a weight field in the pointer. + */ + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct crossed_klut_storage_node : mixed_fanin_node<2, 1> +{ + bool operator==( crossed_klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; +using crossed_klut_storage = storage; + +class crossed_klut_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + static constexpr bool is_crossed_network_type = true; + + using base_type = crossed_klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = crossed_klut_storage_node::pointer_type; + + crossed_klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + crossed_klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } +#pragma endregion + +protected: + static constexpr uint32_t literal_crossing = 0xffffffff; + + inline void _init() + { + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } + +public: +#pragma region Primary I / O and constants + signal get_constant( bool value = false ) const + { + return value ? signal( 1, 0 ) : signal( 0, 0 ); + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return signal( index, 0 ); + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_nor( signal a, signal b ) + { + return _create_node( { a, b }, 7 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_ge( signal a, signal b ) + { + return _create_node( { a, b }, 9 ); + } + + signal create_gt( signal a, signal b ) + { + return _create_node( { a, b }, 10 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } + + signal create_xnor( signal a, signal b ) + { + return _create_node( { a, b }, 13 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c.index].data[0].h1++; + } + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return signal( index, 0 ); + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( crossed_klut_network const& other, node const& source, std::vector const& children ) + { + assert( !other.is_crossing( source ) ); + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } +#pragma endregion + +#pragma region Crossings + /*! \brief Create a crossing cell + * + * \return A pair `(out1, out2)` of two signals to be used as fanouts of the crossing, + * where `in1` connects to `out1` and `in2` connects to `out2`. + */ + std::pair create_crossing( signal const& in1, signal const& in2 ) + { + storage::element_type::node_type node; + node.children.emplace_back( in1 ); + node.children.emplace_back( in2 ); + node.data[1].h1 = literal_crossing; + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + _storage->nodes[in1.index].data[0].h1++; + _storage->nodes[in2.index].data[0].h1++; + + /* TODO: not sure if this is wanted/needed? */ + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return std::make_pair( signal( index, 0 ), signal( index, 1 ) ); + } + + /*! \brief Insert a crossing cell on two wires + * + * After this operation, the network will not be in a topological order. + * + * \param in1 A fanin signal of the node `out1` + * \param in2 A fanin signal of the node `out2` + * \return The created crossing cell. No more fanouts should be added to it. + */ + node insert_crossing( signal const& in1, signal const& in2, node const& out1, node const& out2 ) + { + uint32_t fanin_index1 = std::numeric_limits::max(); + foreach_fanin( out1, [&]( auto const& f, auto i ) { + if ( f == in1 ) + { + fanin_index1 = i; + return false; + } + return true; + } ); + assert( fanin_index1 != std::numeric_limits::max() ); + + uint32_t fanin_index2 = std::numeric_limits::max(); + foreach_fanin( out2, [&]( auto const& f, auto i ) { + if ( f == in2 ) + { + fanin_index2 = i; + return false; + } + return true; + } ); + assert( fanin_index2 != std::numeric_limits::max() ); + + auto [fout1, fout2] = create_crossing( in1, in2 ); + _storage->nodes[out1].children[fanin_index1] = fout1; + _storage->nodes[out2].children[fanin_index2] = fout2; + + /* decrease ref-count to children (was increased in `create_crossing`) */ + _storage->nodes[in1.index].data[0].h1--; + _storage->nodes[in2.index].data[0].h1--; + + return get_node( fout1 ); + } + + /*! \brief Whether a node is a crossing cell + * + * \param n The node to be checked + * \return Whether this node is a crossing cell + */ + bool is_crossing( node const& n ) const + { + return _storage->nodes[n].data[1].h1 == literal_crossing; + } + + /*! \brief Whether a crossing's fanout signal is the second one + * + * \param f A signal pointing to a crossing cell + * \return Whether this fanout connects to the second fanin (return 1) or the first fanin (return 0) + */ + bool is_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + return f.weight; + } + + /*! \brief Take a crossing's first fanout signal and make it the second + * + * \param f A signal pointing to a crossing cell, referring to the fanout connecting to the first fanin + * \return The signal referring to the fanout connecting to the second fanin + */ + signal make_second( signal const& f ) const + { + assert( is_crossing( f.index ) ); + assert( !is_second( f ) ); + return signal( f.index, 1 ); + } + + /*! \brief Get the real fanin signal ignoring all crossings in between + * + * \param f A signal pointing to a crossing + * \return The corresponding signal pointing to a non-crossing node + */ + signal ignore_crossings( signal const& f ) const + { + if ( !is_crossing( get_node( f ) ) ) + return f; + return ignore_crossings( _storage->nodes[f.index].children[f.weight] ); + } + + /*! \brief Iterate through the real fanins of a node, ignoring all crossings in between */ + template + void foreach_fanin_ignore_crossings( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->nodes[n].children.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), [this]( auto f ) { return ignore_crossings( f ); }, fn ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ) && !is_crossing( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + assert( !is_crossing( n ) ); + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } + + bool is_not( node const& n ) const + { + if ( !is_function( n ) ) + return false; + return _storage->nodes[n].children.size() == 1 && _storage->nodes[n].data[1].h1 == 3; + } + + /* AND-2 with any input negation, but not output negation */ + bool is_and( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 4 || node.data[1].h1 == 8 || node.data[1].h1 == 10 || node.data[1].h1 == 7; + } + + /* OR-2 with any input negation, but not output negation */ + bool is_or( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 5 || node.data[1].h1 == 9 || node.data[1].h1 == 11 || node.data[1].h1 == 6; + } + + /* XOR-2 or XNOR-2 */ + bool is_xor( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 2 ) + return false; + return node.data[1].h1 == 12 || node.data[1].h1 == 13; + } + + /* XOR-3 or XNOR-3 */ + bool is_xor3( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 18 || node.data[1].h1 == 19; + } + + /* MAJ-3 or MINORITY-3 without non-symmetric input negation */ + bool is_maj( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 14 || node.data[1].h1 == 15; + } + + /* ITE (MUX2-1) without input negation; with or without output negation + * i.e., (x ? y :z) or !(x ? y : z) = x ? !y : !z */ + bool is_ite( node const& n ) const + { + if ( !is_function( n ) ) + return false; + auto const& node = _storage->nodes[n]; + if ( node.children.size() != 3 ) + return false; + return node.data[1].h1 == 16 || node.data[1].h1 == 17; + } + + std::vector get_fanin_negations( node const& n ) const + { + if ( is_crossing( n ) ) + return {false, false}; + if ( !is_function( n ) ) + return {}; + switch ( _storage->nodes[n].data[1].h1 ) + { + case 2: return {false}; // buf + case 3: return {true}; // not + case 4: return {false, false}; // and + case 5: return {true, true}; // x nand y = !x or !y + case 6: return {false, false}; // or + case 7: return {true, true}; // x nor y = !x and !y + case 8: return {true, false}; // !x and y + case 9: return {false, true}; // x or !y + case 10: return {false, true}; // x and !y + case 11: return {true, false}; // !x or y + case 12: return {false, false}; // xor + case 13: return {true, false}; // xnor (symmetric) + case 14: return {false, false, false}; // maj + case 15: return {true, true, true}; // minority + case 16: return {false, false, false}; // ite + case 17: return {false, true, true}; // !(x ? y : z) = x ? !y : !z + case 18: return {false, false, false}; // xor3 + case 19: return {true, false, false}; // xnor3 (symmetric) + } + return {}; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + /* Note: crossings are included */ + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + detail::foreach_element( _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + assert( !is_crossing( n ) ); + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/networks/detail/foreach.hpp b/include/mockturtle/networks/detail/foreach.hpp new file mode 100644 index 0000000..7aadd5b --- /dev/null +++ b/include/mockturtle/networks/detail/foreach.hpp @@ -0,0 +1,224 @@ +/* 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 foreach.hpp + \brief For each functor utilities + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle::detail +{ + +template +inline constexpr bool is_callable_with_index_v = std::is_invocable_r_v; + +template +inline constexpr bool is_callable_without_index_v = std::is_invocable_r_v; + +template +Iterator foreach_element( Iterator begin, Iterator end, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_if( Iterator begin, Iterator end, Pred&& pred, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + if ( !fn( *begin++, index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++ ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !pred( *begin ) ) + { + ++begin; + continue; + } + fn( *begin++, index++ ); + } + return begin; + } +} + +template +Iterator foreach_element_transform( Iterator begin, Iterator end, Transform&& transform, Fn&& fn, uint32_t counter_offset = 0 ) +{ + static_assert( is_callable_with_index_v || + is_callable_without_index_v || + is_callable_with_index_v || + is_callable_without_index_v ); + + if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ) ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + if ( !fn( transform( *begin++ ), index++ ) ) + { + return begin; + } + } + return begin; + } + else if constexpr ( is_callable_without_index_v ) + { + (void)counter_offset; + while ( begin != end ) + { + fn( transform( *begin++ ) ); + } + return begin; + } + else if constexpr ( is_callable_with_index_v ) + { + uint32_t index{ counter_offset }; + while ( begin != end ) + { + fn( transform( *begin++ ), index++ ); + } + return begin; + } +} + +} // namespace mockturtle::detail \ No newline at end of file diff --git a/include/mockturtle/networks/events.hpp b/include/mockturtle/networks/events.hpp new file mode 100644 index 0000000..6f508ec --- /dev/null +++ b/include/mockturtle/networks/events.hpp @@ -0,0 +1,134 @@ +/* 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 events.hpp + \brief Event API for updating a logic network. + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Network events. + * + * This data structure can be returned by a network. Clients can add functions + * to network events to call code whenever an event occurs. Events are adding + * a node, modifying a node, and deleting a node. + */ +template +class network_events +{ +public: + using add_event_type = std::function const& n )>; + using modified_event_type = std::function const& n, std::vector> const& previous_children )>; + using delete_event_type = std::function const& n )>; + +public: + std::shared_ptr register_add_event( add_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_add.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_modified_event( modified_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_modified.emplace_back( pfn ); + return pfn; + } + + std::shared_ptr register_delete_event( delete_event_type const& fn ) + { + auto pfn = std::make_shared( fn ); + on_delete.emplace_back( pfn ); + return pfn; + } + + void release_add_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_add.erase( std::remove_if( std::begin( on_add ), std::end( on_add ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_add ) ); + } + + void release_modified_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_modified.erase( std::remove_if( std::begin( on_modified ), std::end( on_modified ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_modified ) ); + } + + void release_delete_event( std::shared_ptr& fn ) + { + /* first decrement the reference counter of the event */ + auto fn_ptr = fn.get(); + fn = nullptr; + + /* erase the event if the only instance remains in the vector */ + on_delete.erase( std::remove_if( std::begin( on_delete ), std::end( on_delete ), + [&]( auto&& event ) { return event.get() == fn_ptr && event.use_count() <= 1u; } ), + std::end( on_delete ) ); + } + +public: + /*! \brief Event when node `n` is added. */ + std::vector> on_add; + + /*! \brief Event when `n` is modified. + * + * The event also informs about the previous children. Note that the new + * children are already available at the time the event is triggered. + */ + std::vector> on_modified; + + /*! \brief Event when `n` is deleted. */ + std::vector> on_delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/networks/generic.hpp b/include/mockturtle/networks/generic.hpp new file mode 100644 index 0000000..067db51 --- /dev/null +++ b/include/mockturtle/networks/generic.hpp @@ -0,0 +1,1065 @@ +/* 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 generic.hpp + \brief Generic sequential logic network implementation without strashing + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "sequential.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct generic_storage_data +{ + truth_table_cache cache; + uint32_t num_pis = 0u; + uint32_t num_pos = 0u; + std::vector registers; + uint32_t trav_id = 0u; +}; + +/*! \brief Generic node + * + * `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`: Function literal in truth table cache + * `data[1].h2`: Visited flags + * `data[2].h1`: Node type + * `data[2].h2`: Application-specific value + * + * Node types: + * - 0: unknown + * - 1: klut node + * - 2: PI terminal + * - 3: PO terminal + * - 4: Box input terminal + * - 5: Box output terminal + * - 6: Register + * - 7: Whitebox + * - 8: Blackbox + */ +struct generic_storage_node : mixed_fanin_node<3> +{ + bool operator==( generic_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief Generic storage container + + ... +*/ +using generic_storage = storage_no_hash; + +class generic_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = generic_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + generic_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ), + _register_information( std::make_shared>() ) + { + _init(); + } + + generic_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ), + _register_information( std::make_shared>() ) + { + _init(); + } + + generic_network( std::shared_ptr storage, std::shared_ptr> register_information ) + : _storage( storage ), + _events( std::make_shared() ), + _register_information( register_information ) + { + _init(); + } + + generic_network clone() const + { + return { std::make_shared( *_storage ), std::make_shared>( *_register_information ) }; + } + +protected: + inline void _init() + { + /* already initialized */ + if ( _storage->nodes.size() > 1 ) + return; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _buf = 0x2; + kitty::dynamic_truth_table tt_buf( 1 ); + kitty::create_from_words( tt_buf, &_buf, &_buf + 1 ); + _storage->data.cache.insert( tt_buf ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi( std::string const& name = std::string() ) + { + (void)name; + + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + _storage->nodes[index].data[2].h1 = 2; + ++_storage->data.num_pis; + return index; + } + + uint32_t create_po( signal const& f, std::string const& name = std::string() ) + { + (void)name; + + /* check f is not a PO already */ + if ( is_po( get_node( f ) ) ) + return get_node( f ); + + const auto index = _storage->nodes.size(); + const auto po = create_buf( f ); + _storage->nodes[index].data[2].h1 = 3; + /* increase ref-count of PO */ + _storage->nodes[po].data[0].h1++; + + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( po ); + ++_storage->data.num_pos; + return po_index; + } + + signal create_ro( std::string const& name = std::string() ) + { + (void)name; + + auto const index = static_cast( _storage->nodes.size() ); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + _storage->nodes[index].data[2].h1 = 5; + return index; + } + + uint32_t create_ri( signal const& f, int8_t reset = 0, std::string const& name = std::string() ) + { + (void)name; + + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const ri_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + _storage->data.registers.emplace_back( reset ); + return ri_index; + } + + bool is_combinational() const + { + return ( static_cast( _storage->inputs.size() ) == _storage->data.num_pis && + static_cast( _storage->outputs.size() ) == _storage->data.num_pos ); + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 2 || _storage->nodes[n].data[2].h1 == 5; + } + + bool is_co( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 3 || _storage->nodes[n].data[2].h1 == 4; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 2; + } + + bool is_po( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 3; + } + + bool is_ro( node const& n ) const + { + return std::find( _storage->inputs.begin() + _storage->data.num_pis, _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_node( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 1; + } + + bool is_register( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 6; + } + + bool is_box_input( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 4; + } + + bool is_box_output( node const& n ) const + { + return _storage->nodes[n].data[2].h1 == 5; + } + + bool constant_value( node const& n ) const + { + return n == 1; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return _create_node( { a }, 2 ); + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + node.data[2].h1 = 1; + + /* structural hashing is not used */ + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( generic_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } + + signal create_box_input( signal a ) + { + auto const ri = create_buf( a ); + _storage->nodes[get_node( ri )].data[2].h1 = 4; + return ri; + } + + signal create_box_output( signal a ) + { + auto const ro = create_buf( a ); + _storage->nodes[get_node( ro )].data[2].h1 = 5; + return ro; + } + + signal create_register( signal a, register_t const& l_info = {} ) + { + auto const r = create_buf( a ); + _storage->nodes[get_node( r )].data[2].h1 = 6; + _register_information->insert( { get_node( r ), l_info } ); + + return r; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& root = _storage->nodes[n]; + for ( auto& child : root.children ) + { + if ( child == old_node ) + { + std::vector old_children( root.children.size() ); + std::transform( root.children.begin(), root.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + } + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete PIs, constants, or already dead nodes */ + if ( n <= 1 || is_pi( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove register entry if register */ + if ( is_register( n ) ) + { + _register_information->erase( n ); + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto& child : nobj.children ) + { + if ( fanout_size( child.index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( child.index ) == 0 ) + { + take_out_node( child.index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* reset fan-in of old node */ + take_out_node( old_node ); + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_registers() const + { + return static_cast( _register_information->size() ); + } + + /*! \brief Standard version of num_registers replaced by the one above. */ + // auto num_registers() const + // { + // assert( static_cast( _storage->inputs.size() - _storage->data.num_pis ) == static_cast( _storage->outputs.size() - _storage->data.num_pos ) ); + // return static_cast( _storage->inputs.size() - _storage->data.num_pis ); + // } + + auto num_pis() const + { + return _storage->data.num_pis; + } + + auto num_pos() const + { + return _storage->data.num_pos; + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - _storage->outputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->data.num_pis ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->data.num_pos ); + return ( _storage->outputs.begin() + index )->index; + } + + node ro_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() - _storage->data.num_pis ); + return *( _storage->inputs.begin() + _storage->data.num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() - _storage->data.num_pos ); + return ( _storage->outputs.begin() + _storage->data.num_pos + index )->index; + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data - _storage->data.num_pis ); + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return ( _storage->outputs.begin() + _storage->data.num_pos + _storage->nodes[s].children[0].data - _storage->data.num_pis )->index; + } + + node ri_to_ro( signal const& s ) const + { + return *( _storage->inputs.begin() + _storage->data.num_pis + ri_index( s ) ); + } +#pragma endregion + +#pragma region Children acces + signal get_fanin0( node const& n ) const + { + assert( _storage->nodes[n].children.size() ); + return _storage->nodes[n].children[0].data; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.begin() + _storage->data.num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.begin() + _storage->data.num_pos, []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin() + _storage->data.num_pis, _storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin() + _storage->data.num_pos, _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return is_register( n ) && !is_dead( n ); }, + fn ); + } + + /*! \brief Standard version of foreach_register replaced by the one above. */ + // template + // void foreach_register( Fn&& fn ) const + // { + // static_assert( detail::is_callable_with_index_v, void> || + // detail::is_callable_without_index_v, void> || + // detail::is_callable_with_index_v, bool> || + // detail::is_callable_without_index_v, bool> ); + + // assert( _storage->inputs.size() - _storage->data.num_pis == _storage->outputs.size() - _storage->data.num_pos ); + // auto ro = _storage->inputs.begin() + _storage->data.num_pis; + // auto ri = _storage->outputs.begin() + _storage->data.num_pos; + // if constexpr ( detail::is_callable_without_index_v, bool> ) + // { + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // if ( !fn( std::make_pair( ( ri++ )->index, ro++ ) ) ) + // return; + // } + // } + // else if constexpr ( detail::is_callable_with_index_v, bool> ) + // { + // uint32_t index{ 0 }; + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // if ( !fn( std::make_pair( ( ri++ )->index, ro++ ), index++ ) ) + // return; + // } + // } + // else if constexpr ( detail::is_callable_without_index_v, void> ) + // { + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // fn( std::make_pair( ( ri++ )->index, *ro++ ) ); + // } + // } + // else if constexpr ( detail::is_callable_with_index_v, void> ) + // { + // uint32_t index{ 0 }; + // while ( ro != _storage->inputs.end() && ri != _storage->outputs.end() ) + // { + // fn( std::make_pair( ( ri++ )->index, *ro++ ), index++ ); + // } + // } + // } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_pi( n ) && !is_dead( n ); }, /* change to PI to cycle over boxes as well */ + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n <= 1 ) /* || is_ci( n ) */ + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + std::vector tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } + + void clear_values2() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[2].h2 = 0; } ); + } + + uint32_t value2( node const& n ) const + { + return _storage->nodes[n].data[2].h2; + } + + void set_value2( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[2].h2 = v; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->data.trav_id; + } + + void incr_trav_id() const + { + ++_storage->data.trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; + std::shared_ptr> _register_information; +}; + +} // namespace mockturtle diff --git a/include/mockturtle/networks/gia.hpp b/include/mockturtle/networks/gia.hpp new file mode 100644 index 0000000..64fed98 --- /dev/null +++ b/include/mockturtle/networks/gia.hpp @@ -0,0 +1,269 @@ +#pragma once +#ifdef ENABLE_ABC + +#include "detail/foreach.hpp" +#include + +namespace abc { + typedef struct Gia_Man_t_ Gia_Man_t; + typedef struct Gia_Obj_t_ Gia_Obj_t; + typedef struct Abc_Frame_t_ Abc_Frame_t; + + inline int Abc_LitNot( int Lit ) { assert(Lit >= 0); return Lit ^ 1; } + inline int Abc_Lit2Var( int Lit ) { assert(Lit >= 0); return Lit >> 1; } + inline int Abc_LitRegular( int Lit ) { assert(Lit >= 0); return Lit & ~01; } + + extern Gia_Obj_t * Gia_Regular( Gia_Obj_t * p ); + extern Gia_Obj_t * Gia_Not( Gia_Obj_t * p ); + extern Gia_Man_t * Gia_ManStart(int nObjsMax); + extern void Gia_ManStop(Gia_Man_t * p); + extern int Gia_ManAppendCi(Gia_Man_t * p); + extern int Gia_ManAppendCo(Gia_Man_t * p, int iLit0); + extern int Gia_ManAppendAnd2(Gia_Man_t * p, int iLit0, int iLit1); + extern Gia_Obj_t * Gia_ManObj(Gia_Man_t * p, int v); + extern int Gia_ObjIsPi(Gia_Man_t * p, Gia_Obj_t * pObj); + extern int Gia_ObjIsAnd(Gia_Obj_t * pObj); + extern int Gia_IsComplement(Gia_Obj_t * p); + extern int Gia_ManPiNum(Gia_Man_t * p); + extern int Gia_ManPoNum(Gia_Man_t * p); + extern int Gia_ManAndNum(Gia_Man_t * p); + extern int Gia_ManObjNum(Gia_Man_t * p); + extern Gia_Obj_t * Gia_ManPi(Gia_Man_t * p, int v); + extern int Gia_Obj2Lit(Gia_Man_t * p, Gia_Obj_t * pObj); + extern Gia_Obj_t * Gia_ManCi(Gia_Man_t * p, int v); + extern Gia_Obj_t * Gia_ManCo(Gia_Man_t * p, int v); + extern Gia_Obj_t * Gia_ObjFanin0(Gia_Obj_t * pObj); + extern Gia_Obj_t * Gia_ObjFanin1(Gia_Obj_t * pObj); + extern int Gia_ObjFaninC0(Gia_Obj_t * pObj); + extern int Gia_ObjFaninC1(Gia_Obj_t * pObj); + extern int Gia_ManLevelNum( Gia_Man_t * p ); + extern Gia_Obj_t * Gia_ManConst0( Gia_Man_t * p ); + extern Gia_Obj_t * Gia_ManConst1( Gia_Man_t * p ); + extern int Gia_ObjId( Gia_Man_t * p, Gia_Obj_t * pObj ); + extern Gia_Man_t * Gia_ManDup( Gia_Man_t * p ); + + extern Abc_Frame_t * Abc_FrameGetGlobalFrame(); + extern void Abc_FrameUpdateGia( Abc_Frame_t * p, Gia_Man_t * pNew ); + extern Gia_Man_t * Abc_FrameGetGia( Abc_Frame_t * p ); + extern int Cmd_CommandExecute( Abc_Frame_t * pAbc, const char * sCommand ); +} + +#include + +namespace mockturtle { + +class gia_network; + +class gia_signal { + friend class gia_network; + +public: + explicit gia_signal() = default; + explicit gia_signal(abc::Gia_Obj_t * obj) : obj_(obj) {} + + gia_signal operator!() const { return gia_signal(abc::Gia_Not(obj_)); } + gia_signal operator+() const { return gia_signal(abc::Gia_Regular(obj_)); } + gia_signal operator-() const { return gia_signal(abc::Gia_Not(abc::Gia_Regular(obj_))); } + + bool operator==(const gia_signal& other) const { return obj_ == other.obj_; } + bool operator!=(const gia_signal& other) const { return !operator==(other); } + bool operator<(const gia_signal& other) const { return obj_ < other.obj_; } + + abc::Gia_Obj_t * obj() const { return obj_; } + +private: + abc::Gia_Obj_t * obj_; +}; + +class gia_network { +public: + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = gia_network; + using node = int; + using signal = gia_signal; + using storage = abc::Gia_Man_t*; + + gia_network(int size) + : gia_(abc::Gia_ManStart(size)) /* doesn't automatically resize? */ + {} + + /* network does not implement constant value */ + bool constant_value(node n) const { (void)n; return false; } + + /* each node implements AND function */ + kitty::dynamic_truth_table node_function(node n) const { (void)n; kitty::dynamic_truth_table tt(2); tt._bits[0] = 0x8; return tt; } + + + signal get_constant(bool value) const { + return value ? signal(abc::Gia_ManConst1(gia_)) : signal(abc::Gia_ManConst0(gia_)); + } + + signal create_pi() { + return signal(abc::Gia_ManObj(gia_, abc::Abc_Lit2Var(abc::Gia_ManAppendCi(gia_)))); + } + + void create_po(const signal& f) { + /* po_node = */abc::Gia_ManAppendCo(gia_, abc::Gia_Obj2Lit(gia_, f.obj())); + } + + signal create_not(const signal& f) { + return !f; + } + + signal create_and(const signal& f, const signal& g) { + return signal(abc::Gia_ManObj(gia_, abc::Abc_Lit2Var(abc::Gia_ManAppendAnd2(gia_, abc::Gia_Obj2Lit(gia_, f.obj()), abc::Gia_Obj2Lit(gia_, g.obj()))))); + } + + bool is_constant(node n) const { + return n == 0; + } + + node get_node(const signal& f) const { + return Gia_ObjId(gia_, f.obj()); + } + + bool is_pi(node const& n) const { + return abc::Gia_ObjIsPi(gia_, abc::Gia_ManObj(gia_, n)); + } + + bool is_complemented(const signal& f) const { + return Gia_IsComplement(f.obj()); + } + + template + void foreach_pi(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; (i < abc::Gia_ManPiNum(gia_)) && ((pObj) = abc::Gia_ManCi(gia_, i)); ++i) { + fn(Gia_ObjId(gia_, pObj)); + } + } + + template + void foreach_po(Fn&& fn) const { + abc::Gia_Obj_t * pObj, * pFiObj; + for (int i = 0; (i < abc::Gia_ManPoNum(gia_)) && ((pObj) = abc::Gia_ManCo(gia_, i)); ++i) { + pFiObj = abc::Gia_ObjFanin0(pObj); + fn(abc::Gia_ObjFaninC0(pObj) ? !signal(pFiObj) : signal(pFiObj)); + } + } + + template + void foreach_gate(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; i < abc::Gia_ManObjNum(gia_) && ((pObj) = abc::Gia_ManObj(gia_, i)); ++i) { + if (abc::Gia_ObjIsAnd(pObj)) { + fn(Gia_ObjId(gia_, pObj)); + } + } + } + + template + void foreach_node(Fn&& fn) const { + abc::Gia_Obj_t * pObj; + for (int i = 0; i < abc::Gia_ManObjNum(gia_) && ((pObj) = abc::Gia_ManObj(gia_, i)); ++i) { + fn(signal(pObj)); + } + } + + template + void foreach_fanin(node n, Fn&& fn) const { + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + if (n == 0 || is_pi(n)) { return; } + + abc::Gia_Obj_t * pObj = abc::Gia_ManObj(gia_, n); + if constexpr ( detail::is_callable_without_index_v ) + { + if (!fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj)))) { + return; + } + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj))); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if (!fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj))), 0) { + return; + } + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj)), 1); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj))); + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj))); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn(signal(abc::Gia_ObjFaninC0(pObj) ? Gia_Not(abc::Gia_ObjFanin0(pObj)) : abc::Gia_ObjFanin0(pObj)), 0); + fn(signal(abc::Gia_ObjFaninC1(pObj) ? Gia_Not(abc::Gia_ObjFanin1(pObj)) : abc::Gia_ObjFanin1(pObj)), 1); + } + } + + int literal(const signal& f) const + { + return (abc::Gia_ObjId(gia_, f.obj()) << 1) + abc::Gia_IsComplement(f.obj()); + } + + int node_to_index(node n) const + { + return n; + } + + auto num_pis() const { return abc::Gia_ManPiNum(gia_); } + auto num_pos() const { return abc::Gia_ManPoNum(gia_); } + auto num_gates() const { return abc::Gia_ManAndNum(gia_); } + auto num_levels() const { return abc::Gia_ManLevelNum(gia_); } + auto size() const { return abc::Gia_ManObjNum(gia_); } + + bool load_rc() { + abc::Abc_Frame_t * abc = abc::Abc_FrameGetGlobalFrame(); + const int success = abc::Cmd_CommandExecute(abc, default_rc); + if (success != 0) { + printf("syntax error in script\n"); + } + return success == 0; + } + + bool run_opt_script(const std::string &script) { + abc::Gia_Man_t * gia = abc::Gia_ManDup(gia_); + abc::Abc_Frame_t * abc = abc::Abc_FrameGetGlobalFrame(); + abc::Abc_FrameUpdateGia(abc, gia); + + const int success = abc::Cmd_CommandExecute(abc, script.c_str()); + if (success != 0) { + printf("syntax error in script\n"); + } + + abc::Gia_Man_t * new_gia = abc::Abc_FrameGetGia(abc); + abc::Gia_ManStop(gia_); + gia_ = new_gia; + + return success == 0; + } + +private: + const char * default_rc = + "alias b balance;\n" + "alias rw rewrite;\n" + "alias rwz rewrite -z;\n" + "alias rf refactor;\n" + "alias rfz refactor -z;\n" + "alias rs resub;\n" + "alias rsz resub -z;\n" + "alias &r2rs '&put; b; rs -K 6; rw; rs -K 6 -N 2; rf; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rfz; rs -K 12 -N 2; rwz; b; &get';\n" + "alias &c2rs '&put; b -l; rs -K 6 -l; rw -l; rs -K 6 -N 2 -l; rf -l; rs -K 8 -l; b -l; rs -K 8 -N 2 -l; rw -l; rs -K 10 -l; rwz -l; rs -K 10 -N 2 -l; b -l; rs -K 12 -l; rfz -l; rs -K 12 -N 2 -l; rwz -l; b -l; &get';\n" + "alias compress2rs 'b -l; rs -K 6 -l; rw -l; rs -K 6 -N 2 -l; rf -l; rs -K 8 -l; b -l; rs -K 8 -N 2 -l; rw -l; rs -K 10 -l; rwz -l; rs -K 10 -N 2 -l; b -l; rs -K 12 -l; rfz -l; rs -K 12 -N 2 -l; rwz -l; b -l';\n" + "alias resyn2rs 'b; rs -K 6; rw; rs -K 6 -N 2; rf; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rfz; rs -K 12 -N 2; rwz; b;'\n" + "alias resyn2rs2 'b; rs -K 6; rw; rs -K 6 -N 2; rs -K 8; b; rs -K 8 -N 2; rw; rs -K 10; rwz; rs -K 10 -N 2; b; rs -K 12; rs -K 12 -N 2; rwz; b;'\n"; + +private: + abc::Gia_Man_t *gia_; +}; // gia_network + +} // mockturtle + +#endif diff --git a/include/mockturtle/networks/klut.hpp b/include/mockturtle/networks/klut.hpp new file mode 100644 index 0000000..96a7a6f --- /dev/null +++ b/include/mockturtle/networks/klut.hpp @@ -0,0 +1,707 @@ +/* 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 klut.hpp + \brief k-LUT logic network implementation + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "../utils/truth_table_cache.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +#include +#include + +#include +#include + +namespace mockturtle +{ + +struct klut_storage_data +{ + truth_table_cache cache; +}; + +/*! \brief k-LUT node + * + * `data[0].h1`: Fan-out size + * `data[0].h2`: Application-specific value + * `data[1].h1`: Function literal in truth table cache + * `data[1].h2`: Visited flags + */ +struct klut_storage_node : mixed_fanin_node<2> +{ + bool operator==( klut_storage_node const& other ) const + { + return data[1].h1 == other.data[1].h1 && children == other.children; + } +}; + +/*! \brief k-LUT storage container + + ... +*/ +using klut_storage = storage; + +class klut_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 1; + static constexpr auto max_fanin_size = 32; + + using base_type = klut_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = uint64_t; + + klut_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + _init(); + } + + klut_network clone() const + { + return { std::make_shared( *_storage ) }; + } + +protected: + inline void _init() + { + /* already initialized */ + if ( _storage->nodes.size() > 1 ) + return; + + /* reserve the second node for constant 1 */ + _storage->nodes.emplace_back(); + + /* reserve some truth tables for nodes */ + kitty::dynamic_truth_table tt_zero( 0 ); + _storage->data.cache.insert( tt_zero ); + + static uint64_t _not = 0x1; + kitty::dynamic_truth_table tt_not( 1 ); + kitty::create_from_words( tt_not, &_not, &_not + 1 ); + _storage->data.cache.insert( tt_not ); + + static uint64_t _and = 0x8; + kitty::dynamic_truth_table tt_and( 2 ); + kitty::create_from_words( tt_and, &_and, &_and + 1 ); + _storage->data.cache.insert( tt_and ); + + static uint64_t _or = 0xe; + kitty::dynamic_truth_table tt_or( 2 ); + kitty::create_from_words( tt_or, &_or, &_or + 1 ); + _storage->data.cache.insert( tt_or ); + + static uint64_t _lt = 0x4; + kitty::dynamic_truth_table tt_lt( 2 ); + kitty::create_from_words( tt_lt, &_lt, &_lt + 1 ); + _storage->data.cache.insert( tt_lt ); + + static uint64_t _le = 0xd; + kitty::dynamic_truth_table tt_le( 2 ); + kitty::create_from_words( tt_le, &_le, &_le + 1 ); + _storage->data.cache.insert( tt_le ); + + static uint64_t _xor = 0x6; + kitty::dynamic_truth_table tt_xor( 2 ); + kitty::create_from_words( tt_xor, &_xor, &_xor + 1 ); + _storage->data.cache.insert( tt_xor ); + + static uint64_t _maj = 0xe8; + kitty::dynamic_truth_table tt_maj( 3 ); + kitty::create_from_words( tt_maj, &_maj, &_maj + 1 ); + _storage->data.cache.insert( tt_maj ); + + static uint64_t _ite = 0xd8; + kitty::dynamic_truth_table tt_ite( 3 ); + kitty::create_from_words( tt_ite, &_ite, &_ite + 1 ); + _storage->data.cache.insert( tt_ite ); + + static uint64_t _xor3 = 0x96; + kitty::dynamic_truth_table tt_xor3( 3 ); + kitty::create_from_words( tt_xor3, &_xor3, &_xor3 + 1 ); + _storage->data.cache.insert( tt_xor3 ); + + /* truth tables for constants */ + _storage->nodes[0].data[1].h1 = 0; + _storage->nodes[1].data[1].h1 = 1; + } +#pragma endregion + +#pragma region Primary I / O and constants +public: + signal get_constant( bool value = false ) const + { + return value ? 1 : 0; + } + + signal create_pi() + { + const auto index = _storage->nodes.size(); + _storage->nodes.emplace_back(); + _storage->inputs.emplace_back( index ); + _storage->nodes[index].data[1].h1 = 2; + return index; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n <= 1; + } + + bool is_ci( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + return std::find( _storage->inputs.begin(), _storage->inputs.end(), n ) != _storage->inputs.end(); + } + + bool constant_value( node const& n ) const + { + return n == 1; + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return _create_node( { a }, 3 ); + } +#pragma endregion + +#pragma region Create binary functions + signal create_and( signal a, signal b ) + { + return _create_node( { a, b }, 4 ); + } + + signal create_nand( signal a, signal b ) + { + return _create_node( { a, b }, 5 ); + } + + signal create_or( signal a, signal b ) + { + return _create_node( { a, b }, 6 ); + } + + signal create_lt( signal a, signal b ) + { + return _create_node( { a, b }, 8 ); + } + + signal create_le( signal a, signal b ) + { + return _create_node( { a, b }, 11 ); + } + + signal create_xor( signal a, signal b ) + { + return _create_node( { a, b }, 12 ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_maj( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 14 ); + } + + signal create_ite( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 16 ); + } + + signal create_xor3( signal a, signal b, signal c ) + { + return _create_node( { a, b, c }, 18 ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal _create_node( std::vector const& children, uint32_t literal ) + { + storage::element_type::node_type node; + std::copy( children.begin(), children.end(), std::back_inserter( node.children ) ); + node.data[1].h1 = literal; + + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return it->second; + } + + const auto index = _storage->nodes.size(); + _storage->nodes.push_back( node ); + _storage->hash[node] = index; + + /* increase ref-count to children */ + for ( auto c : children ) + { + _storage->nodes[c].data[0].h1++; + } + + set_value( index, 0 ); + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return index; + } + + signal create_node( std::vector const& children, kitty::dynamic_truth_table const& function ) + { + if ( children.size() == 0u ) + { + assert( function.num_vars() == 0u ); + return get_constant( !kitty::is_const0( function ) ); + } + return _create_node( children, _storage->data.cache.insert( function ) ); + } + + signal clone_node( klut_network const& other, node const& source, std::vector const& children ) + { + assert( !children.empty() ); + const auto tt = other._storage->data.cache[other._storage->nodes[source].data[1].h1]; + return create_node( children, tt ); + } +#pragma endregion + +#pragma region Restructuring + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto i = 0u; i < _storage->nodes.size(); ++i ) + { + auto& n = _storage->nodes[i]; + for ( auto& child : n.children ) + { + if ( child == old_node ) + { + std::vector old_children( n.children.size() ); + std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); + child = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( i, old_children ); + } + } + } + } + + /* check outputs */ + for ( auto& output : _storage->outputs ) + { + if ( output == old_node ) + { + output = new_signal; + + // increment fan-out of new node + _storage->nodes[new_signal].data[0].h1++; + } + } + + // reset fan-out of old node + _storage->nodes[old_node].data[0].h1 = 0; + } + + inline bool is_dead( node const& n ) const + { + return false; + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->nodes.size() - _storage->inputs.size() - 2 ); + } + + uint32_t fanin_size( node const& n ) const + { + return static_cast( _storage->nodes[n].children.size() ); + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1; + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++; + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1; + } + + bool is_function( node const& n ) const + { + return n > 1 && !is_ci( n ); + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + return _storage->data.cache[_storage->nodes[n].data[1].h1]; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f; + } + + signal make_signal( node const& n ) const + { + return n; + } + + bool is_complemented( signal const& f ) const + { + (void)f; + return false; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return ( _storage->outputs.begin() + index )->index; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element( r.begin(), r.end(), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->outputs.begin(), _storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + using IteratorType = decltype( _storage->outputs.begin() ); + detail::foreach_element_transform( + _storage->nodes[n].children.begin(), _storage->nodes[n].children.end(), []( auto f ) { return f.index; }, fn ); + } +#pragma endregion + +#pragma region Simulate values + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + uint32_t index{ 0 }; + while ( begin != end ) + { + index <<= 1; + index ^= *begin++ ? 1 : 0; + } + return kitty::get_bit( _storage->data.cache[_storage->nodes[n].data[1].h1], index ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + const auto nfanin = _storage->nodes[n].children.size(); + + std::vector::value_type> tts( begin, end ); + + assert( nfanin != 0 ); + assert( tts.size() == nfanin ); + + /* resulting truth table has the same size as any of the children */ + auto result = tts.front().construct(); + const auto gate_tt = _storage->data.cache[_storage->nodes[n].data[1].h1]; + + for ( uint32_t i = 0u; i < static_cast( result.num_bits() ); ++i ) + { + uint32_t pattern = 0u; + for ( auto j = 0u; j < nfanin; ++j ) + { + pattern |= kitty::get_bit( tts[j], i ) << j; + } + if ( kitty::get_bit( gate_tt, pattern ) ) + { + kitty::set_bit( result, i ); + } + } + + return result; + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + uint32_t value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + uint32_t incr_value( node const& n ) const + { + return static_cast( _storage->nodes[n].data[0].h2++ ); + } + + uint32_t decr_value( node const& n ) const + { + return static_cast( --_storage->nodes[n].data[0].h2 ); + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h2 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h2; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h2 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/networks/mig.hpp b/include/mockturtle/networks/mig.hpp new file mode 100644 index 0000000..c8646ac --- /dev/null +++ b/include/mockturtle/networks/mig.hpp @@ -0,0 +1,1249 @@ +/* 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.hpp + \brief MIG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Eleonora Testa + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief MIG storage container + + MIGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `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>; + +class mig_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = mig_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( mig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator mig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( mig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + mig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + mig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + mig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + 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 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, node_complement }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Has node + std::optional has_maj( 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, node_complement ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return std::nullopt; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, diff_pol ? child2 : child0 ); + } + else if ( child1.index == child2.index ) + { + const auto diff_pol = child1.complement != child2.complement; + return std::make_pair( n, diff_pol ? child0 : child1 ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + _hash_obj.children[2] = child2; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // 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 ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // 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 ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // 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--; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + bool is_maj( node const& n ) const + { + return n > 0 && !is_ci( n ); + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + (void)n; + kitty::dynamic_truth_table _maj( 3 ); + _maj._bits[0] = 0xe8; + return _maj; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::mig_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/networks/muxig.hpp b/include/mockturtle/networks/muxig.hpp new file mode 100644 index 0000000..b7f769a --- /dev/null +++ b/include/mockturtle/networks/muxig.hpp @@ -0,0 +1,256 @@ +/* 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 muxig.hpp + \brief Mux-inverter graph logic network implementation + + \author Dewmini Marakkalage + */ + +#pragma once + +#include "tig.hpp" +#include + +namespace mockturtle +{ + +template<> +struct compute_function +{ + template + inline std::enable_if_t::value, T> operator()( T a, T b, T c ) + { + return ternary_operation( a, b, c, []( auto a, auto b, auto c ) { return ( a & b ) | ( ~a & c ); } ); + } + + template + inline std::enable_if_t::value, T> operator()( T a, T b, T c ) + { + return ( a & b ) | ( ~a & c ); + } +}; + +using muxig_signal = tig_network::signal; +using muxig_network = tig_network; + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins_for_and( muxig_signal a, muxig_signal b ) +{ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + if ( a.index == 0 ) + { + return { false, { a.complement ? b : get_constant( false ) } }; + } + + if ( a.index == b.index ) + { + return { false, { ( a.complement == b.complement ) ? a : get_constant( false ) } }; + } + + return { false, { !a, get_constant( false ), b } }; +} + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins_for_xor( muxig_signal a, muxig_signal b ) +{ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + if ( a.index == 0 ) + { + return { false, { a.complement ? !b : b } }; + } + + if ( a.index == b.index ) + { + return { false, { get_constant( a.complement != b.complement ) } }; + } + + if ( b.complement ) + { + // !(!a, !b, b) or (a, !b, b) + if ( a.complement ) + { + return { true, { !a, !b, b } }; + } + return { false, { a, !b, b } }; + } + + // (!a, b, !b) or !(a, b, !b) + if ( a.complement ) + { + return { false, { !a, b, !b } }; + } + return { true, { a, b, !b } }; +} + +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanins( muxig_signal a, muxig_signal b, muxig_signal c ) +{ + /* mux(a b c) = a b + a' c */ + /* always make sure that b is never inverted */ + /* always make sure that b.index <= c.index */ + /* if possible, make sure a is not inverted */ + /* if possible, make sure that a.index < b.index */ + /* else, if possible, make sure that a.index < c.index */ + + if ( a.index == 0 ) + { // (1 b c) = b, (0 b c) = c, + return { false, { a.complement ? b : c } }; + } + + if ( a.complement ) + { + + return { false, { !a, c, b } }; + } + + if ( b.index == 0 ) + { + if ( b.complement ) + { + // (a 1 c) = a + a'c = a + c = (a'c')' + return complement( normalized_fanins_for_and( !a, !c ) ); + } + // (a 0 c) = a'c + return normalized_fanins_for_and( !a, c ); + } + + if ( a.index == b.index ) + { + if ( a.complement == b.complement ) + { + // (a a c) = aa + a'c = a + c = (a'c')' + return complement( normalized_fanins_for_and( !a, !c ) ); + } + // (a a' c) = aa' + a'c = a'c + return normalized_fanins_for_and( !a, c ); + } + + if ( a.index == c.index ) + { + if ( a.complement == c.complement ) + { + // (a b a) = ab + a'a = ab + return normalized_fanins_for_and( a, b ); + } + // (a b a') == ab + a'a' = ab + a' = a' + b = (ab')' + return complement( normalized_fanins_for_and( a, !b ) ); + } + + if ( b.index == c.index ) + { + if ( b.complement == c.complement ) + { + // (a, b, b) = a b + a' b + return { false, { b } }; + } + // (a, b, b') = a b + a' b' = a xnor b = (a xor b)' + return complement( normalized_fanins_for_xor( a, b ) ); + } + + // (a b c) = a b + a' c + if ( b.complement ) + { + return { true, { a, !b, !c } }; + } + return { false, { a, b, c } }; +} + +// implemented by jasper +template<> +inline muxig_network::normalization_result muxig_network::normalized_fanout( muxig_signal a, muxig_signal b, muxig_signal c) +{ + //We have an ANDNOT. can be converted to ORNOT to flip output inverter + if(b.index == 0 && b.complement == 0 && c.index != 0){ + std::swap(a,c); + std::swap(b,c); + b = !b; + return { true, { c, a, !b } }; + } + + //We have an ORNOT. can be converted to ORAND to flip output inverter + if(c.index == 0 && c.complement == 1 && b.index != 0){ + return { true, { b, !c, a } }; + } + + return { false, { a, b, c } }; +} + +template<> +inline muxig_signal muxig_network::create_and( muxig_signal const& a, muxig_signal const& b ) +{ + return create_gate( a, b, get_constant( false ) ); +} + +template<> +inline muxig_signal muxig_network::create_xor( muxig_signal const& a, muxig_signal const& b ) +{ + return create_gate( a, !b, b ); +} + +template<> +inline muxig_signal muxig_network::create_maj( signal const& a, signal const& b, signal const& c ) +{ + auto sel = !create_xor( a, b ); + return create_gate( sel, a, c ); +} + +template<> +inline muxig_signal muxig_network::create_ite( signal a, signal b, signal c ) +{ + return create_gate( a, b, c ); +} + +template<> +inline bool muxig_network::is_ite( node const& n ) const +{ + return true; +} + +template<> +inline bool muxig_network::is_mux( node const& n ) const +{ + return true; +} + +template<> +inline kitty::dynamic_truth_table muxig_network::node_function( const node& n ) const +{ + (void)n; + kitty::dynamic_truth_table tt( 3 ); + tt._bits[0] = 0xd8; + return tt; +} + +} // namespace mockturtle diff --git a/include/mockturtle/networks/sequential.hpp b/include/mockturtle/networks/sequential.hpp new file mode 100644 index 0000000..b854a86 --- /dev/null +++ b/include/mockturtle/networks/sequential.hpp @@ -0,0 +1,925 @@ +/* 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 sequential.hpp + \brief Sequential extension to networks + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "aig.hpp" +#include "aqfp.hpp" +#include "cover.hpp" +#include "detail/foreach.hpp" +#include "klut.hpp" +#include "mig.hpp" +#include "xag.hpp" +#include "xmg.hpp" + +namespace mockturtle +{ + +namespace detail +{ + +template +struct is_aig_like : std::false_type +{ +}; + +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; +template<> +struct is_aig_like : std::true_type +{ +}; + +template +inline constexpr bool is_aig_like_v = is_aig_like::value; + +} // namespace detail + +/*! \brief Register information */ +struct register_t +{ + /*! \brief Control (clocking or enabling) signal */ + std::string control = ""; + /*! \brief Initial (reset) value */ + uint8_t init = 3; + /*! \brief Type of register or latch (active high/low, rising/falling edge) */ + std::string type = ""; +}; + +template> +class sequential +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Creates a register output in the network. + * + * Each created register output is stored in a node and contributes + * to the size of the network. Register outputs must be created + * after all primary inputs have been created and must have a + * corresponding register input that is created with `create_ri`. + * + * Register outputs serve as inputs for the network. + * + * Register outputs and register inputs always have to be created in + * pairs; they are associated to each other by index, i.e., the + * first created register output corresponds to the first created + * register input, etc. + */ + signal create_ro(); + + /*! \brief Creates a register input in the network. + * + * A register input is not stored in terms of a node, and it also + * does not contribute to the size of the network. A register input + * is created for a signal in the network and it is possible that + * multiple register inputs point to the same signal. Register + * inputs must be created after all primary outputs have been + * created and must have a corresponding register output that is + * created with `create_ro`. + * + * Register inputs serve as outputs for the network. + * + * Register outputs and register inputs always have to be created in + * pairs; they are associated to each other by index, i.e., the + * first created register output corresponds to the first created + * register input, etc. + * + * \param f Signal that drives the created register input + */ + uint32_t create_ri( signal const& f ); + + /*! \brief Checks whether a node is a combinational input (PI or RO). */ + bool is_ci( node const& n ) const; + + /*! \brief Checks whether a node is a register output. */ + bool is_ro( node const& n ) const; + + /*! \brief Checks whether the network is combinational. + * + * Returns true if and only if the network has no registers (neither + * register outputs nor register inputs). + */ + bool is_combinational() const; + + /*! \brief Returns the number of combinational inputs (PIs and ROs). */ + auto num_cis() const; + + /*! \brief Returns the number of combinational outputs (POs and RIs). */ + auto num_cos() const; + + /*! \brief Returns the number of registers. + * + * This number is usually equal to the number of register outputs + * and register inputs because they have to appear in pairs. During + * the construction of a network, the number of register outputs and + * register inputs may diverge. + */ + auto num_registers() const; + + /*! \brief Returns the combinational input node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational inputs (exclusive). + */ + node ci_at( uint32_t index ) const; + + /*! \brief Returns the combinational output signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * combinational outputs (exclusive). + */ + signal co_at( uint32_t index ) const; + + /*! \brief Returns the register output node for an index. + * + * \param index A value between 0 (inclusive) and the number of + * register outputs (exclusive). + */ + node ro_at( uint32_t index ) const; + + /*! \brief Returns the register input signal for an index. + * + * \param index A value between 0 (inclusive) and the number of + * register inputs (exclusive). + */ + signal ri_at( uint32_t index ) const; + + /*! \brief Returns the index of a combinational input node. + * + * \param n A combinational input node. + * \return A value between 0 and num_cis()-1. + */ + uint32_t ci_index( node const& n ) const; + + /*! \brief Returns the index of a combinational output signal. + * + * \param n A combinational output signal. + * \return A value between 0 and num_cos()-1. + */ + uint32_t co_index( signal const& s ) const; + + /*! \brief Returns the index of a register output node. + * + * \param n A register output node. + * \return A value between 0 and num_cis()-num_pis()-1. + */ + uint32_t ro_index( node const& n ) const; + + /*! \brief Returns the index of a register input signal. + * + * \param n A register input signal. + * \return A value between 0 and num_cos()-num_pos()-1. + */ + uint32_t ri_index( signal const& s ) const; + + /*! \brief Returns the register input signal to a register output node. + * + * \param signal A signal of a register output. + */ + signal ro_to_ri( signal const& s ) const; + + /*! \brief Returns the register output node for a register input signal. + * + * \param signal A node of a register input. + */ + node ri_to_ro( signal const& s ) const; + + /*! \brief Calls ``fn`` on every combinational input node in the network. + * + * The order is in the same order as combinational inputs have been + * created with ``create_pi`` or ``create_ro``. The parameter + * ``fn`` is any callable that must have one of the following four + * signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ci( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every combinational output signal in the network. + * + * The order is in the same order as combinational outputs have been + * created with ``create_po`` or ``create_ri``. The function is + * called on the signal that is driving the output and may occur + * more than once in the iteration, if it drives more than one + * output. The parameter ``fn`` is any callable that must have one + * of the following four + * signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_co( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every register output node in the network. + * + * The order is in the same order as register outputs have been created with + * ``create_ro``. The parameter ``fn`` is any callable that must have one of + * the following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ro( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every register input signal in the network. + * + * The order is in the same order as register inputs have been created with + * ``create_ri``. The function is called on the signal that is driving the + * output and may occur more than once in the iteration, if it drives more + * than one output. The parameter ``fn`` is any callable that must have one + * of the following four signatures. + * - ``void(signal const&)`` + * - ``void(signal const&, uint32_t)`` + * - ``bool(signal const&)`` + * - ``bool(signal const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_ri( Fn&& fn ) const; + + /*! \brief Calls ``fn`` on every pair of register input signal and + * register output node in the network. + * + * Calls each pair of a register input signal and the associated + * register output node. The parameter ``fn`` is any callable that + * must have one of the following four signatures. + * - ``void(std::pair const&)`` + * - ``void(std::pair const&, uint32_t)`` + * - ``bool(std::pair const&)`` + * - ``bool(std::pair const&, uint32_t)`` + * + * If ``fn`` has two parameters, the second parameter is an index starting + * from 0 and incremented in every iteration. If ``fn`` returns a ``bool``, + * then it can interrupt the iteration by returning ``false``. + */ + template + void foreach_register( Fn&& fn ) const; + + /*! \brief Sets the register information for an index. + * + * \param index A value between 0 (inclusive) and the number of + * registers (exclusive). + * \param reg Register information to set. + */ + void set_register( uint32_t index, register_t reg ); + + /*! \brief Returns the register information for an index. + * + * \param index A value between 0 (inclusive) and the number of + * registers (exclusive). + * \return Register information (see ``register_t``). + */ + register_t register_at( uint32_t index ) const; +}; + +template +class sequential : public Ntk +{ +public: + using base_type = typename Ntk::base_type; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct sequential_information + { + uint32_t num_pis{ 0 }; + uint32_t num_pos{ 0 }; + std::vector registers; + }; + + sequential() + : _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + sequential( storage base_storage ) + : Ntk( base_storage ), _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + signal create_pi() + { + ++_sequential_storage->num_pis; + return Ntk::create_pi(); + } + + uint32_t create_po( signal const& f ) + { + ++_sequential_storage->num_pos; + return Ntk::create_po( f ); + } + + signal create_ro() + { + _sequential_storage->registers.emplace_back(); + return Ntk::create_pi(); + } + + uint32_t create_ri( signal const& f ) + { + return Ntk::create_po( f ); + } + + bool is_combinational() const + { + return ( static_cast( this->_storage->inputs.size() ) == _sequential_storage->num_pis && + static_cast( this->_storage->outputs.size() ) == _sequential_storage->num_pos ); + } + + bool is_ci( node const& n ) const + { + return Ntk::is_pi( n ); + } + + bool is_pi( node const& n ) const + { + return Ntk::is_pi( n ) && this->_storage->nodes[n].children[0].data < static_cast( _sequential_storage->num_pis ); + } + + bool is_ro( node const& n ) const + { + return Ntk::is_pi( n ) && this->_storage->nodes[n].children[0].data >= static_cast( _sequential_storage->num_pis ); + } + + auto num_cis() const + { + return static_cast( this->_storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( this->_storage->outputs.size() ); + } + + auto num_pis() const + { + return _sequential_storage->num_pis; + } + + auto num_pos() const + { + return _sequential_storage->num_pos; + } + + auto num_registers() const + { + assert( static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ) == static_cast( this->_storage->outputs.size() - _sequential_storage->num_pos ) ); + return static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pos ); + return *( this->_storage->outputs.begin() + index ); + } + + node ci_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() ); + return *( this->_storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() ); + return *( this->_storage->outputs.begin() + index ); + } + + node ro_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() - _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() - _sequential_storage->num_pos ); + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + index ); + } + + void set_register( uint32_t index, register_t reg ) + { + assert( index < _sequential_storage->registers.size() ); + _sequential_storage->registers[index] = reg; + } + + register_t register_at( uint32_t index ) const + { + assert( index < _sequential_storage->registers.size() ); + return _sequential_storage->registers[index]; + } + + uint32_t pi_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data < _sequential_storage->num_pis ); + return Ntk::pi_index( n ); + } + + uint32_t ci_index( node const& n ) const + { + return Ntk::pi_index( n ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data >= _sequential_storage->num_pis ); + return Ntk::pi_index( n ) - _sequential_storage->num_pis; + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + this->_storage->nodes[s.index].children[0].data - _sequential_storage->num_pis ); + } + + node ri_to_ro( signal const& s ) const + { + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + ri_index( s ) ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin(), this->_storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.begin() + _sequential_storage->num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin(), this->_storage->outputs.begin() + _sequential_storage->num_pos, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + detail::foreach_element( this->_storage->outputs.begin() + _sequential_storage->num_pos, this->_storage->outputs.end(), fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + static_assert( detail::is_callable_with_index_v, void> || + detail::is_callable_without_index_v, void> || + detail::is_callable_with_index_v, bool> || + detail::is_callable_without_index_v, bool> ); + + assert( this->_storage->inputs.size() - _sequential_storage->num_pis == this->_storage->outputs.size() - _sequential_storage->num_pos ); + auto ro = this->_storage->inputs.begin() + _sequential_storage->num_pis; + auto ri = this->_storage->outputs.begin() + _sequential_storage->num_pos; + if constexpr ( detail::is_callable_without_index_v, bool> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ) ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v, bool> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ), index++ ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v, void> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ) ); + } + } + else if constexpr ( detail::is_callable_with_index_v, void> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ), index++ ); + } + } + } + +public: + std::shared_ptr _sequential_storage; +}; // class sequential + +template +class sequential : public Ntk +{ +public: + using base_type = typename Ntk::base_type; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct sequential_information + { + uint32_t num_pis{ 0 }; + uint32_t num_pos{ 0 }; + std::vector registers; + }; + + sequential() + : _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + sequential( storage base_storage ) + : Ntk( base_storage ), _sequential_storage( std::make_shared() ) + { + static_assert( std::is_same_v || std::is_same_v, + "Sequential interfaces extended for unknown network type. Please check the compatibility of implementations." ); + } + + signal create_pi() + { + ++_sequential_storage->num_pis; + return Ntk::create_pi(); + } + + uint32_t create_po( signal const& f ) + { + ++_sequential_storage->num_pos; + return Ntk::create_po( f ); + } + + signal create_ro() + { + _sequential_storage->registers.emplace_back(); + return Ntk::create_pi(); + } + + uint32_t create_ri( signal const& f ) + { + return Ntk::create_po( f ); + } + + bool is_combinational() const + { + return ( static_cast( this->_storage->inputs.size() ) == _sequential_storage->num_pis && + static_cast( this->_storage->outputs.size() ) == _sequential_storage->num_pos ); + } + + bool is_ci( node const& n ) const + { + return std::find( this->_storage->inputs.begin(), this->_storage->inputs.end(), n ) != this->_storage->inputs.end(); + } + + bool is_pi( node const& n ) const + { + const auto end = this->_storage->inputs.begin() + _sequential_storage->num_pis; + return std::find( this->_storage->inputs.begin(), end, n ) != end; + } + + bool is_ro( node const& n ) const + { + return std::find( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), n ) != this->_storage->inputs.end(); + } + + auto num_cis() const + { + return static_cast( this->_storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( this->_storage->outputs.size() ); + } + + auto num_pis() const + { + return _sequential_storage->num_pis; + } + + auto num_pos() const + { + return _sequential_storage->num_pos; + } + + auto num_registers() const + { + assert( static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ) == static_cast( this->_storage->outputs.size() - _sequential_storage->num_pos ) ); + return static_cast( this->_storage->inputs.size() - _sequential_storage->num_pis ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _sequential_storage->num_pos ); + return ( this->_storage->outputs.begin() + index )->index; + } + + node ci_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() ); + return *( this->_storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() ); + return ( this->_storage->outputs.begin() + index )->index; + } + + node ro_at( uint32_t index ) const + { + assert( index < this->_storage->inputs.size() - _sequential_storage->num_pis ); + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + index ); + } + + signal ri_at( uint32_t index ) const + { + assert( index < this->_storage->outputs.size() - _sequential_storage->num_pos ); + return ( this->_storage->outputs.begin() + _sequential_storage->num_pos + index )->index; + } + + void set_register( uint32_t index, register_t reg ) + { + assert( index < _sequential_storage->registers.size() ); + _sequential_storage->registers[index] = reg; + } + + register_t register_at( uint32_t index ) const + { + assert( index < _sequential_storage->registers.size() ); + return _sequential_storage->registers[index]; + } + + uint32_t pi_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data < _sequential_storage->num_pis ); + return Ntk::pi_index( n ); + } + + uint32_t ci_index( node const& n ) const + { + return Ntk::pi_index( n ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + uint32_t ro_index( node const& n ) const + { + assert( this->_storage->nodes[n].children[0].data >= _sequential_storage->num_pis ); + return Ntk::pi_index( n ) - _sequential_storage->num_pis; + } + + uint32_t ri_index( signal const& s ) const + { + uint32_t i = -1; + foreach_ri( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; } ); + return i; + } + + signal ro_to_ri( signal const& s ) const + { + return *( this->_storage->outputs.begin() + _sequential_storage->num_pos + this->_storage->nodes[s.index].children[0].data - _sequential_storage->num_pis ); + } + + node ri_to_ro( signal const& s ) const + { + return *( this->_storage->inputs.begin() + _sequential_storage->num_pis + ri_index( s ) ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin(), this->_storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin(), this->_storage->inputs.begin() + _sequential_storage->num_pis, fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin(), this->_storage->outputs.begin() + _sequential_storage->num_pos, []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + detail::foreach_element( this->_storage->inputs.begin() + _sequential_storage->num_pis, this->_storage->inputs.end(), fn ); + } + + template + void foreach_ri( Fn&& fn ) const + { + using IteratorType = decltype( this->_storage->outputs.begin() ); + detail::foreach_element_transform( + this->_storage->outputs.begin() + _sequential_storage->num_pos, this->_storage->outputs.end(), []( auto o ) { return o.index; }, fn ); + } + + template + void foreach_register( Fn&& fn ) const + { + static_assert( detail::is_callable_with_index_v, void> || + detail::is_callable_without_index_v, void> || + detail::is_callable_with_index_v, bool> || + detail::is_callable_without_index_v, bool> ); + + assert( this->_storage->inputs.size() - _sequential_storage->num_pis == this->_storage->outputs.size() - _sequential_storage->num_pos ); + auto ro = this->_storage->inputs.begin() + _sequential_storage->num_pis; + auto ri = this->_storage->outputs.begin() + _sequential_storage->num_pos; + if constexpr ( detail::is_callable_without_index_v, bool> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ) ) ) + return; + } + } + else if constexpr ( detail::is_callable_with_index_v, bool> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + if ( !fn( std::make_pair( ri++, ro++ ), index++ ) ) + return; + } + } + else if constexpr ( detail::is_callable_without_index_v, void> ) + { + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ) ); + } + } + else if constexpr ( detail::is_callable_with_index_v, void> ) + { + uint32_t index{ 0 }; + while ( ro != this->_storage->inputs.end() && ri != this->_storage->outputs.end() ) + { + fn( std::make_pair( *ri++, *ro++ ), index++ ); + } + } + } + +public: + std::shared_ptr _sequential_storage; +}; // class sequential + +} // namespace mockturtle diff --git a/include/mockturtle/networks/storage.hpp b/include/mockturtle/networks/storage.hpp new file mode 100644 index 0000000..85a2b42 --- /dev/null +++ b/include/mockturtle/networks/storage.hpp @@ -0,0 +1,259 @@ +/* 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 storage.hpp + \brief Configurable storage container + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace mockturtle +{ + +template +struct node_pointer +{ +private: + static constexpr auto _len = sizeof( uint64_t ) * 8; + +public: + node_pointer() = default; + node_pointer( uint64_t index, uint64_t weight ) : weight( weight ), index( index ) {} + node_pointer( uint64_t data ) : data( data ) {} + + union + { + struct + { + uint64_t weight : PointerFieldSize; + uint64_t index : _len - PointerFieldSize; + }; + uint64_t data; + }; + + bool operator==( node_pointer const& other ) const + { + return data == other.data; + } + + bool operator!=( node_pointer const& other ) const + { + return data != other.data; + } +}; + +template<> +struct node_pointer<0> +{ +public: + node_pointer() = default; + node_pointer( uint64_t index ) : index( index ) {} + + union + { + uint64_t index; + uint64_t data; + }; + + bool operator==( node_pointer<0> const& other ) const + { + return data == other.data; + } +}; + +union cauint64_t +{ + uint64_t n{ 0 }; + struct + { + uint64_t h1 : 32; + uint64_t h2 : 32; + }; + struct + { + uint64_t q1 : 16; + uint64_t q2 : 16; + uint64_t q3 : 16; + uint64_t q4 : 16; + }; +}; + +template +struct regular_node +{ + using pointer_type = node_pointer; + + std::array children; + std::array data; + + bool operator==( regular_node const& other ) const + { + return children == other.children; + } +}; + +template +struct mixed_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::array data; + + bool operator==( mixed_fanin_node const& other ) const + { + return children == other.children; + } +}; + +template +struct block_fanin_node +{ + using pointer_type = node_pointer; + + std::vector children; + std::vector data; + + bool operator==( block_fanin_node const& other ) const + { + return children == other.children; + } +}; + +/*! \brief Hash function for 64-bit word */ +inline uint64_t hash_block( uint64_t word ) +{ + /* from boost::hash_detail::hash_value_unsigned */ + return word ^ ( word + ( word << 6 ) + ( word >> 2 ) ); +} + +/*! \brief Combines two hash values */ +inline void hash_combine( uint64_t& seed, uint64_t other ) +{ + /* from boost::hash_detail::hash_combine_impl */ + const uint64_t m = UINT64_C( 0xc6a4a7935bd1e995 ); + const int r = 47; + + other *= m; + other ^= other >> r; + other *= m; + + seed ^= other; + seed *= m; + + seed += 0xe6546b64; +} + +template +struct node_hash +{ + uint64_t operator()( const Node& n ) const + { + if ( n.children.size() == 0 ) + return 0; + + auto it = std::begin( n.children ); + auto seed = hash_block( it->data ); + ++it; + + while ( it != std::end( n.children ) ) + { + hash_combine( seed, hash_block( it->data ) ); + ++it; + } + + return seed; + } +}; + +struct empty_storage_data +{ +}; + +template> +struct storage +{ + storage() + { + nodes.reserve( 10000u ); + hash.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + phmap::flat_hash_map hash; + + T data; +}; + +template +struct storage_no_hash +{ + storage_no_hash() + { + nodes.reserve( 10000u ); + + /* we generally reserve the first node for a constant */ + nodes.emplace_back(); + } + + using node_type = Node; + + uint32_t trav_id = 0u; + + std::vector nodes; + std::vector inputs; + std::vector outputs; + + T data; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/networks/tig.hpp b/include/mockturtle/networks/tig.hpp new file mode 100644 index 0000000..8a95d26 --- /dev/null +++ b/include/mockturtle/networks/tig.hpp @@ -0,0 +1,1134 @@ +/* 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 tig.hpp + \brief Three-input-gate-inverter graph logic network implementation + + \author Dewmini Marakkalage + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ +enum class three_input_function +{ + dot, + onehot, + mux, + andxor, + xorand, + gamble, + orand, + majority, + and3, + xor3 +}; + +/* Remark: Must be specialized. */ +template +struct compute_function +{ + template + std::enable_if_t::value, T> operator()( T a, T b, T c ); + + template + std::enable_if_t::value, T> operator()( T a, T b, T c ); +}; + +using tig_storage = storage>; + +struct tig_signal +{ + tig_signal() = default; + + tig_signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit tig_signal( uint64_t data ) + : data( data ) + { + } + + tig_signal( tig_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + tig_signal operator!() const + { + return tig_signal( data ^ 1 ); + } + + tig_signal operator+() const + { + return { index, 0 }; + } + + tig_signal operator-() const + { + return { index, 1 }; + } + + tig_signal operator^( bool complement ) const + { + return tig_signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( tig_signal const& other ) const + { + return data == other.data; + } + + bool operator!=( tig_signal const& other ) const + { + return data != other.data; + } + + bool operator<( tig_signal const& other ) const + { + return data < other.data; + } + + operator tig_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( tig_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif +}; + +/*! \brief T-Inverter-Graph storage container + TIGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + `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 + */ + +template +class tig_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = tig_network; + using storage = std::shared_ptr; + using node = uint64_t; + using signal = tig_signal; + + tig_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + tig_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + tig_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + 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(); + _storage->inputs.emplace_back( index ); + return { index, 0 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + 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; + } + + 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 ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + + struct normalization_result + { + bool output_compl; + std::vector fanins; + }; + + /*! + * \brief Normalizes fanins. If the gate degenerates to a single fanin, sets + * the first fanin in the return value to the corresponding degenerate signal. + * Remark: Must be specialized in each three-input-gate-network type. + */ + normalization_result normalized_fanins( signal a, signal b, signal c ); + normalization_result normalized_fanout( signal a, signal b, signal c ); + + signal create_gate( signal a, signal b, signal c ) + { + auto norm_res = normalized_fanins( a, b, c ); + + if ( norm_res.fanins.size() == 1u ) + { + return norm_res.fanins[0]; + } + + storage::element_type::node_type node; + node.children[0] = norm_res.fanins[0]; + node.children[1] = norm_res.fanins[1]; + node.children[2] = norm_res.fanins[2]; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, norm_res.output_compl }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, norm_res.output_compl }; + } + + /* Remark: Must be specialized. */ + signal create_and( signal const& a, signal const& b ); + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + /* Remark: To be specialized. */ + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + /* Remark: To be specialized. */ + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + return create_or( create_and( a, b ), create_and( c, create_or( a, b ) ) ); + } + + /* Remark: To be specialized. */ + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + /* Remark: To be specialized. */ + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( a, create_xor( b, c ) ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( tig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_gate( children[0u], children[1u], children[2u] ); + } +#pragma endregion + +#pragma region Restructuring + + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + std::array child; + + bool found = false; + for ( auto i = 0u; i < 3u; ++i ) + { + if ( node.children[i].index == old_node ) + { + found = true; + child[i] = { new_signal.index, new_signal.complement ^ node.children[i].weight }; + } + else + { + child[i] = node.children[i]; + } + } + + if ( !found ) + { + return std::nullopt; + } + + auto norm_res = normalized_fanins( child[0], child[1], child[2] ); + if ( norm_res.fanins.size() == 1u ) + { + return std::make_pair( n, norm_res.fanins[0] ); + } + + // node already in hash table and is alive + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child[0]; + _hash_obj.children[1] = child[1]; + _hash_obj.children[2] = child[2]; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child[0]; + node.children[1] = child[1]; + node.children[2] = child[2]; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + // increment fan-in of new node + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + //if ( get_node( new_signal ) == old_node && !is_complemented( new_signal ) ) + // return; + + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + //if ( get_node( _new ) == _old && !is_complemented( _new ) ) + // continue; + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + /* One of the is_{gate} functions for three-input gates needs to be specialized. */ + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + return is_mux( n ); + } + + bool is_mux( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_and3( node const& n ) const + { + (void)n; + return false; + } + + bool is_dot( node const& n ) const + { + (void)n; + return false; + } + + bool is_onehot( node const& n ) const + { + (void)n; + return false; + } + + bool is_orand( node const& n ) const + { + (void)n; + return false; + } + + bool is_xorand( node const& n ) const + { + (void)n; + return false; + } + + bool is_andxor( node const& n ) const + { + (void)n; + return false; + } + + bool is_gamble( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + /* Remark: Must be specialized */ + kitty::dynamic_truth_table node_function( const node& n ) const; +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + return compute_function()( v1 ^ c1.weight, v2 ^ c2.weight, v3 ^ c3.weight ); + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + return compute_function()( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); + + result.resize( tt1.num_bits() ); + result._bits.back() = compute_function()( + c1.weight ? ~tt1._bits.back() : tt1._bits.back(), + c2.weight ? ~tt2._bits.back() : tt2._bits.back(), + c3.weight ? ~tt3._bits.back() : tt3._bits.back() ); + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; + +private: + normalization_result normalized_fanins_for_and( signal a, signal b ); + normalization_result normalized_fanins_for_xor( signal a, signal b ); + normalization_result complement( normalization_result res ) + { + res.output_compl = !res.output_compl; + if ( res.fanins.size() != 1 ) + { + return res; + } + return { false, { res.output_compl ? !res.fanins[0] : res.fanins[0] } }; + } +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::tig_signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std diff --git a/include/mockturtle/networks/xag.hpp b/include/mockturtle/networks/xag.hpp new file mode 100644 index 0000000..17606a2 --- /dev/null +++ b/include/mockturtle/networks/xag.hpp @@ -0,0 +1,1288 @@ +/* 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 xag.hpp + \brief Xor-And Graph (XAG) logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Eleonora Testa + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief Hash function for XAGs -- the same as for AIGs (from ABC) */ +template +struct xag_hash +{ + uint64_t operator()( Node const& n ) const + { + uint64_t seed = -2011; + seed += n.children[0].index * 7937; + seed += n.children[1].index * 2971; + seed += n.children[0].weight * 911; + seed += n.children[1].weight * 353; + return seed; + } +}; + +/*! \brief XAG storage container + + XAGs have nodes with fan-in 2. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `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, + xag_hash>>; + +class xag_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 2u; + static constexpr auto max_fanin_size = 2u; + + using base_type = xag_network; + using storage = std::shared_ptr; + using node = uint64_t; + + struct signal + { + signal() = default; + + signal( uint64_t index, uint64_t complement ) + : complement( complement ), index( index ) + { + } + + explicit signal( uint64_t data ) + : data( data ) + { + } + + signal( xag_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + uint64_t complement : 1; + uint64_t index : 63; + }; + uint64_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator xag_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( xag_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + xag_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + xag_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + xag_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + 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 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary functions + signal _create_node( signal a, signal b ) + { + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, 0 }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415f * index ) ); + _storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + + signal create_and( signal a, signal b ) + { + /* order inputs a < b it is a AND */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + if ( a.index == b.index ) + { + return a.complement == b.complement ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + return _create_node( a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return !create_and( !a, !b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return create_and( !a, !b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal a, signal b ) + { + /* order inputs a > b it is a XOR */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + bool f_compl = a.complement != b.complement; + a.complement = b.complement = false; + + if ( a.index == b.index ) + { + return get_constant( f_compl ); + } + else if ( b.index == 0 ) + { + return a ^ f_compl; + } + + return _create_node( a, b ) ^ f_compl; + } + + signal create_xnor( signal const& a, signal const& b ) + { + return !create_xor( a, b ); + } +#pragma endregion + +#pragma region Create ternary functions + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_xor( create_and( !cond, create_xor( f_then, f_else ) ), f_then ) ^ f_compl; + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + auto c1 = create_xor( a, b ); + auto c2 = create_xor( a, c ); + auto c3 = create_and( c1, c2 ); + return create_xor( a, c3 ); + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + return create_xor( create_xor( a, b ), c ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( xag_network const& other, node const& source, std::vector const& children ) + { + assert( children.size() == 2u ); + if ( other.is_and( source ) ) + { + return create_and( children[0u], children[1u] ); + } + else + { + return create_xor( children[0u], children[1u] ); + } + } +#pragma endregion + +#pragma region Has node + std::optional has_and( signal a, signal b ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : get_constant( false ); + } + else if ( a.index == 0 ) + { + return a.complement == false ? get_constant( false ) : b; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, 0 ); + } + + return {}; + } + + std::optional has_xor( signal a, signal b ) + { + /* order inputs */ + if ( a.index < b.index ) + { + std::swap( a, b ); + } + + bool f_compl = a.complement != b.complement; + a.complement = b.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return get_constant( f_compl ); + } + else if ( b.index == 0 ) + { + return a ^ f_compl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, f_compl ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return std::nullopt; + } + + // determine gate type of n + auto _is_and = node.children[0].index <= node.children[1].index; + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( ( _is_and && child0.index > child1.index ) || ( !_is_and && child0.index < child1.index ) ) + { + std::swap( child0, child1 ); + } + + // check for trivial cases? + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + if ( _is_and ) + { + return std::make_pair( n, diff_pol ? get_constant( false ) : child1 ); + } + else + { + return std::make_pair( n, get_constant( diff_pol ) ); + } + } + else if ( _is_and && child0.index == 0 ) /* constant child */ + { + return std::make_pair( n, child0.complement ? child1 : get_constant( false ) ); + } + else if ( !_is_and && child1.index == 0 ) + { + return std::make_pair( n, child0 ^ child1.complement ); + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + if ( node.children[0].index == old_node ) + { + fanin = 0u; + new_signal.complement ^= node.children[0].weight; + } + else if ( node.children[1].index == old_node ) + { + fanin = 1u; + new_signal.complement ^= node.children[1].weight; + } + else + { + return; + } + + // determine gate type of n + auto _is_and = node.children[0].index <= node.children[1].index; + + // determine potential new children of node n + signal child1 = new_signal; + signal child0 = node.children[fanin ^ 1]; + + if ( ( _is_and && child0.index > child1.index ) || ( !_is_and && child0.index < child1.index ) ) + { + 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 + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 2u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 2u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + auto size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + auto num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 2; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + 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 ); + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + return n > 0 && !is_ci( n ) && ( _storage->nodes[n].children[0].index > _storage->nodes[n].children[1].index ); + } + + bool is_maj( node const& n ) const + { + (void)n; + return false; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + 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 ) + { + _func._bits[0] = 0x8; + return _func; + } + else + { + _func._bits[0] = 0x6; + return _func; + } + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data ); + return static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); /* start from 1 to avoid constant */ + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + /* we don't use foreach_element here to have better performance */ + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto v1 = *begin++; + auto v2 = *begin++; + + if ( c1.index <= c2.index ) + { + return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); + } + else + { + return ( v1 ^ c1.weight ) ^ ( v2 ^ c2.weight ); + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + if ( c1.index <= c2.index ) + { + return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); + } + else + { + return ( c1.weight ? ~tt1 : tt1 ) ^ ( c2.weight ? ~tt2 : tt2 ); + } + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + 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 ) + { + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + } + else + { + result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) ^ ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); + } + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::xag_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/networks/xmg.hpp b/include/mockturtle/networks/xmg.hpp new file mode 100644 index 0000000..536b68e --- /dev/null +++ b/include/mockturtle/networks/xmg.hpp @@ -0,0 +1,1493 @@ +/* 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 xmg.hpp + \brief XMG logic network implementation + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Jinzheng Tu + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee + \author Walter Lau Neto +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "../traits.hpp" +#include "../utils/algorithm.hpp" +#include "detail/foreach.hpp" +#include "events.hpp" +#include "storage.hpp" + +namespace mockturtle +{ + +/*! \brief XMG storage container + + XMGs have nodes with fan-in 3. We split of one bit of the index pointer to + store a complemented attribute. Every node has 64-bit of additional data + used for the following purposes: + + `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>; + +class xmg_network +{ +public: +#pragma region Types and constructors + static constexpr auto min_fanin_size = 3u; + static constexpr auto max_fanin_size = 3u; + + using base_type = xmg_network; + using storage = std::shared_ptr; + using node = std::size_t; + + struct signal + { + signal() = default; + + signal( std::size_t index, std::size_t complement ) + : complement( complement ), index( index ) + { + } + + signal( std::size_t data ) + : data( data ) + { + } + + signal( xmg_storage::node_type::pointer_type const& p ) + : complement( p.weight ), index( p.index ) + { + } + + union + { + struct + { + std::size_t complement : 1; + std::size_t index : 63; + }; + std::size_t data; + }; + + signal operator!() const + { + return signal( data ^ 1 ); + } + + signal operator+() const + { + return { index, 0 }; + } + + signal operator-() const + { + return { index, 1 }; + } + + signal operator^( bool complement ) const + { + return signal( data ^ ( complement ? 1 : 0 ) ); + } + + bool operator==( signal const& other ) const + { + return data == other.data; + } + + bool operator!=( signal const& other ) const + { + return data != other.data; + } + + bool operator<( signal const& other ) const + { + return data < other.data; + } + + operator xmg_storage::node_type::pointer_type() const + { + return { index, complement }; + } + +#if __cplusplus > 201703L + bool operator==( xmg_storage::node_type::pointer_type const& other ) const + { + return data == other.data; + } +#endif + }; + + xmg_network() + : _storage( std::make_shared() ), + _events( std::make_shared() ) + { + } + + xmg_network( std::shared_ptr storage ) + : _storage( storage ), + _events( std::make_shared() ) + { + } + + xmg_network clone() const + { + return { std::make_shared( *_storage ) }; + } +#pragma endregion + +#pragma region Primary I / O and constants + signal get_constant( bool value ) const + { + return { 0, static_cast( value ? 1 : 0 ) }; + } + + signal create_pi() + { + 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 }; + } + + uint32_t create_po( signal const& f ) + { + /* increase ref-count to children */ + _storage->nodes[f.index].data[0].h1++; + auto const po_index = static_cast( _storage->outputs.size() ); + _storage->outputs.emplace_back( f.index, f.complement ); + return po_index; + } + + bool is_combinational() const + { + return true; + } + + bool is_constant( node const& n ) const + { + return n == 0; + } + + bool is_ci( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1; + } + + bool is_pi( node const& n ) const + { + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); + } + + bool constant_value( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Create unary functions + signal create_buf( signal const& a ) + { + return a; + } + + signal create_not( signal const& a ) + { + return !a; + } +#pragma endregion + +#pragma region Create binary / ternary functions + signal create_maj( 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 ); + } + + /* 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, node_complement }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415 * index ) ); + _storage->hash.reserve( static_cast( 3.1415 * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, node_complement }; + } + + signal create_xor3( 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 ); + } + + /* propagate complement edges */ + bool fcompl = ( a.complement != b.complement ) != c.complement; + a.complement = b.complement = c.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return c ^ fcompl; + } + else if ( b.index == c.index ) + { + return a ^ fcompl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + return { it->second, fcompl }; + } + + const auto index = _storage->nodes.size(); + + if ( index >= .9 * _storage->nodes.capacity() ) + { + _storage->nodes.reserve( static_cast( 3.1415 * index ) ); + _storage->hash.reserve( static_cast( 3.1415 * index ) ); + } + + _storage->nodes.push_back( node ); + + _storage->hash[node] = index; + + /* increase ref-count to children */ + _storage->nodes[a.index].data[0].h1++; + _storage->nodes[b.index].data[0].h1++; + _storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( index ); + } + + return { index, fcompl }; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + return create_xor3( get_constant( false ), a, b ); + } + + signal create_xnor( signal const& a, signal const& b ) + { + return create_xor3( get_constant( true ), a, b ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return ternary_tree_reduce( fs.begin(), fs.end(), get_constant( false ), [this]( auto const& a, auto const& b, auto const& c ) { return create_xor3( a, b, c ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( xmg_network const& other, node const& source, std::vector const& children ) + { + assert( children.size() == 3u ); + + if ( other.is_maj( source ) ) + { + return create_maj( children[0u], children[1u], children[2u] ); + } + else + { + return create_xor3( children[0u], children[1u], children[2u] ); + } + } +#pragma endregion + +#pragma region Has node + std::optional has_maj( 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 ); + } + + /* 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, node_complement ); + } + + return {}; + } + + std::optional has_xor3( 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 ); + } + + /* propagate complement edges */ + bool fcompl = ( a.complement != b.complement ) != c.complement; + a.complement = b.complement = c.complement = false; + + /* trivial cases */ + if ( a.index == b.index ) + { + return c ^ fcompl; + } + else if ( b.index == c.index ) + { + return a ^ fcompl; + } + + storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + const auto it = _storage->hash.find( node ); + if ( it != _storage->hash.end() ) + { + assert( !is_dead( it->second ) ); + return signal( it->second, fcompl ); + } + + return {}; + } +#pragma endregion + +#pragma region Restructuring + std::optional> replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return std::nullopt; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + auto _is_maj = is_maj( n ); + + /* normalize order */ + if ( _is_maj ) + { + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + } + else + { + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index < child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index >= child1.index ); + assert( child1.index >= child2.index ); + } + + // normalize complemented edges + auto node_complement = false; + if ( _is_maj ) + { + if ( static_cast( child0.complement ) + static_cast( child1.complement ) + + static_cast( child2.complement ) >= + 2u ) + { + node_complement = true; + child0.complement = !child0.complement; + child1.complement = !child1.complement; + child2.complement = !child2.complement; + } + } + else + { + node_complement = ( child0.complement != child1.complement ) != child2.complement; + child0.complement = child1.complement = child2.complement = false; + } + + // check for trivial cases? + if ( _is_maj ) + { + if ( child0.index == child1.index ) + { + const auto diff_pol = child0.complement != child1.complement; + return std::make_pair( n, ( diff_pol ? child2 : child0 ) ^ node_complement ); + } + else if ( child1.index == child2.index ) + { + const auto diff_pol = child1.complement != child2.complement; + return std::make_pair( n, ( diff_pol ? child0 : child1 ) ^ node_complement ); + } + } + else + { + if ( child0.index == child1.index ) + { + return std::make_pair( n, child2 ^ node_complement ); + } + else if ( child1.index == child2.index ) + { + return std::make_pair( n, child0 ^ node_complement ); + } + } + + // node already in hash table + storage::element_type::node_type _hash_obj; + _hash_obj.children[0] = child0; + _hash_obj.children[1] = child1; + _hash_obj.children[2] = child2; + if ( const auto it = _storage->hash.find( _hash_obj ); it != _storage->hash.end() && it->second != old_node ) + { + return std::make_pair( n, signal( it->second, 0 ) ); + } + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + _storage->hash[node] = n; + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + + return std::nullopt; + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + auto& node = _storage->nodes[n]; + + uint32_t fanin = 0u; + for ( auto i = 0u; i < 4u; ++i ) + { + if ( i == 3u ) + { + return; + } + + if ( node.children[i].index == old_node ) + { + fanin = i; + new_signal.complement ^= node.children[i].weight; + break; + } + } + + // determine potential new children of node n + signal child2 = new_signal; + signal child1 = node.children[( fanin + 1 ) % 3]; + signal child0 = node.children[( fanin + 2 ) % 3]; + + auto _is_maj = is_maj( n ); + + /* normalize order */ + if ( _is_maj ) + { + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index > child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index > child1.index ) + { + std::swap( child0, child1 ); + } + + assert( child0.index <= child1.index ); + assert( child1.index <= child2.index ); + } + else + { + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + if ( child1.index < child2.index ) + { + std::swap( child1, child2 ); + } + if ( child0.index < child1.index ) + { + std::swap( child0, child1 ); + } + + 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 + auto node_complement = false; + if ( _is_maj ) + { + if ( static_cast( child0.complement ) + static_cast( child1.complement ) + + static_cast( child2.complement ) >= + 2u ) + { + node_complement = true; + child0.complement = !child0.complement; + child1.complement = !child1.complement; + child2.complement = !child2.complement; + } + } + else + { + node_complement = ( child0.complement != child1.complement ) != child2.complement; + child0.complement = child1.complement = child2.complement = false; + } + + // don't check for trivial cases + + // remember before + const auto old_child0 = signal{ node.children[0] }; + const auto old_child1 = signal{ node.children[1] }; + const auto old_child2 = signal{ node.children[2] }; + + // erase old node in hash table + _storage->hash.erase( node ); + + // insert updated node into hash table + node.children[0] = child0; + node.children[1] = child1; + node.children[2] = child2; + if ( _storage->hash.find( node ) == _storage->hash.end() ) + { + _storage->hash[node] = n; + } + + // update the reference counter of the new signal + _storage->nodes[new_signal.index].data[0].h1++; + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, { old_child0, old_child1, old_child2 } ); + } + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output.index = new_signal.index; + output.weight ^= new_signal.complement; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[0].h1++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || is_ci( n ) || is_dead( n ) ) + return; + + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + _storage->hash.erase( nobj ); + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + for ( auto i = 0u; i < 3u; ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + if ( !is_dead( n ) ) + return; + + assert( n < _storage->nodes.size() ); + auto& nobj = _storage->nodes[n]; + nobj.data[0].h1 = UINT32_C( 0 ); /* fanout size 0, but not dead (like just created) */ + _storage->hash[nobj] = n; + + for ( auto const& fn : _events->on_add ) + { + ( *fn )( n ); + } + + /* revive its children if dead, and increment their fanout_size */ + for ( auto i = 0u; i < 3u; ++i ) + { + if ( is_dead( nobj.children[i].index ) ) + { + revive_node( nobj.children[i].index ); + } + incr_fanout_size( nobj.children[i].index ); + } + } + + inline bool is_dead( node const& n ) const + { + return ( _storage->nodes[n].data[0].h1 >> 31 ) & 1; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( is_dead( get_node( _new ) ) ) + { + auto it = old_to_new.find( get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = is_complemented( _new ) ? create_not( it->second ) : it->second; + it = old_to_new.find( get_node( _new ) ); + } + } + /* revive */ + if ( is_dead( get_node( _new ) ) ) + { + revive_node( get_node( _new ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + replace_in_outputs( _old, _new ); + + // reset fan-in of old node + if ( _old != _new.index ) + { + old_to_new.insert( { _old, _new } ); + take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( is_dead( get_node( new_signal ) ) ) + { + revive_node( get_node( new_signal ) ); + } + + for ( auto idx = 1u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node_no_restrash( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } +#pragma endregion + +#pragma region Structural properties + uint32_t size() const + { + return static_cast( _storage->nodes.size() ); + } + + auto num_cis() const + { + return static_cast( _storage->inputs.size() ); + } + + auto num_cos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_pis() const + { + return static_cast( _storage->inputs.size() ); + } + + uint32_t num_pos() const + { + return static_cast( _storage->outputs.size() ); + } + + uint32_t num_gates() const + { + return static_cast( _storage->hash.size() ); + } + + uint32_t fanin_size( node const& n ) const + { + if ( is_constant( n ) || is_ci( n ) ) + return 0; + return 3; + } + + uint32_t fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return _storage->nodes[n].data[0].h1++ & UINT32_C( 0x7FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --_storage->nodes[n].data[0].h1 & UINT32_C( 0x7FFFFFFF ); + } + + bool is_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor( node const& n ) const + { + (void)n; + return false; + } + + 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; + } + + bool is_ite( node const& n ) const + { + (void)n; + return false; + } + + bool is_xor3( node const& n ) const + { + return n > 0 && !is_ci( n ) && _storage->nodes[n].children[0].index > _storage->nodes[n].children[1].index; + } + + bool is_nary_and( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_or( node const& n ) const + { + (void)n; + return false; + } + + bool is_nary_xor( node const& n ) const + { + (void)n; + return false; + } +#pragma endregion + +#pragma region Functional properties + kitty::dynamic_truth_table node_function( const node& n ) const + { + kitty::dynamic_truth_table _tt( 3 ); + _tt._bits[0] = is_xor3( n ) ? 0x96 : 0xe8; + return _tt; + } +#pragma endregion + +#pragma region Nodes and signals + node get_node( signal const& f ) const + { + return f.index; + } + + signal make_signal( node const& n ) const + { + return signal( n, 0 ); + } + + bool is_complemented( signal const& f ) const + { + return f.complement; + } + + uint32_t node_to_index( node const& n ) const + { + return static_cast( n ); + } + + node index_to_node( uint32_t index ) const + { + return index; + } + + node ci_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal co_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + node pi_at( uint32_t index ) const + { + assert( index < _storage->inputs.size() ); + return *( _storage->inputs.begin() + index ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _storage->outputs.size() ); + return *( _storage->outputs.begin() + index ); + } + + uint32_t ci_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t co_index( signal const& s ) const + { + uint32_t i = -1; + foreach_co( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } + + uint32_t pi_index( node const& n ) const + { + assert( _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 static_cast( _storage->nodes[n].children[0].data ); + } + + uint32_t po_index( signal const& s ) const + { + uint32_t i = -1; + foreach_po( [&]( const auto& x, auto index ) { + if ( x == s ) + { + i = index; + return false; + } + return true; + } ); + return i; + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_node( Fn&& fn ) const + { + auto r = range( _storage->nodes.size() ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _storage->inputs.begin(), _storage->inputs.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( _storage->outputs.begin(), _storage->outputs.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + auto r = range( 1u, _storage->nodes.size() ); // start from 1 to avoid constant + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, + fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + if ( n == 0 || is_ci( n ) ) + return; + + static_assert( detail::is_callable_without_index_v || + detail::is_callable_with_index_v || + detail::is_callable_without_index_v || + detail::is_callable_with_index_v ); + + // we don't use foreach_element here to have better performance + if constexpr ( detail::is_callable_without_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] } ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] } ) ) + return; + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + if ( !fn( signal{ _storage->nodes[n].children[0] }, 0 ) ) + return; + if ( !fn( signal{ _storage->nodes[n].children[1] }, 1 ) ) + return; + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + else if constexpr ( detail::is_callable_without_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] } ); + fn( signal{ _storage->nodes[n].children[1] } ); + fn( signal{ _storage->nodes[n].children[2] } ); + } + else if constexpr ( detail::is_callable_with_index_v ) + { + fn( signal{ _storage->nodes[n].children[0] }, 0 ); + fn( signal{ _storage->nodes[n].children[1] }, 1 ); + fn( signal{ _storage->nodes[n].children[2] }, 2 ); + } + } +#pragma endregion + +#pragma region Value simulation + template + iterates_over_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto v1 = *begin++; + auto v2 = *begin++; + auto v3 = *begin++; + + if ( is_xor3( n ) ) + { + return ( ( v1 ^ c1.weight ) != ( v2 ^ c2.weight ) ) != ( v3 ^ c3.weight ); + } + else + { + return ( ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ) ) || ( ( v3 ^ c3.weight ) && ( v1 ^ c1.weight ) ) || ( ( v3 ^ c3.weight ) && ( v2 ^ c2.weight ) ); + } + } + + template + iterates_over_truth_table_t + compute( node const& n, Iterator begin, Iterator end ) const + { + (void)end; + + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + if ( is_xor3( n ) ) + { + return ( c1.weight ? ~tt1 : tt1 ) ^ ( c2.weight ? ~tt2 : tt2 ) ^ ( c3.weight ? ~tt3 : tt3 ); + } + else + { + return kitty::ternary_majority( c1.weight ? ~tt1 : tt1, c2.weight ? ~tt2 : tt2, c3.weight ? ~tt3 : tt3 ); + } + } + + /*! \brief Re-compute the last block. */ + template + void compute( node const& n, kitty::partial_truth_table& result, Iterator begin, Iterator end ) const + { + static_assert( iterates_over_v, "begin and end have to iterate over partial_truth_tables" ); + + (void)end; + assert( n != 0 && !is_ci( n ) ); + + auto const& c1 = _storage->nodes[n].children[0]; + auto const& c2 = _storage->nodes[n].children[1]; + auto const& c3 = _storage->nodes[n].children[2]; + + auto tt1 = *begin++; + auto tt2 = *begin++; + auto tt3 = *begin++; + + assert( tt1.num_bits() > 0 && "truth tables must not be empty" ); + assert( tt1.num_bits() == tt2.num_bits() ); + assert( tt1.num_bits() == tt3.num_bits() ); + assert( tt1.num_bits() >= result.num_bits() ); + 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 ( is_xor3( n ) ) + { + result._bits.back() = + ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) ^ + ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ^ + ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ); + } + else + { + result._bits.back() = + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) ) | + ( ( c1.weight ? ~tt1._bits.back() : tt1._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ) | + ( ( c2.weight ? ~tt2._bits.back() : tt2._bits.back() ) & ( c3.weight ? ~tt3._bits.back() : tt3._bits.back() ) ); + } + result.mask_bits(); + } +#pragma endregion + +#pragma region Custom node values + void clear_values() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[0].h2 = 0; } ); + } + + auto value( node const& n ) const + { + return _storage->nodes[n].data[0].h2; + } + + void set_value( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[0].h2 = v; + } + + auto incr_value( node const& n ) const + { + return _storage->nodes[n].data[0].h2++; + } + + auto decr_value( node const& n ) const + { + return --_storage->nodes[n].data[0].h2; + } +#pragma endregion + +#pragma region Visited flags + void clear_visited() const + { + std::for_each( _storage->nodes.begin(), _storage->nodes.end(), []( auto& n ) { n.data[1].h1 = 0; } ); + } + + auto visited( node const& n ) const + { + return _storage->nodes[n].data[1].h1; + } + + void set_visited( node const& n, uint32_t v ) const + { + _storage->nodes[n].data[1].h1 = v; + } + + uint32_t trav_id() const + { + return _storage->trav_id; + } + + void incr_trav_id() const + { + ++_storage->trav_id; + } +#pragma endregion + +#pragma region General methods + auto& events() const + { + return *_events; + } +#pragma endregion + +public: + std::shared_ptr _storage; + std::shared_ptr> _events; +}; + +} // namespace mockturtle + +namespace std +{ + +template<> +struct hash +{ + uint64_t operator()( mockturtle::xmg_network::signal const& s ) const noexcept + { + uint64_t k = s.data; + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +}; /* hash */ + +} // namespace std \ No newline at end of file diff --git a/include/mockturtle/properties/aqfpcost.hpp b/include/mockturtle/properties/aqfpcost.hpp new file mode 100644 index 0000000..dd1503a --- /dev/null +++ b/include/mockturtle/properties/aqfpcost.hpp @@ -0,0 +1,237 @@ +/* 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 aqfpcost.hpp + \brief Cost functions for AQFP networks + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../utils/hash_functions.hpp" +#include "../views/fanout_view.hpp" + +#include "../algorithms/aqfp/aqfp_assumptions.hpp" + +namespace mockturtle +{ + +/*! \brief Cost function for computing the best splitter and buffer cost for a fanout net with given relative levels. */ +class fanout_net_cost +{ +public: + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + fanout_net_cost( const std::unordered_map& splitters ) + : buffer_cost( splitters.at( 1u ) ), splitters( remove_buffer( splitters ) ) + { + } + + double operator()( const std::vector& config ) + { + return cost_for_config( config, false ); + } + + double operator()( const std::vector& config, bool ignore_initial_buffers ) + { + return cost_for_config( config, ignore_initial_buffers ); + } + +private: + using cache_key_t = std::tuple>; + + double buffer_cost; + std::unordered_map splitters; + std::unordered_map> cache; + + static std::unordered_map remove_buffer( std::unordered_map splitters ) + { + splitters.erase( 1u ); + return splitters; + } + + double cost_for_config( const std::vector config, bool ignore_initial_buffers ) + { + if ( config.size() == 1 ) + { + if ( config[0] >= 1 ) + { + return ignore_initial_buffers ? 0.0 : ( config[0] - 1 ) * buffer_cost; + } + else + { + return IMPOSSIBLE; + } + } + + std::tuple key = { ignore_initial_buffers, config }; + if ( cache.count( key ) ) + { + return cache[key]; + } + + auto result = IMPOSSIBLE; + + for ( const auto& s : splitters ) + { + for ( auto size = 2u; size <= std::min( s.first, uint32_t( config.size() ) ); size++ ) + { + auto sp_lev = config[config.size() - size] - 1; + if ( sp_lev == 0 ) + { + continue; + } + + auto temp = s.second; + + for ( auto i = config.size() - size; i < config.size(); i++ ) + { + temp += ( config[i] - config[config.size() - size] ) * buffer_cost; + } + + std::vector new_config( config.begin(), config.begin() + ( config.size() - size ) ); + new_config.push_back( sp_lev ); + std::stable_sort( new_config.begin(), new_config.end() ); + + temp += cost_for_config( new_config, ignore_initial_buffers ); + + if ( temp < result ) + { + result = temp; + } + } + } + + return ( cache[key] = result ); + } +}; + +/*! \brief Cost function for computing the cost of a path-balanced AQFP network with a given assignment of node levels. + * + * Assumes no path balancing or splitters are needed for primary inputs or register outputs. + */ +struct aqfp_network_cost +{ + static constexpr double IMPOSSIBLE = std::numeric_limits::infinity(); + + aqfp_network_cost( const aqfp_assumptions& assume, const std::unordered_map& gate_costs, const std::unordered_map& splitters ) + : assume( assume ), gate_costs( gate_costs ), fanout_cc( splitters ) {} + + template + double operator()( const Ntk& ntk, const LevelMap& level_of_node, const PoLevelMap& po_level_of_node ) + { + /* Collect fanouts of each node. + Cannot use the fanout_view as duplicate fanis are not accounted with the correct multiplicity. */ + std::unordered_map, std::vector>> fanouts; + ntk.foreach_gate( [&]( auto n ) { ntk.foreach_fanin( n, [&]( auto fi ) { fanouts[ntk.get_node( fi )].push_back( n ); } ); } ); + + auto gate_cost = 0.0; + auto fanout_net_cost = 0.0; + + std::vector> nodes; + if ( assume.branch_pis ) + { + ntk.foreach_ci( [&]( auto n ) { nodes.push_back( n ); } ); + } + ntk.foreach_gate( [&]( auto n ) { nodes.push_back( n ); } ); + + if ( po_level_of_node.size() == 0 ) + { + std::cout << "[w] - the map po_level_of_node is empty!\n"; + } + + size_t critical_po_level = std::max_element( po_level_of_node.begin(), po_level_of_node.end(), []( auto n1, auto n2 ) { return n1.second < n2.second; } ) + ->second; + for ( auto n : nodes ) + { + if ( !ntk.is_ci( n ) ) // n must be a gate + { + gate_cost += gate_costs.at( ntk.fanin_size( n ) ); + } + + if ( ntk.fanout_size( n ) == 0 ) + { + std::cout << fmt::format( "[w] - dangling node {}\n", n ); + continue; + } + + std::vector rellev; + + for ( auto fo : fanouts[n] ) + { + assert( level_of_node.at( fo ) > level_of_node.at( n ) ); + rellev.push_back( level_of_node.at( fo ) - level_of_node.at( n ) ); + } + + uint32_t pos = 0u; + while ( rellev.size() < ntk.fanout_size( n ) ) + { + pos++; + if ( assume.balance_pos ) + { + rellev.push_back( critical_po_level + 1 - level_of_node.at( n ) ); + } + else + { + rellev.push_back( po_level_of_node.at( n ) + 1 - level_of_node.at( n ) ); + } + } + + if ( rellev.size() > 1u || ( rellev.size() == 1u && rellev[0] > 0 ) ) + { + std::stable_sort( rellev.begin(), rellev.end() ); + auto net_cost = fanout_cc( rellev, ntk.is_ci( n ) && !assume.balance_pis ); + if ( net_cost == std::numeric_limits::infinity() ) + { + std::cerr << fmt::format( "[e] impossible to synthesize fanout net of node {} for relative levels [{}]\n", n, fmt::join( rellev, " " ) ); + std::abort(); + } + fanout_net_cost += net_cost; + } + else + { + std::cerr << fmt::format( "[e] invalid level assignment for node {} with levels [{}]\n", n, fmt::join( rellev, " " ) ); + std::abort(); + } + } + + return gate_cost + fanout_net_cost; + } + +private: + aqfp_assumptions assume; + std::unordered_map gate_costs; + fanout_net_cost fanout_cc; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/properties/litcost.hpp b/include/mockturtle/properties/litcost.hpp new file mode 100644 index 0000000..5e2fad3 --- /dev/null +++ b/include/mockturtle/properties/litcost.hpp @@ -0,0 +1,270 @@ +/* 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 litcost.hpp + \brief Cost function based on the factored literal cost + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "../utils/sop_utils.hpp" + +namespace mockturtle +{ + +namespace detail +{ +uint32_t count_literals_rec( std::vector&, uint32_t const ); + +uint32_t count_term_literals( uint64_t const term, uint32_t const num_lit ) +{ + uint32_t lit = 0; + + for ( auto i = 0u; i < num_lit; ++i ) + { + if ( cube_has_lit( term, i ) ) + ++lit; + } + + return lit; +} + +uint32_t lit_factor_count_rec( std::vector const& sop, uint64_t const c_sop, uint32_t const num_lit ) +{ + using sop_t = std::vector; + + sop_t divisor, quotient, reminder; + + /* extract the best literal */ + sop_best_literal( sop, divisor, c_sop, num_lit ); + + /* divide SOP by the literal */ + sop_divide_by_cube( sop, divisor, quotient, reminder ); + + /* count literals in the divisor: cube */ + uint32_t div_lit = count_term_literals( divisor[0], num_lit ); + + /* factor the quotient */ + uint32_t quot_lit = count_literals_rec( quotient, num_lit ); + + /* factor the reminder */ + if ( reminder.size() != 0 ) + { + return div_lit + quot_lit + count_literals_rec( reminder, num_lit ); + } + + return div_lit + quot_lit; +} + +uint32_t count_literals_rec( std::vector& sop, uint32_t const num_lit ) +{ + using sop_t = std::vector; + + assert( sop.size() ); + + sop_t divisor, quotient, reminder; + + /* compute the divisor */ + if ( !sop_quick_divisor( sop, divisor, num_lit ) ) + { + /* count_literals of the current SOP */ + return sop_count_literals( sop ); + } + + /* divide the SOP by the divisor */ + sop_divide( sop, divisor, quotient, reminder ); + + assert( quotient.size() > 0 ); + + if ( quotient.size() == 1 ) + { + return lit_factor_count_rec( sop, quotient[0], num_lit ); + } + + sop_make_cube_free( quotient ); + + /* divide the SOP by the quotient */ + sop_divide( sop, quotient, divisor, reminder ); + + if ( sop_is_cube_free( divisor ) ) + { + uint32_t div_lit = count_literals_rec( divisor, num_lit ); + uint32_t quot_lit = count_literals_rec( quotient, num_lit ); + + if ( reminder.size() ) + { + return div_lit + quot_lit + count_literals_rec( reminder, num_lit ); + } + + return div_lit + quot_lit; + } + + /* get the common cube */ + uint64_t cube = UINT64_MAX; + for ( auto const& c : divisor ) + { + cube &= c; + } + + return lit_factor_count_rec( sop, cube, num_lit ); +} + +} // namespace detail + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of the SOP and + * returns its number of factored form literals. + * + * \param sop Sum-of-products + * \param num_vars Number of variables + */ +uint32_t factored_literal_cost( std::vector const& sop, uint32_t num_vars ) +{ + /* trivial cases: constant 0 or 1 */ + if ( sop.size() == 0 || sop.size() == 1 && sop[0]._mask == 0 ) + return 0; + + using sop_t = std::vector; + sop_t lit_sop = cubes_to_sop( sop, num_vars ); + + return detail::count_literals_rec( lit_sop, num_vars * 2 ); +} + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of a completely specified + * function given as a truth table and returns its number of + * factored form literals. + * + * \param tt function as truth table + * \param try_both_polarities factoring is also tried for the negated TT + */ +uint32_t factored_literal_cost( kitty::dynamic_truth_table const& tt, bool try_both_polarities = false ) +{ + if ( kitty::is_const0( tt ) || kitty::is_const0( ~tt ) ) + { + /* constant */ + return 0; + } + + std::vector cubes = kitty::isop( tt ); + + if ( try_both_polarities ) + { + std::vector n_cubes = kitty::isop( ~tt ); + + if ( n_cubes.size() < cubes.size() ) + { + cubes = n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + cubes = n_cubes; + } + } + } + + return factored_literal_cost( cubes, tt.num_vars() ); +} + +/*! \brief Counts number of literals of the factored form of a SOP. + * + * This method computes the factored form of an incompletely specified + * function given as a truth table and its don't care set and returns + * its number of factored form literals. + * + * \param tt function as truth table + * \param dc don't care set + * \param try_both_polarities factoring is also tried for the negated TT + */ +uint32_t factored_literal_cost( kitty::dynamic_truth_table const& tt, kitty::dynamic_truth_table const& dc, bool try_both_polarities = false ) +{ + if ( kitty::is_const0( tt & ( ~dc ) ) || kitty::is_const0( ~( tt | dc ) ) ) + { + /* constant */ + return 0; + } + + std::vector cubes; + kitty::detail::isop_rec( tt & ~dc, tt | dc, tt.num_vars(), cubes ); + + if ( try_both_polarities ) + { + std::vector n_cubes; + kitty::detail::isop_rec( ~tt & ~dc, ~tt | dc, tt.num_vars(), n_cubes ); + + if ( n_cubes.size() < cubes.size() ) + { + cubes = n_cubes; + } + else if ( n_cubes.size() == cubes.size() ) + { + uint32_t n_lit = 0; + uint32_t lit = 0; + for ( auto const& c : n_cubes ) + { + n_lit += c.num_literals(); + } + for ( auto const& c : cubes ) + { + lit += c.num_literals(); + } + + if ( n_lit < lit ) + { + cubes = n_cubes; + } + } + } + + return factored_literal_cost( cubes, tt.num_vars() ); +} + +} // namespace mockturtle diff --git a/include/mockturtle/properties/mccost.hpp b/include/mockturtle/properties/mccost.hpp new file mode 100644 index 0000000..59622c6 --- /dev/null +++ b/include/mockturtle/properties/mccost.hpp @@ -0,0 +1,301 @@ +/* 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 mccost.hpp + \brief Cost functions based on multiplicative-complexity + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Computes the multiplicative complexity + * + * Computes and sums the multiplicative complexity of each gate in the network. + * Returns `std::nullopt`, if multiplicative complexity cannot be determined + * for some gate. + * + * \param ntk Network + */ +template +std::optional multiplicative_complexity( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + uint32_t total{ 0u }; + bool valid{ true }; + + ntk.foreach_gate( [&]( auto const& n ) { + if constexpr ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + return true; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( ntk.is_ite( n ) ) + { + total++; + return true; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + return true; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + if ( ntk.fanin_size( n ) > 1u ) + { + total += ntk.fanin_size( n ) - 1u; + } + return true; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + if ( ntk.fanin_size( n ) > 1u ) + { + total += ntk.fanin_size( n ) - 1u; + } + return true; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + return true; + } + } + + valid = false; + return false; /* break */ + } ); + + if ( valid ) + { + return total; + } + else + { + return std::nullopt; + } +} + +/*! \brief Computes the multiplicative complexity depth + * + * Computes multiplicative complexity of each gate and the sum of them on the + * critical path in the network. Returns `std::nullopt`, if multiplicative + * complexity cannot be determined for some gate. + * + * \param ntk Network + */ +template +std::optional multiplicative_complexity_depth( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po 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" ); + + bool valid{ true }; + + node_map level( ntk, 0u ); + topo_view topo{ ntk }; + + topo.foreach_node( [&]( auto const& n ) { + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + { + return true; + } + + /* compute the maximum MC of children */ + uint32_t max_level{ 0u }; + ntk.foreach_fanin( n, [&]( const auto& f ) { + if ( level[f] > max_level ) + { + max_level = level[f]; + } + } ); + + if ( has_is_and_v ) + { + if ( ntk.is_and( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_or_v ) + { + if ( ntk.is_or( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_xor_v ) + { + if ( ntk.is_xor( n ) ) + { + level[n] = max_level; + return true; + } + } + + if ( has_is_maj_v ) + { + if ( ntk.is_maj( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_ite_v ) + { + if ( ntk.is_ite( n ) ) + { + level[n] = max_level + 1u; + return true; + } + } + + if ( has_is_xor3_v ) + { + if ( ntk.is_xor3( n ) ) + { + level[n] = max_level; + return true; + } + } + + if ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( n ) ) + { + level[n] = max_level + static_cast( std::ceil( std::log2( ntk.fanin_size( n ) ) ) ); + return true; + } + } + + if ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( n ) ) + { + level[n] = max_level + static_cast( std::ceil( std::log2( ntk.fanin_size( n ) ) ) ); + return true; + } + } + + if ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( n ) ) + { + level[n] = max_level; + return true; + } + } + + valid = false; + return false; /* break */ + } ); + + if ( valid ) + { + uint32_t max_level{ 0u }; + ntk.foreach_po( [&]( const auto& f ) { + if ( level[f] > max_level ) + { + max_level = level[f]; + } + } ); + return max_level; + } + else + { + return std::nullopt; + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/properties/migcost.hpp b/include/mockturtle/properties/migcost.hpp new file mode 100644 index 0000000..82342b1 --- /dev/null +++ b/include/mockturtle/properties/migcost.hpp @@ -0,0 +1,117 @@ +/* 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 migcost.hpp + \brief Cost functions for majority-based technologies + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Counts number of inverters. + * + * This number counts all nodes that need to be inverted. Multiple signals + * with complements to the same node are counted once. + * + * \param ntk Network + */ +template +uint32_t num_inverters( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po 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" ); + + std::unordered_set> inverted_nodes; + + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + inverted_nodes.insert( ntk.get_node( f ) ); + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + inverted_nodes.insert( ntk.get_node( f ) ); + } + } ); + + return static_cast( inverted_nodes.size() ); +} + +/*! \brief Counts fanins which are primary inputs. + * + * \param ntk Network + */ +template +uint32_t num_dangling_inputs( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + + uint32_t costs{ 0u }; + + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_pi( ntk.get_node( f ) ) ) + { + costs++; + } + } ); + } ); + + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_pi( ntk.get_node( f ) ) ) + { + costs++; + } + } ); + + return costs; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/properties/xmgcost.hpp b/include/mockturtle/properties/xmgcost.hpp new file mode 100644 index 0000000..a6a4409 --- /dev/null +++ b/include/mockturtle/properties/xmgcost.hpp @@ -0,0 +1,129 @@ +/* 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 xmgcost.hpp + \brief Cost functions for xmg-based networks + + \author Heinz Riener + \author Shubham Rai +*/ + +#pragma once + +#include "../traits.hpp" + +#include + +namespace mockturtle +{ + +struct xmg_gate_stats +{ + /*! \brief Total number of XOR3 gates (structurally). */ + uint32_t total_xor3{ 0 }; + + /*! \brief Number of XOR3 (functionally). */ + uint32_t xor3{ 0 }; + + /*! \brief Number of XOR2 (functionally). */ + uint32_t xor2{ 0 }; + + /*! \brief Total number of MAJ gates (structurally). */ + uint32_t total_maj{ 0 }; + + /*! \brief Number of MAJ gates. */ + uint32_t maj{ 0 }; + + /*! \brief Number of AND/OR gates. */ + uint32_t and_or{ 0 }; + + void report() const + { + fmt::print( "XOR3: {} = {} XOR3 + {} XOR2 / MAJ: {} = {} MAJ3 + {} AND/OR\n", + total_xor3, xor2, xor3, total_maj, maj, and_or ); + } +}; + +/*! \brief Profile gates + * + * Counts the numbers of MAJ and XOR nodes in an XMG. + * + * \param ntk Network + * \param stats Statistics + */ +template +void xmg_profile_gates( Ntk const& ntk, xmg_gate_stats& stats ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_is_xor3_v, "Ntk does not implement the is_xor3 method" ); + + ntk.foreach_gate( [&]( auto const& node ) { + bool has_const_fanin = false; + + /* Check if all of the fanin nodes are not constant */ + ntk.foreach_fanin( node, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + has_const_fanin = true; + return false; + } + return true; + } ); + + if ( ntk.is_maj( node ) ) + { + if ( has_const_fanin ) + { + ++stats.and_or; + } + else + { + ++stats.maj; + } + } + else if ( ntk.is_xor3( node ) ) + { + if ( has_const_fanin ) + { + ++stats.xor2; + } + else + { + ++stats.xor3; + } + } + } ); + + stats.total_xor3 = stats.xor2 + stats.xor3; + stats.total_maj = stats.and_or + stats.maj; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/traits.hpp b/include/mockturtle/traits.hpp new file mode 100644 index 0000000..2808733 --- /dev/null +++ b/include/mockturtle/traits.hpp @@ -0,0 +1,2674 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file traits.hpp + \brief Type traits and checkers for the network interface + + \author Alessandro Tempia Calvino + \author Andrea Costamagna + \author Bruno Schmitt + \author Hanyu Wang + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Max Austin + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +template +using signal = typename Ntk::signal; + +template +using node = typename Ntk::node; + +template +struct is_network_type : std::false_type +{ +}; + +template +struct is_network_type, node>, + std::void_t, + node, + typename Ntk::storage, + decltype( Ntk::max_fanin_size ), + decltype( Ntk::min_fanin_size )>>> : std::true_type +{ +}; + +template +inline constexpr bool is_network_type_v = is_network_type::value; + +#pragma region is_aig_network_type +template +struct is_aig_network_type : std::false_type +{ +}; + +template +struct is_aig_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_aig_network_type_v = is_aig_network_type::value; +#pragma endregion + +#pragma region is_buffered_network_type +template +struct is_buffered_network_type : std::false_type +{ +}; + +template +struct is_buffered_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_buffered_network_type_v = is_buffered_network_type::value; +#pragma endregion + +#pragma region is_crossed_network_type +template +struct is_crossed_network_type : std::false_type +{ +}; + +template +struct is_crossed_network_type>> : std::true_type +{ +}; + +template +inline constexpr bool is_crossed_network_type_v = is_crossed_network_type::value; +#pragma endregion + +#pragma region has_clone +template +struct has_clone : std::false_type +{ +}; + +template +struct has_clone().clone() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_v = has_clone::value; +#pragma endregion + +#pragma region is_topologically_sorted +template +struct is_topologically_sorted : std::false_type +{ +}; + +template +struct is_topologically_sorted>> : std::true_type +{ +}; + +template +inline constexpr bool is_topologically_sorted_v = is_topologically_sorted::value; +#pragma endregion + +#pragma region has_get_constant +template +struct has_get_constant : std::false_type +{ +}; + +template +struct has_get_constant().get_constant( bool() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_constant_v = has_get_constant::value; +#pragma endregion + +#pragma region has_create_pi +template +struct has_create_pi : std::false_type +{ +}; + +template +struct has_create_pi().create_pi() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_pi_v = has_create_pi::value; +#pragma endregion + +#pragma region has_create_po +template +struct has_create_po : std::false_type +{ +}; + +template +struct has_create_po().create_po( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_po_v = has_create_po::value; +#pragma endregion + +#pragma region has_create_ro +template +struct has_create_ro : std::false_type +{ +}; + +template +struct has_create_ro().create_ro() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ro_v = has_create_ro::value; +#pragma endregion + +#pragma region has_create_ri +template +struct has_create_ri : std::false_type +{ +}; + +template +struct has_create_ri().create_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ri_v = has_create_ri::value; +#pragma endregion + +#pragma region has_is_combinational +template +struct has_is_combinational : std::false_type +{ +}; + +template +struct has_is_combinational().is_combinational() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_combinational_v = has_is_combinational::value; +#pragma endregion + +#pragma region has_is_constant +template +struct has_is_constant : std::false_type +{ +}; + +template +struct has_is_constant().is_constant( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_constant_v = has_is_constant::value; +#pragma endregion + +#pragma region has_is_ci +template +struct has_is_ci : std::false_type +{ +}; + +template +struct has_is_ci().is_ci( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ci_v = has_is_ci::value; +#pragma endregion + +#pragma region has_is_pi +template +struct has_is_pi : std::false_type +{ +}; + +template +struct has_is_pi().is_pi( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_pi_v = has_is_pi::value; +#pragma endregion + +#pragma region has_is_ro +template +struct has_is_ro : std::false_type +{ +}; + +template +struct has_is_ro().is_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ro_v = has_is_ro::value; +#pragma endregion + +#pragma region has_is_ro +template +struct has_is_multioutput : std::false_type +{ +}; + +template +struct has_is_multioutput().is_multioutput( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_multioutput_v = has_is_multioutput::value; +#pragma endregion + +#pragma region has_constant_value +template +struct has_constant_value : std::false_type +{ +}; + +template +struct has_constant_value().constant_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_constant_value_v = has_constant_value::value; +#pragma endregion + +#pragma region has_create_buf +template +struct has_create_buf : std::false_type +{ +}; + +template +struct has_create_buf().create_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_buf_v = has_create_buf::value; +#pragma endregion + +#pragma region has_create_not +template +struct has_create_not : std::false_type +{ +}; + +template +struct has_create_not().create_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_not_v = has_create_not::value; +#pragma endregion + +#pragma region has_create_and +template +struct has_create_and : std::false_type +{ +}; + +template +struct has_create_and().create_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_and_v = has_create_and::value; +#pragma endregion + +#pragma region has_create_nand +template +struct has_create_nand : std::false_type +{ +}; + +template +struct has_create_nand().create_nand( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nand_v = has_create_nand::value; +#pragma endregion + +#pragma region has_create_or +template +struct has_create_or : std::false_type +{ +}; + +template +struct has_create_or().create_or( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_or_v = has_create_or::value; +#pragma endregion + +#pragma region has_create_nor +template +struct has_create_nor : std::false_type +{ +}; + +template +struct has_create_nor().create_nor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nor_v = has_create_nor::value; +#pragma endregion + +#pragma region has_create_lt +template +struct has_create_lt : std::false_type +{ +}; + +template +struct has_create_lt().create_lt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_lt_v = has_create_lt::value; +#pragma endregion + +#pragma region has_create_le +template +struct has_create_le : std::false_type +{ +}; + +template +struct has_create_le().create_le( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_le_v = has_create_le::value; +#pragma endregion + +#pragma region has_create_gt +template +struct has_create_gt : std::false_type +{ +}; + +template +struct has_create_gt().create_gt( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_gt_v = has_create_gt::value; +#pragma endregion + +#pragma region has_create_ge +template +struct has_create_ge : std::false_type +{ +}; + +template +struct has_create_ge().create_ge( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ge_v = has_create_ge::value; +#pragma endregion + +#pragma region has_create_xor +template +struct has_create_xor : std::false_type +{ +}; + +template +struct has_create_xor().create_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor_v = has_create_xor::value; +#pragma endregion + +#pragma region has_create_xnor +template +struct has_create_xnor : std::false_type +{ +}; + +template +struct has_create_xnor().create_xnor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xnor_v = has_create_xnor::value; +#pragma endregion + +#pragma region has_create_maj +template +struct has_create_maj : std::false_type +{ +}; + +template +struct has_create_maj().create_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_v = has_create_maj::value; +#pragma endregion + +#pragma region has_create_maj_odd +template +struct has_create_maj_odd : std::false_type +{ +}; + +template +struct has_create_maj_odd().create_maj( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_maj_odd_v = has_create_maj_odd::value; +#pragma endregion + +#pragma region has_create_ite +template +struct has_create_ite : std::false_type +{ +}; + +template +struct has_create_ite().create_ite( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_ite_v = has_create_ite::value; +#pragma endregion + +#pragma region has_create_xor3 +template +struct has_create_xor3 : std::false_type +{ +}; + +template +struct has_create_xor3().create_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_xor3_v = has_create_xor3::value; +#pragma endregion + +#pragma region has_create_nary_and +template +struct has_create_nary_and : std::false_type +{ +}; + +template +struct has_create_nary_and().create_nary_and( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_and_v = has_create_nary_and::value; +#pragma endregion + +#pragma region has_create_nary_or +template +struct has_create_nary_or : std::false_type +{ +}; + +template +struct has_create_nary_or().create_nary_or( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_or_v = has_create_nary_or::value; +#pragma endregion + +#pragma region has_create_nary_xor +template +struct has_create_nary_xor : std::false_type +{ +}; + +template +struct has_create_nary_xor().create_nary_xor( std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_nary_xor_v = has_create_nary_xor::value; +#pragma endregion + +#pragma region has_create_node +template +struct has_create_node : std::false_type +{ +}; + +template +struct has_create_node().create_node( std::declval>>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_node_v = has_create_node::value; +#pragma endregion + +#pragma region has_create_cover_node +template +struct has_create_cover_node : std::false_type +{ +}; + +template +struct has_create_cover_node().create_cover_node( std::declval>>(), std::declval, bool>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_cover_node_v = has_create_cover_node::value; +#pragma endregion + +#pragma region has_create_crossing +template +struct has_create_crossing : std::false_type +{ +}; + +template +struct has_create_crossing().create_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_create_crossing_v = has_create_crossing::value; +#pragma endregion + +#pragma region has_insert_crossing +template +struct has_insert_crossing : std::false_type +{ +}; + +template +struct has_insert_crossing().insert_crossing( std::declval>(), std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_insert_crossing_v = has_insert_crossing::value; +#pragma endregion + +#pragma region has_merge_into_crossing +template +struct has_merge_into_crossing : std::false_type +{ +}; + +template +struct has_merge_into_crossing().merge_into_crossing( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_merge_into_crossing_v = has_merge_into_crossing::value; +#pragma endregion + +#pragma region has_clone_node +template +struct has_clone_node : std::false_type +{ +}; + +template +struct has_clone_node().clone_node( std::declval(), std::declval>(), std::declval>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clone_node_v = has_clone_node::value; +#pragma endregion + +#pragma region has_has_and +template +struct has_has_and : std::false_type +{ +}; + +template +struct has_has_and().has_and( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_and_v = has_has_and::value; +#pragma endregion + +#pragma region has_has_xor +template +struct has_has_xor : std::false_type +{ +}; + +template +struct has_has_xor().has_xor( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor_v = has_has_xor::value; +#pragma endregion + +#pragma region has_has_maj +template +struct has_has_maj : std::false_type +{ +}; + +template +struct has_has_maj().has_maj( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_maj_v = has_has_maj::value; +#pragma endregion + +#pragma region has_has_xor3 +template +struct has_has_xor3 : std::false_type +{ +}; + +template +struct has_has_xor3().has_xor3( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_xor3_v = has_has_xor3::value; +#pragma endregion + +#pragma region has_substitute_node +template +struct has_substitute_node : std::false_type +{ +}; + +template +struct has_substitute_node().substitute_node( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_node_v = has_substitute_node::value; +#pragma endregion + +#pragma region has_substitute_nodes +template +struct has_substitute_nodes : std::false_type +{ +}; + +template +struct has_substitute_nodes().substitute_nodes( std::declval, signal>>>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_substitute_nodes_v = has_substitute_nodes::value; +#pragma endregion + +#pragma region has_replace_in_node +template +struct has_replace_in_node : std::false_type +{ +}; + +template +struct has_replace_in_node().replace_in_node( std::declval>(), std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_node_v = has_replace_in_node::value; +#pragma endregion + +#pragma region has_replace_in_outputs +template +struct has_replace_in_outputs : std::false_type +{ +}; + +template +struct has_replace_in_outputs().replace_in_outputs( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_replace_in_outputs_v = has_replace_in_outputs::value; +#pragma endregion + +#pragma region has_take_out_node +template +struct has_take_out_node : std::false_type +{ +}; + +template +struct has_take_out_node().take_out_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_take_out_node_v = has_take_out_node::value; +#pragma endregion + +#pragma region is_dead +template +struct has_is_dead : std::false_type +{ +}; + +template +struct has_is_dead().is_dead( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dead_v = has_is_dead::value; +#pragma endregion + +#pragma region has_size +template +struct has_size : std::false_type +{ +}; + +template +struct has_size().size() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_size_v = has_size::value; +#pragma endregion + +#pragma region has_num_cis +template +struct has_num_cis : std::false_type +{ +}; + +template +struct has_num_cis().num_cis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cis_v = has_num_cis::value; +#pragma endregion + +#pragma region has_num_cos +template +struct has_num_cos : std::false_type +{ +}; + +template +struct has_num_cos().num_cos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cos_v = has_num_cos::value; +#pragma endregion + +#pragma region has_num_pis +template +struct has_num_pis : std::false_type +{ +}; + +template +struct has_num_pis().num_pis() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pis_v = has_num_pis::value; +#pragma endregion + +#pragma region has_num_pos +template +struct has_num_pos : std::false_type +{ +}; + +template +struct has_num_pos().num_pos() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_pos_v = has_num_pos::value; +#pragma endregion + +#pragma region has_num_gates +template +struct has_num_gates : std::false_type +{ +}; + +template +struct has_num_gates().num_gates() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_gates_v = has_num_gates::value; +#pragma endregion + +#pragma region has_num_registers +template +struct has_num_registers : std::false_type +{ +}; + +template +struct has_num_registers().num_registers() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_registers_v = has_num_registers::value; +#pragma endregion + +#pragma region has_fanin_size +template +struct has_fanin_size : std::false_type +{ +}; + +template +struct has_fanin_size().fanin_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanin_size_v = has_fanin_size::value; +#pragma endregion + +#pragma region has_num_outputs +template +struct has_num_outputs : std::false_type +{ +}; + +template +struct has_num_outputs().num_outputs( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_outputs_v = has_num_outputs::value; +#pragma endregion + +#pragma region has_fanout_size +template +struct has_fanout_size : std::false_type +{ +}; + +template +struct has_fanout_size().fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_fanout_size_v = has_fanout_size::value; +#pragma endregion + +#pragma region has_incr_fanout_size +template +struct has_incr_fanout_size : std::false_type +{ +}; + +template +struct has_incr_fanout_size().incr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_fanout_size_v = has_incr_fanout_size::value; +#pragma endregion + +#pragma region has_decr_fanout_size +template +struct has_decr_fanout_size : std::false_type +{ +}; + +template +struct has_decr_fanout_size().decr_fanout_size( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_fanout_size_v = has_decr_fanout_size::value; +#pragma endregion + +#pragma region has_cost +template +struct has_cost : std::false_type +{ +}; + +template +struct has_cost().get_cost() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cost_v = has_cost::value; +#pragma endregion + +#pragma region has_depth +template +struct has_depth : std::false_type +{ +}; + +template +struct has_depth().depth() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_depth_v = has_depth::value; +#pragma endregion + +#pragma region has_level +template +struct has_level : std::false_type +{ +}; + +template +struct has_level().level( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_level_v = has_level::value; +#pragma endregion + +#pragma region has_update_levels +template +struct has_update_levels : std::false_type +{ +}; + +template +struct has_update_levels().update_levels() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_levels_v = has_update_levels::value; +#pragma endregion + +#pragma region has_rank_position +template +struct has_rank_position : std::false_type +{ +}; + +template +struct has_rank_position().rank_position( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_rank_position_v = has_rank_position::value; +#pragma endregion + +#pragma region has_at_rank_position +template +struct has_at_rank_position : std::false_type +{ +}; + +template +struct has_at_rank_position().at_rank_position( std::declval(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_at_rank_position_v = has_at_rank_position::value; +#pragma endregion + +#pragma region has_width +template +struct has_width : std::false_type +{ +}; + +template +struct has_width().width() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_width_v = has_width::value; +#pragma endregion + +#pragma region has_swap +template +struct has_swap : std::false_type +{ +}; + +template +struct has_swap().swap( std::declval>(), std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_swap_v = has_swap::value; +#pragma endregion + +#pragma region has_sort_rank +template +struct has_sort_rank : std::false_type +{ +}; + +template +struct has_sort_rank().sort_rank( std::declval(), std::declval, node )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_sort_rank_v = has_sort_rank::value; +#pragma endregion + +#pragma region has_foreach_node_in_rank +template +struct has_foreach_node_in_rank : std::false_type +{ +}; + +template +struct has_foreach_node_in_rank().foreach_node_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_in_rank_v = has_foreach_node_in_rank::value; +#pragma endregion + +#pragma region has_foreach_gate_in_rank +template +struct has_foreach_gate_in_rank : std::false_type +{ +}; + +template +struct has_foreach_gate_in_rank().foreach_gate_in_rank( std::declval(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_in_rank_v = has_foreach_gate_in_rank::value; +#pragma endregion + +#pragma region has_update_mffcs +template +struct has_update_mffcs : std::false_type +{ +}; + +template +struct has_update_mffcs().update_mffcs() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_mffcs_v = has_update_mffcs::value; +#pragma endregion + +#pragma region has_update_topo +template +struct has_update_topo : std::false_type +{ +}; + +template +struct has_update_topo().update_topo() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_topo_v = has_update_topo::value; +#pragma endregion + +#pragma region has_update_fanout +template +struct has_update_fanout : std::false_type +{ +}; + +template +struct has_update_fanout().update_fanout() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_update_fanout_v = has_update_fanout::value; +#pragma endregion + +#pragma region has_is_on_critical_path +template +struct has_is_on_critical_path : std::false_type +{ +}; + +template +struct has_is_on_critical_path().is_on_critical_path( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_on_critical_path_v = has_is_on_critical_path::value; +#pragma endregion + +#pragma region has_is_buf +template +struct has_is_buf : std::false_type +{ +}; + +template +struct has_is_buf().is_buf( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_buf_v = has_is_buf::value; +#pragma endregion + +#pragma region has_is_not +template +struct has_is_not : std::false_type +{ +}; + +template +struct has_is_not().is_not( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_not_v = has_is_not::value; +#pragma endregion + +#pragma region has_is_crossing +template +struct has_is_crossing : std::false_type +{ +}; + +template +struct has_is_crossing().is_crossing( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_crossing_v = has_is_crossing::value; +#pragma endregion + +#pragma region has_is_and +template +struct has_is_and : std::false_type +{ +}; + +template +struct has_is_and().is_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_and_v = has_is_and::value; +#pragma endregion + +#pragma region has_is_or +template +struct has_is_or : std::false_type +{ +}; + +template +struct has_is_or().is_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_or_v = has_is_or::value; +#pragma endregion + +#pragma region has_is_xor +template +struct has_is_xor : std::false_type +{ +}; + +template +struct has_is_xor().is_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor_v = has_is_xor::value; +#pragma endregion + +#pragma region has_is_maj +template +struct has_is_maj : std::false_type +{ +}; + +template +struct has_is_maj().is_maj( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_maj_v = has_is_maj::value; +#pragma endregion + +#pragma region has_is_ite +template +struct has_is_ite : std::false_type +{ +}; + +template +struct has_is_ite().is_ite( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_ite_v = has_is_ite::value; +#pragma endregion + +#pragma region has_is_xor3 +template +struct has_is_xor3 : std::false_type +{ +}; + +template +struct has_is_xor3().is_xor3( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_xor3_v = has_is_xor3::value; +#pragma endregion + +#pragma region has_is_nary_and +template +struct has_is_nary_and : std::false_type +{ +}; + +template +struct has_is_nary_and().is_nary_and( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_and_v = has_is_nary_and::value; +#pragma endregion + +#pragma region has_is_nary_or +template +struct has_is_nary_or : std::false_type +{ +}; + +template +struct has_is_nary_or().is_nary_or( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_or_v = has_is_nary_or::value; +#pragma endregion + +#pragma region has_is_nary_xor +template +struct has_is_nary_xor : std::false_type +{ +}; + +template +struct has_is_nary_xor().is_nary_xor( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_nary_xor_v = has_is_nary_xor::value; +#pragma endregion + +#pragma region has_is_function +template +struct has_is_function : std::false_type +{ +}; + +template +struct has_is_function().is_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_function_v = has_is_function::value; +#pragma endregion + +#pragma region has_node_function +template +struct has_node_function : std::false_type +{ +}; + +template +struct has_node_function().node_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_v = has_node_function::value; +#pragma endregion + +#pragma region has_node_function_pin +template +struct has_node_function_pin : std::false_type +{ +}; + +template +struct has_node_function_pin().node_function_pin( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_function_pin_v = has_node_function_pin::value; +#pragma endregion + +#pragma region has_get_node +template +struct has_get_node : std::false_type +{ +}; + +template +struct has_get_node().get_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_node_v = has_get_node::value; +#pragma endregion + +#pragma region has_make_signal +template +struct has_make_signal : std::false_type +{ +}; + +template +struct has_make_signal().make_signal( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_make_signal_v = has_make_signal::value; +#pragma endregion + +#pragma region has_get_output_pin +template +struct has_get_output_pin : std::false_type +{ +}; + +template +struct has_get_output_pin().get_output_pin( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_pin_v = has_get_output_pin::value; +#pragma endregion + +#pragma region has_is_complemented +template +struct has_is_complemented : std::false_type +{ +}; + +template +struct has_is_complemented().is_complemented( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_complemented_v = has_is_complemented::value; +#pragma endregion + +#pragma region has_node_to_index +template +struct has_node_to_index : std::false_type +{ +}; + +template +struct has_node_to_index().node_to_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_node_to_index_v = has_node_to_index::value; +#pragma endregion + +#pragma region has_index_to_node +template +struct has_index_to_node : std::false_type +{ +}; + +template +struct has_index_to_node().index_to_node( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_index_to_node_v = has_index_to_node::value; +#pragma endregion + +#pragma region has_ci_at +template +struct has_ci_at : std::false_type +{ +}; + +template +struct has_ci_at().ci_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ci_at_v = has_ci_at::value; +#pragma endregion + +#pragma region has_co_at +template +struct has_co_at : std::false_type +{ +}; + +template +struct has_co_at().co_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_co_at_v = has_co_at::value; +#pragma endregion + +#pragma region has_pi_at +template +struct has_pi_at : std::false_type +{ +}; + +template +struct has_pi_at().pi_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_pi_at_v = has_pi_at::value; +#pragma endregion + +#pragma region has_po_at +template +struct has_po_at : std::false_type +{ +}; + +template +struct has_po_at().po_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_po_at_v = has_po_at::value; +#pragma endregion + +#pragma region has_ro_at +template +struct has_ro_at : std::false_type +{ +}; + +template +struct has_ro_at().ro_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_at_v = has_ro_at::value; +#pragma endregion + +#pragma region has_ri_at +template +struct has_ri_at : std::false_type +{ +}; + +template +struct has_ri_at().ri_at( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_at_v = has_ri_at::value; +#pragma endregion + +#pragma region ci_index +template +struct ci_index : std::false_type +{ +}; + +template +struct ci_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ci_index_v = ci_index::value; +#pragma endregion + +#pragma region co_index +template +struct co_index : std::false_type +{ +}; + +template +struct co_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool co_index_v = co_index::value; +#pragma endregion + +#pragma region pi_index +template +struct pi_index : std::false_type +{ +}; + +template +struct pi_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool pi_index_v = pi_index::value; +#pragma endregion + +#pragma region po_index +template +struct po_index : std::false_type +{ +}; + +template +struct po_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool po_index_v = po_index::value; +#pragma endregion + +#pragma region ro_index +template +struct ro_index : std::false_type +{ +}; + +template +struct ro_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ro_index_v = ro_index::value; +#pragma endregion + +#pragma region ri_index +template +struct ri_index : std::false_type +{ +}; + +template +struct ri_index().index_to_node( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool ri_index_v = ri_index::value; +#pragma endregion + +#pragma region has_ro_to_ri +template +struct has_ro_to_ri : std::false_type +{ +}; + +template +struct has_ro_to_ri().ro_to_ri( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ro_to_ri_v = has_ro_to_ri::value; +#pragma endregion + +#pragma region has_ri_to_ro +template +struct has_ri_to_ro : std::false_type +{ +}; + +template +struct has_ri_to_ro().ri_to_ro( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_ri_to_ro_v = has_ri_to_ro::value; +#pragma endregion + +#pragma region has_foreach_node +template +struct has_foreach_node : std::false_type +{ +}; + +template +struct has_foreach_node().foreach_node( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_node_v = has_foreach_node::value; +#pragma endregion + +#pragma region has_foreach_ci +template +struct has_foreach_ci : std::false_type +{ +}; + +template +struct has_foreach_ci().foreach_ci( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ci_v = has_foreach_ci::value; +#pragma endregion + +#pragma region has_foreach_co +template +struct has_foreach_co : std::false_type +{ +}; + +template +struct has_foreach_co().foreach_co( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_co_v = has_foreach_co::value; +#pragma endregion + +#pragma region has_foreach_pi +template +struct has_foreach_pi : std::false_type +{ +}; + +template +struct has_foreach_pi().foreach_pi( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_pi_v = has_foreach_pi::value; +#pragma endregion + +#pragma region has_foreach_po +template +struct has_foreach_po : std::false_type +{ +}; + +template +struct has_foreach_po().foreach_po( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_po_v = has_foreach_po::value; +#pragma endregion + +#pragma region has_foreach_ro +template +struct has_foreach_ro : std::false_type +{ +}; + +template +struct has_foreach_ro().foreach_ro( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ro_v = has_foreach_ro::value; +#pragma endregion + +#pragma region has_foreach_ri +template +struct has_foreach_ri : std::false_type +{ +}; + +template +struct has_foreach_ri().foreach_ri( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_ri_v = has_foreach_ri::value; +#pragma endregion + +#pragma region has_foreach_gate +template +struct has_foreach_gate : std::false_type +{ +}; + +template +struct has_foreach_gate().foreach_gate( std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_gate_v = has_foreach_gate::value; +#pragma endregion + +#pragma region has_foreach_register +template +struct has_foreach_register : std::false_type +{ +}; + +template +struct has_foreach_register().foreach_register( std::declval, signal>, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_register_v = has_foreach_register::value; +#pragma endregion + +#pragma region has_foreach_fanin +template +struct has_foreach_fanin : std::false_type +{ +}; + +template +struct has_foreach_fanin().foreach_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanin_v = has_foreach_fanin::value; +#pragma endregion + +#pragma region has_foreach_fanout +template +struct has_foreach_fanout : std::false_type +{ +}; + +template +struct has_foreach_fanout().foreach_fanout( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_fanout_v = has_foreach_fanout::value; +#pragma endregion + +#pragma region has_foreach_choice +template +struct has_foreach_choice : std::false_type +{ +}; + +template +struct has_foreach_choice().foreach_choice( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_choice_v = has_foreach_choice::value; +#pragma endregion + +#pragma region has_compute +template +struct has_compute : std::false_type +{ +}; + +template +struct has_compute().compute( std::declval>(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_v = has_compute::value; +#pragma endregion + +#pragma region has_compute_inplace +template +struct has_compute_inplace : std::false_type +{ +}; + +template +struct has_compute_inplace().compute( std::declval>(), std::declval(), std::begin( std::vector() ), std::end( std::vector() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_compute_inplace_v = has_compute_inplace::value; +#pragma endregion + +#pragma region has_has_mapping +template +struct has_has_mapping : std::false_type +{ +}; + +template +struct has_has_mapping().has_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_mapping_v = has_has_mapping::value; +#pragma endregion + +#pragma region has_is_cell_root +template +struct has_is_cell_root : std::false_type +{ +}; + +template +struct has_is_cell_root().is_cell_root( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_cell_root_v = has_is_cell_root::value; +#pragma endregion + +#pragma region has_clear_mapping +template +struct has_clear_mapping : std::false_type +{ +}; + +template +struct has_clear_mapping().clear_mapping() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_mapping_v = has_clear_mapping::value; +#pragma endregion + +#pragma region has_num_cells +template +struct has_num_cells : std::false_type +{ +}; + +template +struct has_num_cells().num_cells() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_num_cells_v = has_num_cells::value; +#pragma endregion + +#pragma region has_add_to_mapping +template +struct has_add_to_mapping : std::false_type +{ +}; + +template +struct has_add_to_mapping().add_to_mapping( std::declval>(), std::begin( std::vector>() ), std::end( std::vector>() ) ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_to_mapping_v = has_add_to_mapping::value; +#pragma endregion + +#pragma region has_remove_from_mapping +template +struct has_remove_from_mapping : std::false_type +{ +}; + +template +struct has_remove_from_mapping().remove_from_mapping( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_remove_from_mapping_v = has_remove_from_mapping::value; +#pragma endregion + +#pragma region has_cell_function +template +struct has_cell_function : std::false_type +{ +}; + +template +struct has_cell_function().cell_function( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_cell_function_v = has_cell_function::value; +#pragma endregion + +#pragma region has_set_cell_function +template +struct has_set_cell_function : std::false_type +{ +}; + +template +struct has_set_cell_function().set_cell_function( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_cell_function_v = has_set_cell_function::value; +#pragma endregion + +#pragma region has_foreach_cell_fanin +template +struct has_foreach_cell_fanin : std::false_type +{ +}; + +template +struct has_foreach_cell_fanin().foreach_cell_fanin( std::declval>(), std::declval, uint32_t )>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_foreach_cell_fanin_v = has_foreach_cell_fanin::value; +#pragma endregion + +#pragma region has_has_binding +template +struct has_has_binding : std::false_type +{ +}; + +template +struct has_has_binding().has_binding( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_binding_v = has_has_binding::value; +#pragma endregion + +#pragma region has_has_cell +template +struct has_has_cell : std::false_type +{ +}; + +template +struct has_has_cell().has_cell( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_cell_v = has_has_cell::value; +#pragma endregion + +#pragma region has_get_binding_index +template +struct has_get_binding_index : std::false_type +{ +}; + +template +struct has_get_binding_index().get_binding_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_binding_index_v = has_get_binding_index::value; +#pragma endregion + +#pragma region has_get_cell_index +template +struct has_get_cell_index : std::false_type +{ +}; + +template +struct has_get_cell_index().get_cell_index( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_cell_index_v = has_get_cell_index::value; +#pragma endregion + +#pragma region has_add_binding +template +struct has_add_binding : std::false_type +{ +}; + +template +struct has_add_binding().add_binding( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_add_binding_v = has_add_binding::value; +#pragma endregion + +#pragma region has_select_dont_touch +template +struct has_select_dont_touch : std::false_type +{ +}; + +template +struct has_select_dont_touch().select_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_select_dont_touch_v = has_select_dont_touch::value; +#pragma endregion + +#pragma region has_is_dont_touch +template +struct has_is_dont_touch : std::false_type +{ +}; + +template +struct has_is_dont_touch().is_dont_touch( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_is_dont_touch_v = has_is_dont_touch::value; +#pragma endregion + +#pragma region has_clear_values +template +struct has_clear_values : std::false_type +{ +}; + +template +struct has_clear_values().clear_values() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_values_v = has_clear_values::value; +#pragma endregion + +#pragma region has_value +template +struct has_value : std::false_type +{ +}; + +template +struct has_value().value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_value_v = has_value::value; +#pragma endregion + +#pragma region set_value +template +struct has_set_value : std::false_type +{ +}; + +template +struct has_set_value().set_value( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_value_v = has_set_value::value; +#pragma endregion + +#pragma region incr_value +template +struct has_incr_value : std::false_type +{ +}; + +template +struct has_incr_value().incr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_value_v = has_incr_value::value; +#pragma endregion + +#pragma region decr_value +template +struct has_decr_value : std::false_type +{ +}; + +template +struct has_decr_value().decr_value( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_decr_value_v = has_decr_value::value; +#pragma endregion + +#pragma region has_get_fanin0 +template +struct has_get_fanin0 : std::false_type +{ +}; + +template +struct has_get_fanin0().get_fanin0( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_fanin0_v = has_get_fanin0::value; +#pragma endregion + +#pragma region has_clear_visited +template +struct has_clear_visited : std::false_type +{ +}; + +template +struct has_clear_visited().clear_visited() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_visited_v = has_clear_visited::value; +#pragma endregion + +#pragma region has_visited +template +struct has_visited : std::false_type +{ +}; + +template +struct has_visited().visited( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_visited_v = has_visited::value; +#pragma endregion + +#pragma region set_visited +template +struct has_set_visited : std::false_type +{ +}; + +template +struct has_set_visited().set_visited( std::declval>(), uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_visited_v = has_set_visited::value; +#pragma endregion + +#pragma region trav_id +template +struct has_trav_id : std::false_type +{ +}; + +template +struct has_trav_id().trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_trav_id_v = has_trav_id::value; +#pragma endregion + +#pragma region incr_trav_id +template +struct has_incr_trav_id : std::false_type +{ +}; + +template +struct has_incr_trav_id().incr_trav_id() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_incr_trav_id_v = has_incr_trav_id::value; +#pragma endregion + +#pragma region has_get_network_name +template +struct has_get_network_name : std::false_type +{ +}; + +template +struct has_get_network_name().get_network_name() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_network_name_v = has_get_network_name::value; +#pragma endregion + +#pragma region has_set_network_name +template +struct has_set_network_name : std::false_type +{ +}; + +template +struct has_set_network_name().set_network_name( std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_network_name_v = has_set_network_name::value; +#pragma endregion + +#pragma region has_get_name +template +struct has_get_name : std::false_type +{ +}; + +template +struct has_get_name().get_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_name_v = has_get_name::value; +#pragma endregion + +#pragma region has_set_name +template +struct has_set_name : std::false_type +{ +}; + +template +struct has_set_name().set_name( std::declval>(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_name_v = has_set_name::value; +#pragma endregion + +#pragma region has_has_name +template +struct has_has_name : std::false_type +{ +}; + +template +struct has_has_name().has_name( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_name_v = has_has_name::value; +#pragma endregion + +#pragma region has_get_output_name +template +struct has_get_output_name : std::false_type +{ +}; + +template +struct has_get_output_name().get_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_get_output_name_v = has_get_output_name::value; +#pragma endregion + +#pragma region has_set_output_name +template +struct has_set_output_name : std::false_type +{ +}; + +template +struct has_set_output_name().set_output_name( uint32_t(), std::string() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_set_output_name_v = has_set_output_name::value; +#pragma endregion + +#pragma region has_has_output_name +template +struct has_has_output_name : std::false_type +{ +}; + +template +struct has_has_output_name().has_output_name( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_has_output_name_v = has_has_output_name::value; +#pragma endregion + +#pragma region has_new_color +template +struct has_new_color : std::false_type +{ +}; + +template +struct has_new_color().new_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_new_color_v = has_new_color::value; +#pragma endregion + +#pragma region has_current_color +template +struct has_current_color : std::false_type +{ +}; + +template +struct has_current_color().current_color() )>> : std::true_type +{ +}; + +template +inline constexpr bool has_current_color_v = has_current_color::value; +#pragma endregion + +#pragma region has_clear_colors +template +struct has_clear_colors : std::false_type +{ +}; + +template +struct has_clear_colors().clear_colors( uint32_t() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_clear_colors_v = has_clear_colors::value; +#pragma endregion + +#pragma region has_color +template +struct has_color : std::false_type +{ +}; + +template +struct has_color().color( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_color_v = has_color::value; +#pragma endregion + +#pragma region has_paint +template +struct has_paint : std::false_type +{ +}; + +template +struct has_paint().paint( std::declval>() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_paint_v = has_paint::value; +#pragma endregion + +#pragma region has_eval_color +template +struct has_eval_color : std::false_type +{ +}; + +template +struct has_eval_color().eval_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_color_v = has_eval_color::value; +#pragma endregion + +#pragma region has_eval_fanins_color +template +struct has_eval_fanins_color : std::false_type +{ +}; + +template +struct has_eval_fanins_color().eval_fanins_color( std::declval>(), std::declval() ) )>> : std::true_type +{ +}; + +template +inline constexpr bool has_eval_fanins_color_v = has_eval_fanins_color::value; +#pragma endregion + +#pragma region has_EXCDC_interface +template +struct has_EXCDC_interface : std::false_type +{ +}; + +template +struct has_EXCDC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXCDC_interface_v = has_EXCDC_interface::value; +#pragma endregion + +#pragma region has_EXODC_interface +template +struct has_EXODC_interface : std::false_type +{ +}; + +template +struct has_EXODC_interface>> : std::true_type +{ +}; + +template +inline constexpr bool has_EXODC_interface_v = has_EXODC_interface::value; +#pragma endregion + +/*! \brief SFINAE based on iterator type (for compute functions). + */ +template +using iterates_over_t = std::enable_if_t::value_type, T>, T>; + +/*! \brief SFINAE based on iterator type for truth tables (for compute functions). + */ +template +using iterates_over_truth_table_t = std::enable_if_t::value_type>::value, typename std::iterator_traits::value_type>; + +template +inline constexpr bool iterates_over_v = std::is_same_v; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/abc.hpp b/include/mockturtle/utils/abc.hpp new file mode 100644 index 0000000..5988a1e --- /dev/null +++ b/include/mockturtle/utils/abc.hpp @@ -0,0 +1,118 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file abc.hpp + \brief Utility functions for interfacing with ABC + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once +#ifdef ENABLE_ABC + +#include "../networks/aig.hpp" +#include "../networks/gia.hpp" + +namespace mockturtle +{ + +void aig_to_gia(gia_network &gia, aig_network aig) { + using aig_node = aig_network::node; + using aig_signal = aig_network::signal; + + std::vector a_to_g(aig.size()); + + /* constant */ + a_to_g[0] = gia.get_constant(false); + + /* pis */ + aig.foreach_pi([&](aig_node n) { + a_to_g[n] = gia.create_pi(); + }); + + /* ands */ + aig.foreach_gate([&](aig_node n){ + std::array fis; + aig.foreach_fanin(n, [&](aig_signal fi, int index){ + fis[index] = aig.is_complemented(fi) ? !a_to_g[aig.get_node(fi)] : a_to_g[aig.get_node(fi)]; + }); + a_to_g[n] = gia.create_and(fis[0], fis[1]); + }); + + /* pos */ + aig.foreach_po([&](aig_signal f){ + gia.create_po(aig.is_complemented(f) ? !a_to_g[aig.get_node(f)] : a_to_g[aig.get_node(f)]); + }); +} + +void gia_to_aig(aig_network aig, const gia_network &gia) { + using gia_node = gia_network::node; + using gia_signal = gia_network::signal; + + std::vector g_to_a(gia.size()); + + /* constant */ + g_to_a[0] = aig.get_constant(false); + + /* pis */ + gia.foreach_pi([&](gia_network::node n){ + g_to_a[n] = aig.create_pi(); + }); + + /* ands */ + gia.foreach_gate([&](gia_network::node n){ + std::array fis; + gia.foreach_fanin(n, [&](gia_signal fi, int index){ + fis[index] = gia.is_complemented(fi) ? !g_to_a[gia.get_node(fi)] : g_to_a[gia.get_node(fi)]; + }); + + g_to_a[n] = aig.create_and(fis[0], fis[1]); + }); + + /* pos */ + gia.foreach_po([&](gia_network::signal f){ + aig.create_po(gia.is_complemented(f) ? !g_to_a[gia.get_node(f)] : g_to_a[gia.get_node(f)]); + }); +} + +aig_network call_abc_script( aig_network const& aig, std::string const& script ) +{ + gia_network gia( aig.size() << 1 ); + aig_to_gia( gia, aig ); + + gia.load_rc(); + gia.run_opt_script( script ); + + aig_network new_aig; + gia_to_aig( new_aig, gia ); + + new_aig = cleanup_dangling( new_aig ); + return new_aig; +} + +} + +#endif \ No newline at end of file diff --git a/include/mockturtle/utils/algorithm.hpp b/include/mockturtle/utils/algorithm.hpp new file mode 100644 index 0000000..6397fff --- /dev/null +++ b/include/mockturtle/utils/algorithm.hpp @@ -0,0 +1,230 @@ +/* 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 algorithm.hpp + \brief STL-like algorithm extensions + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken +*/ + +#pragma once + +#include + +namespace mockturtle +{ + +template +T tree_reduce( Iterator first, Iterator last, T const& init, BinaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( *first, *( first + 1 ) ); + default: + { + const auto m = len / 2; + return op( tree_reduce( first, first + m, init, op ), tree_reduce( first + m, last, init, op ) ); + } + break; + } +} + +template +T ternary_tree_reduce( Iterator first, Iterator last, T const& init, TernaryOperation&& op ) +{ + const auto len = std::distance( first, last ); + + switch ( len ) + { + case 0u: + return init; + case 1u: + return *first; + case 2u: + return op( init, *first, *( first + 1 ) ); + case 3u: + return op( *first, *( first + 1 ), *( first + 2 ) ); + default: + { + const auto m1 = len / 3; + const auto m2 = ( len - m1 ) / 2; + return op( ternary_tree_reduce( first, first + m1, init, op ), + ternary_tree_reduce( first + m1, first + m1 + m2, init, op ), + ternary_tree_reduce( first + m1 + m2, last, init, op ) ); + } + break; + } +} + +template +Iterator max_element_unary( Iterator first, Iterator last, UnaryOperation&& fn, T const& init ) +{ + auto best = last; + auto max = init; + for ( ; first != last; ++first ) + { + if ( const auto v = fn( *first ) > max ) + { + max = v; + best = first; + } + } + return best; +} + +template>> +constexpr auto range( T begin, T end ) +{ + struct iterator + { + using value_type = T; + + value_type curr_; + bool operator!=( iterator const& other ) const { return curr_ != other.curr_; } + iterator& operator++() + { + ++curr_; + return *this; + } + iterator operator++( int ) + { + auto copy = *this; + ++( *this ); + return copy; + } + value_type operator*() const { return curr_; } + }; + struct iterable_wrapper + { + T begin_; + T end_; + auto begin() { return iterator{ begin_ }; } + auto end() { return iterator{ end_ }; } + }; + return iterable_wrapper{ begin, end }; +} + +template>> +constexpr auto range( T end ) +{ + return range( {}, end ); +} + +/*! \brief Performs the set union of two sorted sets. + * + * Compared to std::set_union, limits the copy to `limit`. + * Moreover, it returns the number of elements copied if the + * union operation is successful. Else, it returns -1. + * + */ +template +int32_t set_union_safe( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, uint32_t limit ) +{ + /* special case: sets are at the limit */ + if ( std::distance( first1, last1 ) == limit && std::distance( first2, last2 ) == limit ) + { + while ( first1 != last1 ) + { + if ( *first1 != *first2 ) + return -1; + + *result = *first1; + ++first1; + ++first2; + ++result; + } + + return static_cast( limit ); + } + + uint32_t size = 0; + while ( size < limit ) + { + if ( first1 == last1 ) + { + size += std::distance( first2, last2 ); + if ( size <= limit ) + { + std::copy( first2, last2, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + else if ( first2 == last2 ) + { + size += std::distance( first1, last1 ); + if ( size <= limit ) + { + std::copy( first1, last1, result ); + return static_cast( size ); + } + else + { + return -1; + } + } + + if ( *first1 < *first2 ) + { + *result = *first1; + ++first1; + } + else if ( *first2 < *first1 ) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + + ++result; + ++size; + } + + if ( std::distance( first1, last1 ) + std::distance( first2, last2 ) > 0 ) + return -1; + + return static_cast( size ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/cost_functions.hpp b/include/mockturtle/utils/cost_functions.hpp new file mode 100644 index 0000000..6b5315b --- /dev/null +++ b/include/mockturtle/utils/cost_functions.hpp @@ -0,0 +1,146 @@ +/* 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 cost_functions.hpp + \brief Various cost functions for (optimization) algorithms + + \author Heinz Riener + \author Mathias Soeken + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include + +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +template +struct unit_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + (void)ntk; + (void)node; + return 1u; + } +}; + +template +struct mc_cost +{ + uint32_t operator()( Ntk const& ntk, node const& node ) const + { + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_xor( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( ntk.is_xor3( node ) ) + { + return 0u; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( ntk.is_nary_and( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( ntk.is_nary_or( node ) ) + { + if ( ntk.fanin_size( node ) > 1u ) + { + return ntk.fanin_size( node ) - 1u; + } + return 0u; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( ntk.is_nary_xor( node ) ) + { + return 0u; + } + } + + // TODO (Does not take into account general node functions) + return 1u; + } +}; + +struct lut_unitary_cost +{ + std::pair operator()( uint32_t num_leaves ) const + { + if ( num_leaves < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } + + std::pair operator()( kitty::dynamic_truth_table const& tt ) const + { + if ( tt.num_vars() < 2u ) + return { 0u, 0u }; + return { 1u, 1u }; /* area, delay */ + } +}; + +template> +uint32_t costs( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + uint32_t total{ 0u }; + NodeCostFn cost_fn{}; + ntk.foreach_gate( [&]( auto const& n ) { + total += cost_fn( ntk, n ); + } ); + return total; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/cuts.hpp b/include/mockturtle/utils/cuts.hpp new file mode 100644 index 0000000..5a9c440 --- /dev/null +++ b/include/mockturtle/utils/cuts.hpp @@ -0,0 +1,591 @@ +/* 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 cuts.hpp + \brief Data structure for cuts + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "algorithm.hpp" + +namespace mockturtle +{ + +struct empty_cut_data +{ +}; + +/*! \brief A data-structure to hold a cut. + * + * The cut class is specialized via two template arguments, `MaxLeaves` and `T`. + * `MaxLeaves` controls the maximum number of leaves a cut can hold. To + * guarantee an efficient implementation, this value should be \f$k \cdot l\f$, + * where \f$k\f$ is the maximum cut size and \f$l\f$ is the maximum fanin size + * of a gate in the logic network. The second template argument `T` can be a + * type for which a data entry is created in the cut to store additional data, + * e.g., to compute the cost of a cut. It defaults to `empty_cut_data`, which + * is an empty struct that does not consume memory. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector data{1, 2, 3, 4}; + + cut<10> cut1; + cut1.set_leaves( data.begin(), data.end() ); + + cut<10, uint32_t> cut2; + cut2.set_leaves( data.begin(), data.end() ); + cut2.data() = 42u; + + struct cut_data { uint32_t costs; }; + cut<20, cut_data> c3; + c3.set_leaves( std::vector{1, 2, 3} ); + c3->costs = 37u; + \endverbatim + */ +template +class cut +{ +public: + /*! \brief Default constructor. + */ + cut() = default; + + /*! \brief Copy constructor. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut( cut const& other ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + + /*! \brief Assignment operator. + * + * Copies leaves, length, signature, and data. + * + * \param other Other cut + */ + cut& operator=( cut const& other ); + + /*! \brief Sets leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void set_leaves( Iterator begin, Iterator end ); + + /*! \brief Sets leaves (using container). + * + * Convenience function, which extracts the begin and end iterators from the + * container. + */ + template + void set_leaves( Container const& c ); + + /*! \brief Add leaves (using iterators). + * + * \param begin Begin iterator to leaves + * \param end End iterator to leaves (exclusive) + */ + template + void add_leaves( Iterator begin, Iterator end ); + + /*! \brief Signature of the cut. */ + auto signature() const { return _signature; } + + /*! \brief Returns the size of the cut (number of leaves). */ + auto size() const { return _length; } + + /*! \brief Begin iterator (constant). */ + auto begin() const { return _leaves.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _cend; } + + /*! \brief Begin iterator (mutable). */ + auto begin() { return _leaves.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _end; } + + /*! \brief Access to data (mutable). */ + T* operator->() { return &_data; } + + /*! \brief Access to data (constant). */ + T const* operator->() const { return &_data; } + + /*! \brief Access to data (mutable). */ + T& data() { return _data; } + + /*! \brief Access to data (constant). */ + T const& data() const { return _data; } + + /*! \brief Checks whether the cut is a subset of another cut. + * + * If \f$L_1\f$ are the leaves of the current cut and \f$L_2\f$ are the leaves + * of `that`, then this method returns true if and only if + * \f$L_1 \subseteq L_2\f$. + * + * \param that Other cut + */ + bool dominates( cut const& that ) const; + + /*! \brief Merges two cuts. + * + * This method merges two cuts and stores the result in `res`. The merge of + * two cuts is the union \f$L_1 \cup L_2\f$ of the two leaf sets \f$L_1\f$ of + * the cut and \f$L_2\f$ of `that`. The merge is only successful if the + * union has not more than `cut_size` elements. In that case, the function + * returns `false`, otherwise `true`. + * + * \param that Other cut + * \param res Resulting cut + * \param cut_size Maximum cut size + * \return True, if resulting cut is small enough + */ + bool merge( cut const& that, cut& res, uint32_t cut_size ) const; + +private: + std::array _leaves; + uint32_t _length; + uint64_t _signature; + typename std::array::const_iterator _cend; + typename std::array::iterator _end; + + T _data; +}; + +/*! \brief Compare two cuts. + * + * Default comparison function for two cuts. A cut is smaller than another + * cut, if it has fewer leaves. + * + * This function should be specialized for custom cuts, if additional data + * changes the cost of a cut. + */ +template +bool operator<( cut const& c1, cut const& c2 ) +{ + return c1.size() < c2.size(); +} + +/*! \brief Prints a cut. + */ +template +std::ostream& operator<<( std::ostream& os, cut const& c ) +{ + os << "{ "; + std::copy( c.begin(), c.end(), std::ostream_iterator( os, " " ) ); + os << "}"; + return os; +} + +template +cut& cut::operator=( cut const& other ) +{ + if ( &other != this ) + { + _cend = _end = std::copy( other.begin(), other.end(), _leaves.begin() ); + _length = other._length; + _signature = other._signature; + _data = other._data; + } + return *this; +} + +template +template +void cut::set_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _leaves.begin() ); + _length = static_cast( std::distance( begin, end ) ); + _signature = 0; + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +template +void cut::set_leaves( Container const& c ) +{ + set_leaves( std::begin( c ), std::end( c ) ); +} + +template +template +void cut::add_leaves( Iterator begin, Iterator end ) +{ + _cend = _end = std::copy( begin, end, _end ); + _length = static_cast( std::distance( _leaves.begin(), _end ) ); + + while ( begin != end ) + { + _signature |= UINT64_C( 1 ) << ( *begin++ & 0x3f ); + } +} + +template +bool cut::dominates( cut const& that ) const +{ + /* quick check for counter example */ + if ( _length > that._length || ( _signature & that._signature ) != _signature ) + { + return false; + } + + if ( _length == that._length ) + { + return std::equal( begin(), end(), that.begin() ); + } + + if ( _length == 0 ) + { + return true; + } + + // this is basically + // return std::includes( that.begin(), that.end(), begin(), end() ) + // but it turns out that this code is faster compared to the standard + // implementation. + for ( auto it2 = that.begin(), it1 = begin(); it2 != that.end(); ++it2 ) + { + if ( *it2 > *it1 ) + { + return false; + } + if ( ( *it2 == *it1 ) && ( ++it1 == end() ) ) + { + return true; + } + } + + return false; +} + +template +bool cut::merge( cut const& that, cut& res, uint32_t cut_size ) const +{ + if ( _length + that._length > cut_size ) + { + const auto sign = _signature + that._signature; + if ( uint32_t( __builtin_popcount( static_cast( sign & 0xffffffff ) ) ) + uint32_t( __builtin_popcount( static_cast( sign >> 32 ) ) ) > cut_size ) + { + return false; + } + } + + int32_t length = set_union_safe( begin(), end(), that.begin(), that.end(), res.begin(), cut_size ); + if ( length >= 0 ) + { + res._cend = res._end = res.begin() + length; + res._length = static_cast( length ); + res._signature = _signature | that._signature; + return true; + } + return false; +} + +/*! \brief A data-structure to hold a set of cuts. + * + * The aim of a cut set is to contain cuts and maintain two properties. First, + * all cuts are ordered according to the `<` operator, and second, all cuts + * are irredundant, i.e., no cut in the set dominates another cut in the set. + * + * The cut set is defined using the `CutType` of cuts it should hold and a + * maximum number of cuts it can hold. No check is performed whether a cut set + * is full, and therefore the caller must not insert cuts into a full set. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + cut_set, 30> cuts; + + cut<10> c1, c2, c3, c4; + + c1.set_leaves( {1, 2, 3} ); + c2.set_leaves( {4, 5} ); + c3.set_leaves( {1, 2} ); + c4.set_leaves( {1, 3, 4} ); + + cuts.insert( c1 ); + cuts.insert( c2 ); + cuts.insert( c3 ); + cuts.insert( c4 ); + + assert( cuts.size() == 3 ); + + std::cout << cuts << std::endl; + + // will print: + // { 4, 5 } + // { 1, 2 } + // { 1, 3, 4 } + \endverbatim + */ +template +class cut_set +{ +public: + /*! \brief Standard constructor. + */ + cut_set(); + + /*! \brief Clears a cut set. + */ + void clear(); + + /*! \brief Adds a cut to the end of the set. + * + * This function should only be called to create a set of cuts which is known + * to be sorted and irredundant (i.e., no cut in the set dominates another + * cut). + * + * \param begin Begin iterator to leaf indexes + * \param end End iterator (exclusive) to leaf indexes + * \return Reference to the added cut + */ + template + CutType& add_cut( Iterator begin, Iterator end ); + + /*! \brief Checks whether cut is dominates by any cut in the set. + * + * \param cut Cut outside of the set + */ + bool is_dominated( CutType const& cut ) const; + + /*! \brief Inserts a cut into a set. + * + * This method will insert a cut into a set and maintain an order. Before the + * cut is inserted into the correct position, it will remove all cuts that are + * dominated by `cut`. + * + * If `cut` is dominated by any of the cuts in the set, it will still be + * inserted. The caller is responsible to check whether `cut` is dominated + * before inserting it into the set. + * + * \param cut Cut to insert. + */ + void insert( CutType const& cut ); + + /*! \brief Begin iterator (constant). + * + * The iterator will point to a cut pointer. + */ + auto begin() const { return _pcuts.begin(); } + + /*! \brief End iterator (constant). */ + auto end() const { return _pcend; } + + /*! \brief Begin iterator (mutable). + * + * The iterator will point to a cut pointer. + */ + auto begin() { return _pcuts.begin(); } + + /*! \brief End iterator (mutable). */ + auto end() { return _pend; } + + /*! \brief Number of cuts in the set. */ + auto size() const { return _pcend - _pcuts.begin(); } + + /*! \brief Returns reference to cut at index. + * + * This function does not return the cut pointer but dereferences it and + * returns a reference. The function does not check whether index is in the + * valid range. + * + * \param index Index + */ + auto const& operator[]( uint32_t index ) const { return *_pcuts[index]; } + + /*! \brief Returns the best cut, i.e., the first cut. + */ + auto const& best() const { return *_pcuts[0]; } + + /*! \brief Updates the best cut. + * + * This method will set the cut at index `index` to be the best cut. All + * cuts before `index` will be moved one position higher. + * + * \param index Index of new best cut + */ + void update_best( uint32_t index ); + + /*! \brief Resize the cut set, if it is too large. + * + * This method will resize the cut set to `size` only if the cut set has more + * than `size` elements. Otherwise, the size will remain the same. + */ + void limit( uint32_t size ); + + /*! \brief Prints a cut set. */ + friend std::ostream& operator<<( std::ostream& os, cut_set const& set ) + { + for ( auto const& c : set ) + { + os << *c << "\n"; + } + return os; + } + +private: + std::array _cuts; + std::array _pcuts; + typename std::array::const_iterator _pcend{ _pcuts.begin() }; + typename std::array::iterator _pend{ _pcuts.begin() }; +}; + +template +cut_set::cut_set() +{ + clear(); +} + +template +void cut_set::clear() +{ + _pcend = _pend = _pcuts.begin(); + auto pit = _pcuts.begin(); + for ( auto& c : _cuts ) + { + *pit++ = &c; + } +} + +template +template +CutType& cut_set::add_cut( Iterator begin, Iterator end ) +{ + assert( _pend != _pcuts.end() ); + + auto& cut = **_pend++; + cut.set_leaves( begin, end ); + + ++_pcend; + return cut; +} + +template +bool cut_set::is_dominated( CutType const& cut ) const +{ + return std::find_if( _pcuts.begin(), _pcend, [&cut]( auto const* other ) { return other->dominates( cut ); } ) != _pcend; +} + +template +void cut_set::insert( CutType const& cut ) +{ + /* remove elements that are dominated by new cut */ + _pcend = _pend = std::stable_partition( _pcuts.begin(), _pend, [&cut]( auto const* other ) { return !cut.dominates( *other ); } ); + + /* insert cut in a sorted way */ + auto ipos = std::lower_bound( _pcuts.begin(), _pend, &cut, []( auto a, auto b ) { return *a < *b; } ); + + /* too many cuts, we need to remove one */ + if ( _pend == _pcuts.end() ) + { + /* cut to be inserted is worse than all the others, return */ + if ( ipos == _pend ) + { + return; + } + else + { + /* remove last cut */ + --_pend; + --_pcend; + } + } + + /* copy cut */ + auto& icut = *_pend; + icut->set_leaves( cut.begin(), cut.end() ); + icut->data() = cut.data(); + + if ( ipos != _pend ) + { + auto it = _pend; + while ( it > ipos ) + { + std::swap( *it, *( it - 1 ) ); + --it; + } + } + + /* update iterators */ + _pcend++; + _pend++; +} + +template +void cut_set::update_best( uint32_t index ) +{ + auto* best = _pcuts[index]; + for ( auto i = index; i > 0; --i ) + { + _pcuts[i] = _pcuts[i - 1]; + } + _pcuts[0] = best; +} + +template +void cut_set::limit( uint32_t size ) +{ + if ( std::distance( _pcuts.begin(), _pend ) > static_cast( size ) ) + { + _pcend = _pend = _pcuts.begin() + size; + } +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/debugging_utils.hpp b/include/mockturtle/utils/debugging_utils.hpp new file mode 100644 index 0000000..0a53b79 --- /dev/null +++ b/include/mockturtle/utils/debugging_utils.hpp @@ -0,0 +1,551 @@ +/* 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 debugging_utils.hpp + \brief Network debugging utilities + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../algorithms/simulation.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +/*! \brief Prints information of all nodes in a network + * + * This utility function prints the following information for all + * nodes in the network: + * - ID + * - Fanin signals, if any + * - Level, if `level` is provided for the network type + * - Whether the node is dead + * - Reference count (fanout size) + * - Visited marker + * - Custom value data + * + * It also prints the outputs of the network. + */ +template +inline void print( Ntk const& ntk ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + for ( uint32_t n = 0; n < ntk.size(); ++n ) + { + std::cout << n; + + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + { + std::cout << std::endl; + continue; + } + + std::cout << " = "; + + ntk.foreach_fanin( n, [&]( signal const& fi ) { + std::cout << ( ntk.is_complemented( fi ) ? "~" : "" ) << ntk.get_node( fi ) << " "; + } ); + std::cout << " ;"; + if constexpr ( has_level_v ) + { + std::cout << " [level = " << int32_t( ntk.level( n ) ) << "]"; + } + std::cout << " [dead = " << ntk.is_dead( n ) << "]"; + std::cout << " [ref = " << ntk.fanout_size( n ) << "]"; + std::cout << " [visited = " << ntk.visited( n ) << "]"; + std::cout << " [value = " << ntk.value( n ) << "]"; + std::cout << std::endl; + } + + ntk.foreach_co( [&]( signal const& s ) { + std::cout << "o " << ( ntk.is_complemented( s ) ? "~" : "" ) << ntk.get_node( s ) << std::endl; + } ); +} + +/*! \brief Counts dead nodes in a network + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true). + */ +template +inline uint64_t count_dead_nodes( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + + uint64_t counter{ 0 }; + for ( uint64_t n = 0; n < ntk.size(); ++n ) + { + if ( ntk.is_dead( n ) ) + { + ++counter; + } + } + return counter; +} + +/*! \brief Counts dangling roots in a network + * + * This utility function counts how many nodes in the network have + * a fanout size of zero. Note that it does not skip the nodes which + * are marked as dead, which are normally skipped when using `foreach` + * functions. + */ +template +inline uint64_t count_dangling_roots( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size function" ); + + uint64_t counter{ 0 }; + for ( uint64_t n = 0; n < ntk.size(); ++n ) + { + if ( ntk.fanout_size( n ) == 0 ) + { + ++counter; + } + } + return counter; +} + +namespace detail +{ + +template +void count_reachable_dead_nodes_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using signal = typename Ntk::signal; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + if ( ntk.is_dead( n ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + + ntk.paint( n ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + count_reachable_dead_nodes_recur( ntk, ntk.get_node( fi ), nodes ); + } ); +} + +} /* namespace detail */ + +/*! \brief Counts reachable dead nodes in a network + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true) and are reachable + * from an output. + * + * This function requires the `paint` by the network (provided by + * wrapping with `color_view`). + */ +template +inline uint64_t count_reachable_dead_nodes( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector dead_nodes; + ntk.foreach_co( [&]( signal const& po ) { + detail::count_reachable_dead_nodes_recur( ntk, ntk.get_node( po ), dead_nodes ); + } ); + + return dead_nodes.size(); +} + +namespace detail +{ + +template +void count_reachable_dead_nodes_from_node_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using node = typename Ntk::node; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + if ( ntk.is_dead( n ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + + ntk.paint( n ); + ntk.foreach_fanin( n, [&]( auto const& f ) { + count_reachable_dead_nodes_from_node_recur( ntk, ntk.get_node( f ), nodes ); + } ); +} + +} /* namespace detail */ + +/*! \brief Counts dead nodes that are reachable from a given node + * + * This utility function counts how many nodes in the network are + * said to be dead (i.e., `is_dead` returns true) and are reachable + * from a given node (i.e., in the transitive fanin cone of this node). + * + * This function requires `paint` of the network (provided by + * wrapping with `color_view`). + */ +template +inline uint64_t count_reachable_dead_nodes_from_node( Ntk const& ntk, typename Ntk::node const& n ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector dead_nodes; + detail::count_reachable_dead_nodes_from_node_recur( ntk, n, dead_nodes ); + + return dead_nodes.size(); +} + +/*! \brief Counts nodes with dead fanin(s) in a network + * + * This utility function counts how many (not-dead) nodes in the + * network have at least one fanin being dead. + */ +template +uint64_t count_nodes_with_dead_fanins( Ntk const& ntk ) +{ + 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 function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + uint64_t counter{ 0u }; + ntk.foreach_node( [&]( node const& n ) { + ntk.foreach_fanin( n, [&]( signal const& s ) { + if ( ntk.is_dead( ntk.get_node( s ) ) ) + { + counter++; + return false; + } + return true; + } ); + } ); + + return counter; +} + +namespace detail +{ + +template +bool network_is_acyclic_recur( Ntk const& ntk, typename Ntk::node const& n ) +{ + using signal = typename Ntk::signal; + + if ( ntk.color( n ) == ntk.current_color() ) + { + return true; + } + + if ( ntk.color( n ) == ntk.current_color() - 1 ) + { + /* cycle detected at node n */ + return false; + } + + ntk.paint( n, ntk.current_color() - 1 ); + + bool result{ true }; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + if ( !network_is_acyclic_recur( ntk, ntk.get_node( fi ) ) ) + { + result = false; + return false; + } + return true; + } ); + ntk.paint( n, ntk.current_color() ); + + return result; +} + +} /* namespace detail */ + +/*! \brief Check if a network is acyclic + * + * This utility function checks if the network is acyclic, i.e., there + * is no path from a node to itself. + * + * This function requires `paint` of the network (provided by + * wrapping with `color_view`). + */ +template +bool network_is_acyclic( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_ci_v, "Ntk does not implement the foreach_ci function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + ntk.new_color(); + + ntk.paint( ntk.get_node( ntk.get_constant( false ) ) ); + ntk.foreach_ci( [&]( node const& n ) { + ntk.paint( n ); + } ); + + bool result{ true }; + ntk.foreach_co( [&]( signal const& o ) { + if ( !detail::network_is_acyclic_recur( ntk, ntk.get_node( o ) ) ) + { + result = false; + return false; + } + return true; + } ); + + return result; +} + +/*! \brief Check the level information of a network + * + * This utility function checks if the levels of each node in the + * network and the depth of the network are correct. + * + * This function requires `level` of the network (provided by + * wrapping with `depth_view`). + */ +template +bool check_network_levels( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size function" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant function" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_level_v, "Ntk does not implement the level function" ); + static_assert( has_depth_v, "Ntk does not implement the depth function" ); + + using signal = typename Ntk::signal; + + uint32_t max = 0; + for ( uint32_t i = 0u; i < ntk.size(); ++i ) + { + if ( ntk.is_constant( i ) || ntk.is_ci( i ) || ntk.is_dead( i ) ) + { + continue; + } + + uint32_t max_fanin_level = 0; + ntk.foreach_fanin( i, [&]( signal fi ) { + if ( ntk.level( ntk.get_node( fi ) ) > max_fanin_level ) + { + max_fanin_level = ntk.level( ntk.get_node( fi ) ); + } + } ); + + /* the node's level has not been correctly computed */ + if ( ntk.level( i ) != max_fanin_level + 1 ) + { + return false; + } + + if ( ntk.level( i ) > max ) + { + max = ntk.level( i ); + } + } + + /* the network's depth has not been correctly computed */ + if ( ntk.depth() != max ) + { + return false; + } + + return true; +} + +/*! \brief Check the fanout information of a network + * + * This utility function checks if the fanouts of each node in the + * network are correct. + * + * This function requires `foreach_fanout` of the network (provided by + * wrapping with `fanout_view`). + */ +template +bool check_fanouts( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + for ( auto i = 0u; i < ntk.size(); ++i ) + { + uint32_t fanout_counter{ 0 }; + + bool fanout_error = false; + ntk.foreach_fanout( i, [&]( node fo ) { + ++fanout_counter; + + /* check the fanins of the fanout */ + bool found = false; + ntk.foreach_fanin( fo, [&]( signal fi ) { + if ( ntk.get_node( fi ) == i ) + { + found = true; + return false; + } + return true; + } ); + + /* if errors have been detected, then terminate */ + if ( !found ) + { + fanout_error = true; + return false; + } + + return true; + } ); + + /* report error */ + if ( fanout_error ) + { + return false; + } + + /* update the fanout counter by considering outputs */ + ntk.foreach_co( [&]( signal f ) { + if ( ntk.get_node( f ) == i ) + { + ++fanout_counter; + } + } ); + + /* report error fanout_size does not match with the counter */ + if ( fanout_counter != ntk.fanout_size( i ) ) + { + return false; + } + } + + return true; +} + +/*! \brief Check functional equivalence between a window in a network + * and a stand-alone window. + * + * This utility function checks if a window in a network, defined by + * a set of inputs, a set of gates (internal nodes), and a set of + * outputs, is functionally equivalent to an extracted window represented + * as a stand-alone network. + * + */ +template +bool check_window_equivalence( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates, NtkWin const& win_opt ) +{ + NtkWin win; + clone_subnetwork( ntk, inputs, outputs, gates, win ); + topo_view topo_win{ win_opt }; + assert( win.num_pis() == win_opt.num_pis() ); + assert( win.num_pos() == win_opt.num_pos() ); + + default_simulator sim( inputs.size() ); + auto const tts1 = simulate( win, sim ); + auto const tts2 = simulate>( topo_win, sim ); + for ( auto i = 0u; i < tts1.size(); ++i ) + { + if ( tts1[i] != tts2[i] ) + { + return false; + } + } + return true; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/hash_functions.hpp b/include/mockturtle/utils/hash_functions.hpp new file mode 100644 index 0000000..a4d9b17 --- /dev/null +++ b/include/mockturtle/utils/hash_functions.hpp @@ -0,0 +1,156 @@ +/* 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 hash_functions.hpp + \brief hash specializations for collections. + + \author Dewmini Sudara Marakkalage +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +template +struct hash +{ + std::hash h; + size_t operator()( const T& t ) const { return h( t ); } +}; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash>; + +template +struct hash> +{ +public: + size_t operator()( const std::tuple& key ) const + { + size_t seed = ha( std::get<0>( key ) ); + seed ^= hb( std::get<1>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + return seed; + } + +private: + hash ha; + hash hb; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::tuple& key ) const + { + size_t seed = ha( std::get<0>( key ) ); + seed ^= hb( std::get<1>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + seed ^= hc( std::get<2>( key ) ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + return seed; + } + +private: + hash ha; + hash hb; + hash hc; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::vector& key ) const + { + std::size_t seed = key.size(); + for ( auto& i : key ) + { + seed ^= ha( i ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; +}; + +template +struct hash>> +{ +public: + size_t operator()( const std::multiset>& key ) const + { + size_t seed = key.size(); + for ( auto& x : key ) + { + seed ^= ha( x ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; +}; + +template +struct hash> +{ +public: + size_t operator()( const std::map& key ) const + { + size_t seed = key.size(); + for ( auto it = key.begin(); it != key.end(); it++ ) + { + seed ^= ha( it->first ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + seed ^= hb( it->second ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 ); + } + return seed; + } + +private: + hash ha; + hash hb; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/include/percy.hpp b/include/mockturtle/utils/include/percy.hpp new file mode 100644 index 0000000..da2576d --- /dev/null +++ b/include/mockturtle/utils/include/percy.hpp @@ -0,0 +1,51 @@ +/* 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 percy.hpp + \brief Include percy, disable warnings for Windows + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4018 ) +#pragma warning( disable : 4068 ) +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4267 ) +#pragma warning( disable : 4334 ) +#pragma warning( disable : 4477 ) +#pragma warning( disable : 4566 ) +#pragma warning( disable : 4805 ) +#pragma warning( disable : 4996 ) +#include +#pragma warning( pop ) +#else +#include +#endif \ No newline at end of file diff --git a/include/mockturtle/utils/include/supergate.hpp b/include/mockturtle/utils/include/supergate.hpp new file mode 100644 index 0000000..2ff55c0 --- /dev/null +++ b/include/mockturtle/utils/include/supergate.hpp @@ -0,0 +1,92 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file supergate.hpp + \brief Defines the composed gate and supergate data structure. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../../io/genlib_reader.hpp" + +#include + +namespace mockturtle +{ + +template +struct composed_gate +{ + /* unique ID */ + uint32_t id; + + /* gate is a supergate */ + bool is_super{ false }; + + /* pointer to the root library gate */ + gate const* root{ nullptr }; + + /* support of the composed gate */ + uint32_t num_vars{ 0 }; + + /* function */ + kitty::dynamic_truth_table function; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delays */ + std::array tdelay{}; + + /* fanin gates */ + std::vector*> fanin{}; +}; + +template +struct supergate +{ + /* pointer to the root gate */ + composed_gate const* root{}; + + /* area */ + double area{ 0.0 }; + + /* pin-to-pin delay */ + std::array tdelay{}; + + /* np permutation vector */ + std::vector permutation{}; + + /* pin negations */ + uint16_t polarity{ 0 }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/index_list.hpp b/include/mockturtle/utils/index_list.hpp new file mode 100644 index 0000000..aa38400 --- /dev/null +++ b/include/mockturtle/utils/index_list.hpp @@ -0,0 +1,1079 @@ +/* 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 index_list.hpp + \brief List of indices to represent small networks. + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +/*! \brief Index list for mux-inverter graphs. + * + * Small network consisting of mux gates and inverters + * represented as a list of literals. + * + * Example: The following index list creates the output function + * `< ? x2 : x4>` with 4 inputs, 1 output, and 2 gates: + * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + */ +struct muxig_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit muxig_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + } + + explicit muxig_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 3 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 3 ) + { + fn( values.at( i ), values.at( i + 1 ), values.at( i + 2 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + element_type add_mux( element_type lit0, element_type lit1, element_type lit2 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + values.push_back( lit2 ); + return ( num_gates() + num_pis() ) << 1; + } + + element_type add_xor( element_type lit0, element_type lit1 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + + // select, then, else + values.push_back( lit0 ); + values.push_back( !lit1 ); + values.push_back( lit1 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Inserts a muxig_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_ite` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, muxig_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_ite_v, "Ntk does not implement the create_maj method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + signal const s0 = ( lit0 % 2 ) ? !signals.at( lit0 >> 1 ) : signals.at( lit0 >> 1 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( lit1 >> 1 ) : signals.at( lit1 >> 1 ); + signal const s2 = ( lit2 % 2 ) ? !signals.at( lit2 >> 1 ) : signals.at( lit2 >> 1 ); + signals.push_back( ntk.create_ite( s0, s1, s2 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an mig_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( muxig_index_list const& indices ) +{ + auto s = fmt::format( "{{{} pis | {} pos | {} gates", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + s += fmt::format( ", ({} ? {} : {})", lit0, lit1, lit2 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Index list for majority-inverter graphs. + * + * Small network consisting of majority gates and inverters + * represented as a list of literals. + * + * Example: The following index list creates the output function + * `<, x2, x4>` with 4 inputs, 1 output, and 2 gates: + * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + */ +struct mig_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit mig_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + } + + explicit mig_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 3 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 3 ) + { + fn( values.at( i ), values.at( i + 1 ), values.at( i + 2 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + element_type add_maj( element_type lit0, element_type lit1, element_type lit2 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + values.push_back( lit2 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Generates a mig_index_list from a network + * + * The function requires `ntk` to consist of majority gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_complemented` + * - `is_maj` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( mig_index_list& indices, Ntk const& ntk ) +{ + 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_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + } ); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ) { + assert( ntk.is_maj( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ) { + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2 * ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + } ); + indices.add_maj( lits[0u], lits[1u], lits[2u] ); + } ); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ) { + indices.add_output( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + } ); + + assert( indices.size() == 1u + 3u * ntk.num_gates() + ntk.num_pos() ); +} + +/*! \brief Inserts a mig_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_maj` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, mig_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + signal const s0 = ( lit0 % 2 ) ? !signals.at( lit0 >> 1 ) : signals.at( lit0 >> 1 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( lit1 >> 1 ) : signals.at( lit1 >> 1 ); + signal const s2 = ( lit2 % 2 ) ? !signals.at( lit2 >> 1 ) : signals.at( lit2 >> 1 ); + signals.push_back( ntk.create_maj( s0, s1, s2 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an mig_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( mig_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ) { + s += fmt::format( ", {}, {}, {}", lit0, lit1, lit2 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Index list for xor-and graphs. + * + * Small network represented as a list of literals. Supports XOR and + * AND gates. The list has the following 32-bit unsigned integer + * elements. It starts with a signature whose partitioned into `| + * num_gates | num_pos | num_pis |`, where `num_gates` accounts for + * the most-significant 16 bits, `num_pos` accounts for 8 bits, and + * `num_pis` accounts for the least-significant 8 bits. Afterwards, + * gates are defined as literal indexes `(2 * i + c)`, where `i` is an + * index, with 0 indexing the constant 0, 1 to `num_pis` indexing the + * primary inputs, and all successive indexes for the gates. Gate + * literals come in pairs. If the first literal has a smaller value + * than the second one, an AND gate is created, otherwise, an XOR gate + * is created. Afterwards, all outputs are defined in terms of + * literals. + * + * Example: The following index list creates the output function `(x1 + * AND x2) XOR (x3 AND x4)` with 4 inputs, 1 output, and 3 gates: + * `{4 | 1 << 8 | 3 << 16, 2, 4, 6, 8, 12, 10, 14}` + * + * Note: if `separate_header = true`, the header will be split into 3 + * elements to support networks with larger number of PIs. + */ +template +struct xag_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit xag_index_list( uint32_t num_pis = 0 ) + : values( { num_pis } ) + { + if constexpr ( separate_header ) + { + values.emplace_back( 0 ); + values.emplace_back( 0 ); + } + } + + explicit xag_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + if constexpr ( separate_header ) + { + return values.at( 2 ); + } + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + if constexpr ( separate_header ) + { + return values.at( 0 ); + } + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + if constexpr ( separate_header ) + { + return values.at( 1 ); + } + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_gate( Fn&& fn ) const + { + if constexpr ( separate_header ) + { + assert( ( values.size() - 3u - num_pos() ) % 2 == 0 ); + for ( uint64_t i = 3u; i < values.size() - num_pos(); i += 2 ) + { + fn( values.at( i ), values.at( i + 1 ) ); + } + } + else + { + assert( ( values.size() - 1u - num_pos() ) % 2 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 2 ) + { + fn( values.at( i ), values.at( i + 1 ) ); + } + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void clear() + { + values.clear(); + values.emplace_back( 0 ); + if constexpr ( separate_header ) + { + values.emplace_back( 0 ); + values.emplace_back( 0 ); + } + } + + void add_inputs( uint32_t n = 1u ) + { + if constexpr ( !separate_header ) + { + assert( num_pis() + n <= 0xff ); + } + values.at( 0u ) += n; + } + + element_type add_and( element_type lit0, element_type lit1 ) + { + if constexpr ( separate_header ) + { + values.at( 2u ) += 1; + } + else + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + } + + values.push_back( lit0 < lit1 ? lit0 : lit1 ); + values.push_back( lit0 < lit1 ? lit1 : lit0 ); + return ( num_gates() + num_pis() ) << 1; + } + + element_type add_xor( element_type lit0, element_type lit1 ) + { + if constexpr ( separate_header ) + { + values.at( 2u ) += 1; + } + else + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + } + + values.push_back( lit0 > lit1 ? lit0 : lit1 ); + values.push_back( lit0 > lit1 ? lit1 : lit0 ); + return ( num_gates() + num_pis() ) << 1; + } + + void add_output( element_type lit ) + { + if constexpr ( separate_header ) + { + values.at( 1u ) += 1; + } + else + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + } + + values.push_back( lit ); + } + +private: + std::vector values; +}; + +using large_xag_index_list = xag_index_list; + +/*! \brief Generates a xag_index_list from a network + * + * The function requires `ntk` to consist of XOR and AND gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_and` + * - `is_complemented` + * - `is_xor` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( xag_index_list& indices, Ntk const& ntk ) +{ + 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_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + } ); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ) { + assert( ntk.is_and( n ) || ntk.is_xor( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ) { + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2 * ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + } ); + + if ( ntk.is_and( n ) ) + { + indices.add_and( lits[0u], lits[1u] ); + } + else if ( ntk.is_xor( n ) ) + { + indices.add_xor( lits[0u], lits[1u] ); + } + } ); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ) { + indices.add_output( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + } ); + + if constexpr ( separate_header ) + { + assert( indices.size() == 3u + 2u * ntk.num_gates() + ntk.num_pos() ); + } + else + { + assert( indices.size() == 1u + 2u * ntk.num_gates() + ntk.num_pos() ); + } +} + +/*! \brief Inserts a xag_index_list into an existing network + * + * **Required network functions:** + * - `create_and` + * - `create_xor` + * - `get_constant` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, xag_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if constexpr ( useSignal ) + { + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + } + else + { + static_assert( std::is_same_v::value_type>, node>, "BeginIter value_type must be Ntk node type" ); + static_assert( std::is_same_v::value_type>, node>, "EndIter value_type must be Ntk node type" ); + } + + assert( uint64_t( std::distance( begin, end ) ) == indices.num_pis() ); + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + if constexpr ( useSignal ) + { + signals.push_back( *it ); + } + else + { + signals.emplace_back( ntk.make_signal( *it ) ); + } + } + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + assert( lit0 != lit1 ); + uint32_t const i0 = lit0 >> 1; + uint32_t const i1 = lit1 >> 1; + signal const s0 = ( lit0 % 2 ) ? ntk.create_not( signals.at( i0 ) ) : signals.at( i0 ); + signal const s1 = ( lit1 % 2 ) ? ntk.create_not( signals.at( i1 ) ) : signals.at( i1 ); + signals.push_back( lit0 > lit1 ? ntk.create_xor( s0, s1 ) : ntk.create_and( s0, s1 ) ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? ntk.create_not( signals.at( i ) ) : signals.at( i ) ); + } ); +} + +/*! \brief Converts an xag_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( xag_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + s += fmt::format( ", {}, {}", lit0, lit1 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +inline std::string to_index_list_string( xag_index_list const& indices ) +{ + auto s = fmt::format( "{{{}, {}, {}", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_gate( [&]( uint32_t lit0, uint32_t lit1 ) { + s += fmt::format( ", {}, {}", lit0, lit1 ); + } ); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + } ); + + s += "}"; + + return s; +} + +/*! \brief Generates a network from an index_list + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * + * \param ntk A logic network + * \param indices An index list + */ +template +void decode( Ntk& ntk, IndexList const& indices ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + + using signal = typename Ntk::signal; + + std::vector signals( indices.num_pis() ); + std::generate( std::begin( signals ), std::end( signals ), + [&]() { return ntk.create_pi(); } ); + + insert( ntk, std::begin( signals ), std::end( signals ), indices, + [&]( signal const& s ) { ntk.create_po( s ); } ); +} + +/*! \brief Enumerate structured index_lists + * + * Enumerate concrete `xag_index_list`s from an abstract index list + * specification. The specifiation is provided in an extended index + * list format, where a `-1` indicates an unspecified input. + * + * The algorithm concretizes unspecified inputs and negates nodes. + * + * The enumerator generates the following concrete index lists + * {2 | 1 << 8 | 1 << 16, 2, 4, 6} + * {2 | 1 << 8 | 1 << 16, 2, 4, 7} + * {2 | 1 << 8 | 1 << 16, 3, 4, 6} + * {2 | 1 << 8 | 1 << 16, 3, 4, 7} + * {2 | 1 << 8 | 1 << 16, 3, 5, 6} + * {2 | 1 << 8 | 1 << 16, 3, 5, 7} + * {2 | 1 << 8 | 1 << 16, 2, 5, 6} + * {2 | 1 << 8 | 1 << 16, 2, 5, 7} + * from the abstract index list specification `{ -1, -1, 6 }`. + \verbatim embed:rst + + Example + + .. code-block:: c++ + + aig_index_list_enumerator e( { -1, -1, -1, 6, 8 }, 2u, 2u, 1u ); + e.run( [&]( xag_index_list const& il ) { + aig_network aig; + decode( aig, il ); + } ); + \endverbatim + */ +class aig_index_list_enumerator +{ +public: + explicit aig_index_list_enumerator( std::vector const& values, uint32_t num_pis, uint32_t num_gates, uint32_t num_pos ) + : values_( values ), num_pis( num_pis ), num_gates( num_gates ), num_pos( num_pos ) + {} + + template + void run( Fn&& fn ) + { + recurse( values_, 0u, fn ); + } + +protected: + template + void recurse( std::vector values, uint32_t pos, Fn&& fn ) + { + /* process gate */ + if ( pos < 2 * num_gates ) + { + auto& a = values.at( pos ); + auto& b = values.at( pos + 1 ); + if ( a == -1 && b == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + a = ( i + 1 ) << 1; + for ( uint32_t j = i + 1; j < num_pis; ++j ) + { + b = ( j + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + } + return; + } + else if ( a == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + a = ( i + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + return; + } + else if ( b == -1 ) + { + for ( uint32_t i = 0u; i < num_pis; ++i ) + { + b = ( i + 1 ) << 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + } + return; + } + + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + recurse( values, pos + 2, fn ); + a = a ^ 1; + recurse( values, pos + 2, fn ); + b = b ^ 1; + return; + } + + /* process output */ + if ( pos < values.size() ) + { + auto& o = values.at( pos ); + recurse( values, pos + 1u, fn ); + o = o ^ 1; + recurse( values, pos + 1u, fn ); + return; + } + + /* finished processing values */ + std::vector index_list; + index_list.emplace_back( num_pis | ( num_pos << 8 ) | ( num_gates << 16 ) ); + for ( int32_t const& v : values ) + { + index_list.emplace_back( v ); + } + fn( xag_index_list( index_list ) ); + } + +protected: + std::vector values_; + uint32_t num_pis; + uint32_t num_gates; + uint32_t num_pos; +}; + +template +struct is_index_list : std::false_type +{ +}; + +template<> +struct is_index_list> : std::true_type +{ +}; + +template<> +struct is_index_list> : std::true_type +{ +}; + +template<> +struct is_index_list : std::true_type +{ +}; + +template +inline constexpr bool is_index_list_v = is_index_list::value; + +} // namespace mockturtle diff --git a/include/mockturtle/utils/json_utils.hpp b/include/mockturtle/utils/json_utils.hpp new file mode 100644 index 0000000..b19f11b --- /dev/null +++ b/include/mockturtle/utils/json_utils.hpp @@ -0,0 +1,53 @@ +/* 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 json_utils.hpp + \brief JSON utils + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace kitty +{ + +inline void to_json( nlohmann::json& j, const dynamic_truth_table& tt ) +{ + j = nlohmann::json{ { "_bits", tt._bits }, { "_num_vars", tt._num_vars } }; +} + +inline void from_json( const nlohmann::json& j, dynamic_truth_table& tt ) +{ + j.at( "_bits" ).get_to( tt._bits ); + j.at( "_num_vars" ).get_to( tt._num_vars ); +} + +} // namespace kitty \ No newline at end of file diff --git a/include/mockturtle/utils/mixed_radix.hpp b/include/mockturtle/utils/mixed_radix.hpp new file mode 100644 index 0000000..1660a49 --- /dev/null +++ b/include/mockturtle/utils/mixed_radix.hpp @@ -0,0 +1,113 @@ +/* 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 mixed_radix.hpp + \brief Mixed radix loop + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +namespace mockturtle +{ + +/*! \brief Mixed radix enumeration. + * + * The iterator pair `begin` and `end` represent a list of radixes, which are + * used to enumerate all combinations of indexes. + * + * For example if the radixes are \f$2, 3, 3\f$, then the callable `fn` is + * called on the index lists \f$0, 0, 0\f$, \f$0, 0, 1\f$, \f$0, 0, 2\f$, + * \f$\dots\f$, \f$1, 2, 1\f$, \f$1, 2, 2\f$. + * + * The callable `fn` expects two parameters which are an iterator pair of the + * indexes. If it returns a `bool`, the iteration is stopped, if the return + * value is `false`. + * + * \param begin Begin iterator of radixes + * \param end End iterator of radixes + * \param fn Callable + */ +template +void foreach_mixed_radix_tuple( Iterator begin, Iterator end, Fn&& fn ) +{ + constexpr auto is_bool_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + constexpr auto is_void_f = std::is_invocable_r_v::iterator, std::vector::iterator>; + + static_assert( is_bool_f || is_void_f ); + + std::vector positions( std::distance( begin, end ), 0u ); + + while ( true ) + { + if constexpr ( is_bool_f ) + { + if ( !fn( positions.begin(), positions.end() ) ) + { + return; + } + } + else + { + fn( positions.begin(), positions.end() ); + } + + auto itm = end - 1; + auto itp = positions.end() - 1; + auto ret = false; + while ( !ret && *itp == ( *itm - 1 ) ) + { + *itp = 0; + if ( itp != positions.begin() ) + { + --itp; + } + + if ( itm == begin ) + { + ret = true; + } + else + { + --itm; + } + } + + if ( ret ) + { + break; + } + + ( *itp )++; + } +} + +} // namespace mockturtle diff --git a/include/mockturtle/utils/name_utils.hpp b/include/mockturtle/utils/name_utils.hpp new file mode 100644 index 0000000..19e4d9d --- /dev/null +++ b/include/mockturtle/utils/name_utils.hpp @@ -0,0 +1,159 @@ +/* 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 name_utils.hpp + \brief Utility functions to restore network names after optimization. + + \author Marcel Walter + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "node_map.hpp" + +namespace mockturtle +{ + +/*! \brief Restores the network name that might have been given to network's former incarnation. + * + * \param ntk_src The source logic network, which potentially has a name + * \param ntk_dest The destination logic network, whose name is to be restored + */ +template +void restore_network_name( const NtkSrc& ntk_src, NtkDest& ntk_dest ) noexcept +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + + if constexpr ( has_get_network_name_v && has_set_network_name_v ) + { + ntk_dest.set_network_name( ntk_src.get_network_name() ); + } +} + +/*! \brief Restores all names that might have been given to a network's former incarnation. + * + * **Required network functions for the NtkSrc:** + * - `foreach_node` + * - `foreach_fanin` + * - `foreach_po` + * - `get_node` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + * \param old2new Mapping of nodes from ntk_src to signals of ntk_dest + */ +template +void restore_names( const NtkSrc& ntk_src, NtkDest& ntk_dest, node_map, NtkSrc>& old2new ) noexcept +{ + restore_network_name( ntk_src, ntk_dest ); + + if constexpr ( has_has_name_v && has_get_name_v && has_set_name_v ) + { + static_assert( has_foreach_node_v, "NtkSrc does not implement the foreach_node function" ); + static_assert( has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin function" ); + static_assert( has_foreach_po_v, "NtkSrc does not implement the foreach_po function" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node function" ); + + const auto restore_signal_name = [&ntk_src, &ntk_dest, &old2new]( const auto& f ) { + if ( ntk_src.has_name( f ) ) + { + const auto name = ntk_src.get_name( f ); + + ntk_dest.set_name( old2new[ntk_src.get_node( f )], name ); + } + }; + + const auto restore_output_name = [&ntk_src, &ntk_dest]( [[maybe_unused]] const auto& po, const auto i ) { + if ( ntk_src.has_output_name( i ) ) + { + const auto name = ntk_src.get_output_name( i ); + + ntk_dest.set_output_name( i, name ); + } + }; + + ntk_src.foreach_node( [&ntk_src, &restore_signal_name]( const auto& n ) { ntk_src.foreach_fanin( n, restore_signal_name ); } ); + + ntk_src.foreach_po( restore_output_name ); + } +} + +/*! \brief Restore PI and PO names, matching by order. + * + * **Required network functions for NtkSrc:** + * - `foreach_pi` + * - `foreach_po` + * - `num_pis` + * - `num_pos` + * - `has_name` + * - `get_name` + * - `make_signal` + * - `has_output_name` + * - `get_output_name` + * + * **Required network functions for NtkDest:** + * - `foreach_pi` + * - `num_pis` + * - `num_pos` + * - `set_name` + * - `make_signal` + * - `set_output_name` + * + * \param ntk_src The source logic network, which potentially has named signals + * \param ntk_dest The destination logic network, whose names are to be restored + */ +template +void restore_pio_names_by_order( const NtkSrc& ntk_src, NtkDest& ntk_dest ) +{ + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( has_has_name_v && has_get_name_v, "NtkSrc does not implement the has_name and/or get_name functions" ); + static_assert( has_has_output_name_v && has_get_output_name_v, "NtkSrc does not implement the has_output_name and/or get_output_name functions" ); + static_assert( has_set_name_v && has_set_output_name_v, "NtkDest does not implement the set_name and/or set_output_name functions" ); + + assert( ntk_src.num_pis() == ntk_dest.num_pis() ); + assert( ntk_src.num_pos() == ntk_dest.num_pos() ); + + std::vector pi_names( ntk_src.num_pis(), "" ); + ntk_src.foreach_pi( [&]( auto const& n, auto i ) { + if ( ntk_src.has_name( ntk_src.make_signal( n ) ) ) + pi_names[i] = ntk_src.get_name( ntk_src.make_signal( n ) ); + } ); + ntk_dest.foreach_pi( [&]( auto const& n, auto i ) { + if ( pi_names[i] != "" ) + ntk_dest.set_name( ntk_dest.make_signal( n ), pi_names[i] ); + } ); + + ntk_src.foreach_po( [&]( auto const& f, auto i ) { + if ( ntk_src.has_output_name( i ) ) + ntk_dest.set_output_name( i, ntk_src.get_output_name( i ) ); + } ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/network_cache.hpp b/include/mockturtle/utils/network_cache.hpp new file mode 100644 index 0000000..26e28d3 --- /dev/null +++ b/include/mockturtle/utils/network_cache.hpp @@ -0,0 +1,173 @@ +/* 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 network_cache.hpp + \brief Network cache + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../algorithms/simulation.hpp" +#include "../io/verilog_reader.hpp" +#include "../io/write_verilog.hpp" +#include "../traits.hpp" +#include "../views/topo_view.hpp" + +namespace mockturtle +{ + +/*! \brief Network cache. + * + * ... + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + ... + \endverbatim + */ +template> +class network_cache +{ +public: + explicit network_cache( uint32_t num_vars ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + + ensure_pis( num_vars ); + } + + Ntk& network() + { + return _db; + } + + std::vector> const& pis() const + { + return _pis; + } + + void ensure_pis( uint32_t count ) + { + if ( count > _pis.size() ) + { + for ( auto i = _pis.size(); i < count; ++i ) + { + _pis.emplace_back( _db.create_pi() ); + } + } + } + + auto size() const + { + return _db.num_pos(); + } + + bool has( Key const& key ) const + { + return _map.find( key ) != _map.end(); + } + + template + bool insert( Key const& key, _Ntk const& ntk ) + { + /* ntk must have one primary output and not too many primary inputs */ + if ( ntk.num_pos() != 1u || ntk.num_pis() > _pis.size() ) + { + return false; + } + + /* insert ntk into _db, create an output and return the index of the output */ + const auto f = cleanup_dangling( ntk, _db, _pis.begin(), _pis.begin() + ntk.num_pis() ).front(); + insert_signal( key, f ); + return true; + } + + void insert_signal( Key const& key, signal const& f ) + { + _map.emplace( key, f ); + _db.create_po( f ); + _output_functions.push_back( key ); + } + + signal get( Key const& key ) const + { + return _map.at( key ).first; + } + + auto get_view( Key const& key ) const + { + return topo_view( _db, _map.at( key ) ); + } + + void insert_json( nlohmann::json const& data ) + { + ensure_pis( data["num_pis"].get() ); + + std::istringstream sstr( data["db"].get() ); + Ntk read_ntk; + lorina::read_verilog( sstr, verilog_reader( read_ntk ) ); + const auto pos = cleanup_dangling( read_ntk, _db, _pis.begin(), _pis.end() ); + + auto cntr = 0u; + for ( auto const& tt : data["output_functions"].get>() ) + { + insert_signal( tt, pos[cntr++] ); + } + } + + nlohmann::json to_json() const + { + std::stringstream sstr; + write_verilog( _db, sstr ); + + return nlohmann::json{ { "num_pis", _pis.size() }, { "output_functions", _output_functions }, { "db", sstr.str() } }; + } + +private: + Ntk _db; + std::vector> _pis; + std::unordered_map, Hash> _map; + std::vector _output_functions; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/network_utils.hpp b/include/mockturtle/utils/network_utils.hpp new file mode 100644 index 0000000..0a6bdda --- /dev/null +++ b/include/mockturtle/utils/network_utils.hpp @@ -0,0 +1,237 @@ +/* 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 network_utils.hpp + \brief Utility functions to insert a network into another network. + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "node_map.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +auto clone_node_topologically( NtkSrc const& ntk, NtkDest& subntk, unordered_node_map& node_to_signal, typename NtkSrc::node n ) +{ + if ( node_to_signal.has( n ) ) + { + return node_to_signal[n]; + } + + std::vector children; + ntk.foreach_fanin( n, [&]( auto const& fi ) { + auto s = clone_node_topologically( ntk, subntk, node_to_signal, ntk.get_node( fi ) ); + children.emplace_back( ntk.is_complemented( fi ) ? !s : s ); + } ); + + if constexpr ( has_is_and_v ) + { + static_assert( has_create_and_v && "NtkDest does not implement the create_and method" ); + if ( ntk.is_and( n ) ) + { + assert( children.size() == 2u ); + return node_to_signal[n] = subntk.create_and( children[0], children[1] ); + } + } + if constexpr ( has_is_xor_v ) + { + static_assert( has_create_xor_v && "NtkDest does not implement the create_xor method" ); + if ( ntk.is_xor( n ) ) + { + assert( children.size() == 2u ); + return node_to_signal[n] = subntk.create_xor( children[0], children[1] ); + } + } + if constexpr ( has_is_maj_v ) + { + static_assert( has_create_maj_v && "NtkDest does not implement the create_maj method" ); + if ( ntk.is_maj( n ) ) + { + assert( children.size() == 3u ); + return node_to_signal[n] = subntk.create_maj( children[0], children[1], children[2] ); + } + } + if constexpr ( has_is_xor3_v ) + { + static_assert( has_create_xor3_v && "NtkDest does not implement the create_xor3 method" ); + if ( ntk.is_xor3( n ) ) + { + assert( children.size() == 3u ); + return node_to_signal[n] = subntk.create_xor3( children[0], children[1], children[2] ); + } + } + + assert( false && "[e] unsupported node type" ); + return subntk.get_constant( false ); +} + +} // namespace detail + +/*! \brief Constructs a (sub-)network from a window of another network. + * + * The window is specified by three parameters: + * 1.) `inputs` are the common support of all window nodes, they do + * not overlap with `gates` (i.e., the intersection of `inputs` and + * `gates` is the empty set). + * 2.) `gates` are the nodes in the window, supported by the + * `inputs` (i.e., `gates` are in the transitive fanout of the + * `inputs`). + * 3.) `outputs` are signals (regular or complemented nodes) + * pointing to nodes in `gates` or `inputs`. Not all fanouts + * of an output node are already part of the window. + * + * **Required network functions for the source Ntk:** + * - `foreach_fanin` + * - `get_node` + * - `get_constant` + * - `is_complemented` + * + * **Required network functions for the cloned SubNtk:** + * - `create_pi` + * - `create_po` + * - `create_not` + * - `get_constant` + * + * \param ntk A logic network + * \param subntk An empty network to be constructed + */ +template +void clone_subnetwork( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates, SubNtk& subntk ) +{ + 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_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + + static_assert( is_network_type_v, "SubNtk is not a network type" ); + static_assert( has_create_pi_v, "SubNtk does not implement the create_pi method" ); + static_assert( has_create_po_v, "SubNtk does not implement the create_po method" ); + static_assert( has_create_not_v, "SubNtk does not implement the create_not method" ); + static_assert( has_get_constant_v, "SubNtk does not implement the get_constant method" ); + + /* map from nodes in ntk to signals in subntk */ + unordered_node_map node_to_signal( ntk ); + + /* constant */ + node_to_signal[ntk.get_node( ntk.get_constant( false ) )] = subntk.get_constant( false ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) + { + node_to_signal[ntk.get_node( ntk.get_constant( true ) )] = subntk.get_constant( true ); + } + + /* inputs */ + for ( auto const& i : inputs ) + { + node_to_signal[i] = subntk.create_pi(); + } + + /* create gates topologically */ + for ( auto const& g : gates ) + { + detail::clone_node_topologically( ntk, subntk, node_to_signal, g ); + } + + /* outputs */ + for ( auto const& o : outputs ) + { + subntk.create_po( ntk.is_complemented( o ) ? subntk.create_not( node_to_signal[ntk.get_node( o )] ) : node_to_signal[ntk.get_node( o )] ); + } +} + +/*! \brief Inserts a network into another network + * + * **Required network functions for the host Ntk:** + * - `get_constant` + * - `create_not` + * + * **Required network functions for the subnetwork SubNtk:** + * - `num_pis` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_gate` + * - `foreach_fanin` + * - `get_node` + * - `is_complemented` + * + * \param ntk The host logic network + * \param begin Begin iterator of signal inputs in the host network + * \param end End iterator of signal inputs in the host network + * \param subntk The sub-network + * \param fn Callback function + */ +template +void insert_ntk( Ntk& ntk, BeginIter begin, EndIter end, SubNtk const& subntk, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + + static_assert( is_network_type_v, "SubNtk is not a network type" ); + static_assert( has_num_pis_v, "SubNtk does not implement the num_pis method" ); + static_assert( has_foreach_pi_v, "SubNtk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "SubNtk does not implement the foreach_po method" ); + static_assert( has_foreach_gate_v, "SubNtk does not implement the foreach_gate method" ); + static_assert( has_foreach_fanin_v, "SubNtk does not implement the foreach_fanin method" ); + static_assert( has_get_node_v, "SubNtk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "SubNtk does not implement the is_complemented method" ); + + static_assert( std::is_same_v::value_type>, signal>, "BeginIter value_type must be Ntk signal type" ); + static_assert( std::is_same_v::value_type>, signal>, "EndIter value_type must be Ntk signal type" ); + + assert( uint64_t( std::distance( begin, end ) ) == subntk.num_pis() ); + + /* map from nodes in subntk to signals in ntk */ + unordered_node_map node_to_signal( subntk ); + + /* inputs */ + auto it = begin; + subntk.foreach_pi( [&]( auto const& n ) { + node_to_signal[n] = *( it++ ); + } ); + + /* create gates topologically */ + subntk.foreach_gate( [&]( auto const& n ) { + detail::clone_node_topologically( subntk, ntk, node_to_signal, n ); + } ); + + /* outputs */ + subntk.foreach_po( [&]( auto const& f ) { + fn( subntk.is_complemented( f ) ? ntk.create_not( node_to_signal[subntk.get_node( f )] ) : node_to_signal[subntk.get_node( f )] ); + } ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/node_map.hpp b/include/mockturtle/utils/node_map.hpp new file mode 100644 index 0000000..6fb4e12 --- /dev/null +++ b/include/mockturtle/utils/node_map.hpp @@ -0,0 +1,555 @@ +/* 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 node_map.hpp + \brief Map indexed by network nodes + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Associative container network nodes + * + * This container helps to store and access values associated to nodes + * in a network. + * + * Two implementations are provided, one using std::vector and another + * using std::unordered_map as internal storage. The former + * implementation can be pre-allocated and provides a fast way to + * access the data. The later implementation offers a way to + * associate values to a subset of nodes and to check whether a value + * is available. + */ +template> +class node_map; + +/*! \brief Vector node map + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure which + * is indexed by the node's index. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector; + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value = {} ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + * + * \param init_value Initialization value after resize + */ + void resize( T const& init_value = {} ) + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size(), init_value ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Unordered node map + * + * This implementation of the container is initialized with a network. + * The map entries are constructed on the fly. The container + * can be accessed via ndoes, or indirectly via signals, from which + * the corresponding node is derived. + * + * The implementation uses an std::unordered_map as underlying data + * structure which is indexed by the node's index. + * + * This implementation is aliased as `unordered_node_map`. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * + */ +template +class node_map> +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::unordered_map; + using reference = T&; + using const_reference = const T&; + +public: + /*! \brief Default constructor. */ + explicit node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared() ) + { + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Deep copy. */ + node_map copy() const + { + node_map copy( ntk ); + *( copy.data ) = *data; + return copy; + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return data->find( ntk->node_to_index( n ) ) != data->end(); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return data->find( ntk->node_to_index( ntk->get_node( f ) ) ) != data->end(); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + if ( has( n ) ) + { + data->erase( ntk->node_to_index( n ) ); + } + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + if ( has( ntk->get_node( f ) ) ) + { + data->erase( ntk->node_to_index( ntk->get_node( f ) ) ); + } + } + + /*! \brief Mutable access to value by node. */ + reference operator[]( node const& n ) + { + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Constant access to value by node. */ + const_reference operator[]( node const& n ) const + { + assert( has( n ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( n )]; + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + reference operator[]( signal const& f ) + { + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + const_reference operator[]( signal const& f ) const + { + assert( has( ntk->get_node( f ) ) && "index out of bounds" ); + return ( *data )[ntk->node_to_index( ntk->get_node( f ) )]; + } + + /*! \brief Clear all entries of the map. + * + * All data in the map is cleared. + */ + void reset() + { + data->clear(); + } + + void resize() + { + } + +protected: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Template alias `unordered_node_map` */ +template +using unordered_node_map = node_map>; + +/*! \brief Vector-based node map with validity query + * + * This container is initialized with a network to derive the size + * according to the number of nodes. The container can be accessed + * via nodes, or indirectly via signals, from which the corresponding + * node is derived. + * + * The implementation uses a vector as underlying data structure, so + * that it benefits from fast access. It is supplemented with an + * additional validity field such that it can be used like an + * `unordered_node_map`. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `node_to_index` + * + */ +template +class incomplete_node_map +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + using container_type = std::vector>; + +public: + /*! \brief Default constructor. */ + explicit incomplete_node_map( Ntk const& ntk ) + : ntk( &ntk ), + data( std::make_shared( ntk.size() ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Constructor with default value. + * + * Initializes all values in the container to `init_value`. + */ + incomplete_node_map( Ntk const& ntk, T const& init_value ) + : ntk( &ntk ), + data( std::make_shared( ntk.size(), init_value ) ) + { + static_assert( !std::is_same_v, "T cannot be std::monostate" ); + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + /*! \brief Number of keys stored in the data structure. */ + auto size() const + { + return data->size(); + } + + /*! \brief Check if a key is already defined. */ + bool has( node const& n ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Check if a key is already defined. */ + template>> + bool has( signal const& f ) const + { + return std::holds_alternative( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Erase a key (if it exists). */ + void erase( node const& n ) + { + ( *data )[ntk->node_to_index( n )] = std::monostate(); + } + + /*! \brief Erase a key (if it exists). */ + template>> + void erase( signal const& f ) + { + ( *data )[ntk->node_to_index( ntk->get_node( f ) )] = std::monostate(); + } + + /*! \brief Mutable access to value by node. */ + T& operator[]( node const& n ) + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by node. */ + T const& operator[]( node const& n ) const + { + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + assert( has( n ) ); + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Mutable access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T& operator[]( signal const& f ) + { + auto n = ntk->get_node( f ); + assert( ntk->node_to_index( n ) < data->size() && "index out of bounds" ); + if ( !has( n ) ) + { + ( *data )[ntk->node_to_index( n )] = T(); + } + return std::get( ( *data )[ntk->node_to_index( n )] ); + } + + /*! \brief Constant access to value by signal. + * + * This method derives the node from the signal. If the node and signal type + * are the same in the network implementation, this method is disabled. + */ + template>> + T const& operator[]( signal const& f ) const + { + assert( ntk->node_to_index( ntk->get_node( f ) ) < data->size() && "index out of bounds" ); + assert( has( ntk->get_node( f ) ) ); + return std::get( ( *data )[ntk->node_to_index( ntk->get_node( f ) )] ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with the place holder (empty) element. + */ + void reset() + { + data->clear(); + data->resize( ntk->size() ); + } + + /*! \brief Resets the size of the map. + * + * This function should be called, if the network changed in size. Then, the + * map is cleared, and resized to the current network's size. All values are + * initialized with `init_value`. + * + * \param init_value Initialization value after resize + */ + void reset( T const& init_value ) + { + data->clear(); + data->resize( ntk->size(), init_value ); + } + + /*! \brief Resizes the map. + * + * This function should be called, if the node_map's size needs to + * be changed without clearing its data. + */ + void resize() + { + if ( ntk->size() > data->size() ) + { + data->resize( ntk->size() ); + } + } + +private: + Ntk const* ntk; + std::shared_ptr data; +}; + +/*! \brief Initializes a network for copying together with node map. + * + * This utility function is helpful when creating a network from another one, + * a very common task. It creates the network of type `NtkDest` and already + * creates a node map to map nodes from the source network, of type `NtkSrc` to + * nodes of the new network. The function map constant inputs and creates and + * maps primary inputs. + */ +template +std::pair, NtkSrc>> initialize_copy_network( NtkSrc const& src ) +{ + static_assert( is_network_type_v, "NtkDest is not a network type" ); + static_assert( is_network_type_v, "NtkSrc is not a network type" ); + + static_assert( has_get_constant_v, "NtkDest does not implement the get_constant method" ); + static_assert( has_create_pi_v, "NtkDest does not implement the create_pi method" ); + static_assert( has_get_constant_v, "NtkSrc does not implement the get_constant method" ); + static_assert( has_get_node_v, "NtkSrc does not implement the get_node method" ); + static_assert( has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method" ); + + node_map, NtkSrc> old2new( src ); + NtkDest dest; + old2new[src.get_constant( false )] = dest.get_constant( false ); + if ( src.get_node( src.get_constant( true ) ) != src.get_node( src.get_constant( false ) ) ) + { + old2new[src.get_constant( true )] = dest.get_constant( true ); + } + src.foreach_pi( [&]( auto const& n ) { + old2new[n] = dest.create_pi(); + } ); + return { dest, old2new }; +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/utils/null_utils.hpp b/include/mockturtle/utils/null_utils.hpp new file mode 100644 index 0000000..e031564 --- /dev/null +++ b/include/mockturtle/utils/null_utils.hpp @@ -0,0 +1,46 @@ +/* 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 null_utils.hpp + \brief Placeholder empty data structures for interfacing purposes + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +struct null_params +{ +}; +struct null_stats +{ + void report() const {} +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/progress_bar.hpp b/include/mockturtle/utils/progress_bar.hpp new file mode 100644 index 0000000..a36840c --- /dev/null +++ b/include/mockturtle/utils/progress_bar.hpp @@ -0,0 +1,178 @@ +/* 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 progress_bar.hpp + \brief Progress bar + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Prints progress bars. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + { // some block + progress_bar bar( 100, "|{0}| neg. index = {1}, index squared = {2}" ); + + for ( auto i = 0; i < 100; ++i ) + { + bar( i, -i, i * i ); + } + } // progress bar is deleted at exit of this block + \endverbatim + */ +class progress_bar +{ +public: + /*! \brief Constructor. + * + * This constructor is used when a the total number of iterations is known, + * and the progress should be visually printed. When using this constructor, + * the current iteration must be provided to the `operator()` call. + * + * \param size Number of iterations (for progress bar) + * \param fmt Format strind; used with `fmt::format`, first placeholder `{0}` + * is used for progress bar, the others for the parameters passed + * to `operator()` + * \param enable If true, output is printed, otherwise not + * \param os Output stream + */ + progress_bar( uint32_t size, std::string const& fmt, bool enable = true, std::ostream& os = std::cout ) + : _show_progress( true ), + _size( size ), + _fmt( fmt ), + _enable( enable ), + _os( os ) {} + + /*! \brief Constructor. + * + * This constructor is used when a the total number of iterations is not + * known. In this case, the progress is not visually printed. When using this + * constructor, just pass the arguments for the `fmt` string. + * + * \param fmt Format strind; used with `fmt::format`, parameters are passed + * to `operator()` + * \param enable If true, output is printed, otherwise not + * \param os Output stream + */ + progress_bar( std::string const& fmt, bool enable = true, std::ostream& os = std::cout ) + : _show_progress( false ), + _fmt( fmt ), + _enable( enable ), + _os( os ) {} + + /*! \brief Deconstructor + * + * Will remove the last printed line and restore the cursor. + */ + ~progress_bar() + { + done(); + } + + /*! \brief Prints and updates the progress bar status. + * + * This updates the progress and re-prints the progress line. The previous + * print of the line is removed. If the progress bar has a progress segment + * the first argument must be an integer indicating the current progress + * with respect to the `size` parameter passed to the constructor. + * + * \param first Progress position or first argument + * \param args Vardiadic argument pack with values for format string + */ + template + void operator()( First first, Args... args ) + { + if ( _show_progress ) + { + show_with_progress( first, args... ); + } + else + { + show_without_progress( first, args... ); + } + } + + /*! \brief Removes the progress bar. + * + * This method is automatically invoked when the progress bar is deleted. In + * some cases, one may wish to invoke this method manually. + */ + void done() + { + if ( _enable ) + { + _os << "\u001B[G" << std::string( 79, ' ' ) << "\u001B[G\u001B[?25h" << std::flush; + } + } + +private: + template + void show_with_progress( uint32_t pos, Args... args ) + { + if ( !_enable ) + return; + + int spidx = static_cast( ( 6.0 * pos ) / _size ); + _os << "\u001B[G" << fmt::format( _fmt, spinner.substr( spidx * 5, 5 ), args... ) << std::flush; + } + + template + void show_without_progress( Args... args ) + { + if ( !_enable ) + return; + + _os << "\u001B[G" << fmt::format( _fmt, args... ) << std::flush; + } + +private: + bool _show_progress; + uint32_t _size; + std::string _fmt; + bool _enable; + std::ostream& _os; + + std::string spinner{ " . .. ... .... ....." }; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/recursive_cost_functions.hpp b/include/mockturtle/utils/recursive_cost_functions.hpp new file mode 100644 index 0000000..1792cd1 --- /dev/null +++ b/include/mockturtle/utils/recursive_cost_functions.hpp @@ -0,0 +1,122 @@ +/* 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 recursive_cost_functions.hpp + \brief Various recursive cost functions for (optimization) algorithms + + \author Hanyu Wang +*/ + +#pragma once + +#include + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief (Recursive) customizable cost function + * + * To define a new cost function, you need to first specify how each node + * contributes to the total cost via the *contribution function*. Each node + * is evaluated individually and independently. + * + * If additional (global) information is required to decide a node's contribution, + * you may specify them as *context*. The content stored in the context can be + * arbitrarily defined (`context_t`), but the derivation must be recursive. In + * other words, the context of a node is derived using *context propagation function* + * which takes only the context of fanins as input. + * + * Examples of recursive cost functions can be found at: + * `mockturtle/utils/recursive_cost_functions.hpp` + */ +template +struct recursive_cost_functions +{ + using base_type = recursive_cost_functions; + using context_t = uint32_t; + /*! \brief Context propagation function + * + * Return the context of a node given fanin contexts. + */ + virtual context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const = 0; + + /*! \brief Contribution function + * + * Update the total cost using node n and its context. + */ + virtual void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const = 0; +}; + +template +struct xag_depth_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + uint32_t _cost = ntk.is_pi( n ) ? 0 : *std::max_element( std::begin( fanin_contexts ), std::end( fanin_contexts ) ) + 1; + return _cost; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost = std::max( total_cost, context ); + } +}; + +template +struct t_xag_depth_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + uint32_t _cost = ntk.is_pi( n ) ? 0 : *std::max_element( std::begin( fanin_contexts ), std::end( fanin_contexts ) ) + ntk.is_and( n ); + return _cost; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost = std::max( total_cost, context ); + } +}; + +template +struct xag_size_cost_function : recursive_cost_functions +{ +public: + using context_t = uint32_t; + context_t operator()( Ntk const& ntk, node const& n, std::vector const& fanin_contexts = {} ) const + { + return 0; + } + void operator()( Ntk const& ntk, node const& n, uint32_t& total_cost, context_t const context ) const + { + total_cost += ( !ntk.is_pi( n ) && ntk.visited( n ) != ntk.trav_id() ) ? 1 : 0; + } +}; + +} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/sop_utils.hpp b/include/mockturtle/utils/sop_utils.hpp new file mode 100644 index 0000000..4c2432c --- /dev/null +++ b/include/mockturtle/utils/sop_utils.hpp @@ -0,0 +1,594 @@ +/* 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 sop_utils.hpp + \brief Utilities for sum-of-products + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +inline bool cube_has_lit( uint64_t const cube, uint64_t const lit ) +{ + return ( cube & ( static_cast( 1 ) << lit ) ) > 0; +} + +inline uint32_t cube_count_literals( uint64_t cube ) +{ + uint32_t count; + for ( count = 0; cube; ++count ) + { + cube &= cube - 1u; + }; + return count; +} + +inline int64_t sop_literals_occurrences( std::vector const& sop, uint32_t const num_lit ) +{ + /* find the first literal which occurs more than once */ + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + unsigned occurrences = 0; + for ( auto const& cube : sop ) + { + if ( cube_has_lit( cube, lit ) ) + ++occurrences; + + if ( occurrences > 1 ) + return lit; + } + } + + /* each literal appears once */ + return -1; +} + +inline int64_t sop_least_occurrent_literal( std::vector const& sop, uint32_t const num_lit ) +{ + int64_t min_lit = -1; + uint32_t min_occurrences = UINT32_MAX; + + /* find the first literal which occurs more than once */ + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + uint32_t occurrences = 0; + for ( auto const& cube : sop ) + { + if ( cube_has_lit( cube, lit ) ) + ++occurrences; + } + + if ( occurrences > 1 && occurrences < min_occurrences ) + { + min_lit = static_cast( lit ); + min_occurrences = occurrences; + } + } + + return min_lit; +} + +inline int64_t sop_most_occurrent_literal_masked( std::vector const& sop, uint64_t const cube, uint32_t const num_lit ) +{ + int64_t max_lit = -1; + uint32_t max_occurrences = 1; + + for ( uint64_t lit = 0; lit < num_lit; ++lit ) + { + if ( !cube_has_lit( cube, lit ) ) + continue; + + uint32_t occurrences = 0; + for ( auto const& c : sop ) + { + if ( cube_has_lit( c, lit ) ) + ++occurrences; + } + + if ( occurrences > max_occurrences ) + { + max_lit = static_cast( lit ); + max_occurrences = occurrences; + } + } + + return max_lit; +} + +inline bool sop_maximal_cube_literal( std::vector const& sop, uint64_t& cube, uint32_t const lit ) +{ + uint64_t max_cube = UINT64_MAX; + uint32_t occurrences = 0; + + for ( auto const& c : sop ) + { + if ( cube_has_lit( c, lit ) ) + { + ++occurrences; + max_cube &= c; + } + } + + cube = max_cube; + return occurrences > 1; +} + +inline void sop_best_literal( std::vector const& sop, std::vector& result, uint64_t const cube, uint32_t const num_lit ) +{ + int64_t max_lit = sop_most_occurrent_literal_masked( sop, cube, num_lit ); + assert( max_lit >= 0 ); + + result.push_back( static_cast( 1 ) << max_lit ); +} + +} // namespace detail + +inline uint32_t sop_count_literals( std::vector const& sop ) +{ + uint32_t lit_count = 0; + + for ( auto const& c : sop ) + { + lit_count += detail::cube_count_literals( c ); + } + + return lit_count; +} + +/*! \brief Makes a SOP cube free + * + * This method checks for a common cube divisor in + * the SOP. If found, the SOP is divided by that cube. + * + * \param sop + */ +inline void sop_make_cube_free( std::vector& sop ) +{ + /* find common cube */ + uint64_t mask = UINT64_MAX; + for ( auto const& c : sop ) + { + mask &= c; + } + + if ( mask == 0 ) + return; + + /* make cube free */ + for ( auto& c : sop ) + { + c &= ~mask; + } +} + +/*! \brief Checks if a SOP is cube free + * + * This method checks for a common cube divisor in + * the SOP. + * + * \param sop + */ +inline bool sop_is_cube_free( std::vector const& sop ) +{ + /* find common cube */ + uint64_t mask = UINT64_MAX; + for ( auto const& c : sop ) + { + mask &= c; + } + + return mask == 0; +} + +/*! \brief Algebraic division by literal + * + * This method divides a SOP inplace by a literal and + * stores the resulting quotient in the original SOP. + * + * \param sop + * \param lit + */ +inline void sop_divide_by_literal_inplace( std::vector& sop, uint64_t const lit ) +{ + uint32_t p = 0; + for ( auto i = 0; i < sop.size(); ++i ) + { + if ( detail::cube_has_lit( sop[i], lit ) ) + { + sop[p++] = sop[i] & ( ~( static_cast( 1 ) << lit ) ); + } + } + + sop.resize( p ); +} + +/*! \brief Algebraic division by a cube + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient and reminder. + * + * \param Divident + * \param Divisor + * \param Quotient + * \param Reminder + */ +inline void sop_divide_by_cube( std::vector const& divident, std::vector const& divisor, std::vector& quotient, std::vector& reminder ) +{ + assert( divisor.size() == 1 ); + + quotient.clear(); + reminder.clear(); + + uint32_t p = 0; + for ( auto const& c : divident ) + { + if ( ( c & divisor[0] ) == divisor[0] ) + { + quotient.push_back( c & ( ~divisor[0] ) ); + } + else + { + reminder.push_back( c ); + } + } +} + +/*! \brief Algebraic division by a cube + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient. + * + * \param Divident + * \param Divisor + * \param Quotient + */ +inline void sop_divide_by_cube_no_reminder( std::vector const& divident, uint64_t const& divisor, std::vector& quotient ) +{ + quotient.clear(); + + uint32_t p = 0; + for ( auto const& c : divident ) + { + if ( ( c & divisor ) == divisor ) + { + quotient.push_back( c & ~divisor ); + } + } +} + +/*! \brief Algebraic division + * + * This method divides a SOP (divident) by the divisor + * and stores the resulting quotient and reminder. + * + * \param Divident + * \param Divisor + * \param Quotient + * \param Reminder + */ +inline void sop_divide( std::vector& divident, std::vector const& divisor, std::vector& quotient, std::vector& reminder ) +{ + /* divisor contains a single cube */ + if ( divisor.size() == 1 ) + { + sop_divide_by_cube( divident, divisor, quotient, reminder ); + return; + } + + quotient.clear(); + reminder.clear(); + + /* perform division */ + for ( auto i = 0; i < divident.size(); ++i ) + { + auto const c = divident[i]; + + /* cube has been already covered */ + if ( detail::cube_has_lit( c, 63 ) ) + continue; + + uint32_t div_i; + for ( div_i = 0u; div_i < divisor.size(); ++div_i ) + { + if ( ( c & divisor[div_i] ) == divisor[div_i] ) + break; + } + + /* cube is not divisible -> reminder */ + if ( div_i >= divisor.size() ) + continue; + + /* extract quotient */ + uint64_t c_quotient = c & ~divisor[div_i]; + + /* find if c_quotient can be obtained for all the divisors */ + bool found = true; + for ( auto const& div : divisor ) + { + if ( div == divisor[div_i] ) + continue; + + found = false; + for ( auto const& c2 : divident ) + { + /* cube has been already covered */ + if ( detail::cube_has_lit( c2, 63 ) ) + continue; + + if ( ( ( c2 & div ) == div ) && ( c_quotient == ( c2 & ~div ) ) ) + { + found = true; + break; + } + } + + if ( !found ) + break; + } + + if ( !found ) + continue; + + /* valid divisor, select covered cubes */ + quotient.push_back( c_quotient ); + + divident[i] |= static_cast( 1 ) << 63; + for ( auto const& div : divisor ) + { + if ( div == divisor[div_i] ) + continue; + + for ( auto& c2 : divident ) + { + /* cube has been already covered */ + if ( detail::cube_has_lit( c2, 63 ) ) + continue; + + if ( ( ( c2 & div ) == div ) && ( c_quotient == ( c2 & ~div ) ) ) + { + c2 |= static_cast( 1 ) << 63; + break; + } + } + } + } + + /* add remainder */ + for ( auto& c : divident ) + { + if ( !detail::cube_has_lit( c, 63 ) ) + { + reminder.push_back( c ); + } + else + { + /* unmark */ + c &= ~( static_cast( 1 ) << 63 ); + } + } +} + +/*! \brief Extracts all the kernels + * + * This method is used to identify and collect all + * the kernels. + * + * \param sop + * \param kernels + * \param j + * \param num_lit + */ +inline void sop_kernels_rec( std::vector const& sop, std::vector>& kernels, uint32_t const j, uint32_t const num_lit ) +{ + std::vector kernel; + + for ( uint32_t i = j; i < num_lit; ++i ) + { + uint64_t c; + if ( detail::sop_maximal_cube_literal( sop, c, i ) ) + { + /* cube has been visited already */ + if ( ( c & ( ( static_cast( 1 ) << i ) - 1 ) ) > 0u ) + continue; + + sop_divide_by_cube_no_reminder( sop, c, kernel ); + sop_kernels_rec( kernel, kernels, i + 1, num_lit ); + } + } + + kernels.push_back( sop ); +} + +/*! \brief Extracts the best factorizing kernel + * + * This method is used to identify the best kernel + * according to the algebraic factorization value. + * + * \param sop + * \param kernel + * \param best_kernel + * \param j + * \param best_cost + * \param num_lit + */ +inline uint32_t sop_best_kernel_rec( std::vector& sop, std::vector& kernel, std::vector& best_kernel, uint32_t const j, uint32_t& best_cost, uint32_t const num_lit ) +{ + std::vector new_kernel; + std::vector quotient; + std::vector reminder; + + /* evaluate kernel */ + sop_divide( sop, kernel, quotient, reminder ); + uint32_t division_cost = sop_count_literals( quotient ) + sop_count_literals( reminder ); + uint32_t best_fact_cost = sop_count_literals( kernel ); + + for ( uint32_t i = j; i < num_lit; ++i ) + { + uint64_t c; + if ( detail::sop_maximal_cube_literal( kernel, c, i ) ) + { + /* cube has been already visited */ + if ( ( c & ( ( static_cast( 1 ) << i ) - 1 ) ) > 0u ) + continue; + + /* extract the new kernel */ + sop_divide_by_cube( kernel, { c }, new_kernel, reminder ); + uint32_t fact_cost_rec = detail::cube_count_literals( c ) + sop_count_literals( reminder ); + uint32_t fact_cost = sop_best_kernel_rec( sop, new_kernel, best_kernel, i + 1, best_cost, num_lit ); + + /* compute the factorization value for kernel */ + if ( ( fact_cost + fact_cost_rec ) < best_fact_cost ) + best_fact_cost = fact_cost + fact_cost_rec; + } + } + + if ( best_kernel.empty() || ( division_cost + best_fact_cost ) < best_cost ) + { + best_kernel = kernel; + best_cost = division_cost + best_fact_cost; + } + + return best_fact_cost; +} + +/*! \brief Extracts a one level-0 kernel + * + * This method is used to identify and return a one + * level-0 kernel. + * + * \param sop + * \param num_lit + */ +inline void sop_one_level_zero_kernel_rec( std::vector& sop, uint32_t const num_lit ) +{ + /* find least occurring leteral which occurs more than once. TODO: test other metrics */ + int64_t min_lit = detail::sop_least_occurrent_literal( sop, num_lit ); + + if ( min_lit == -1 ) + return; + + sop_divide_by_literal_inplace( sop, static_cast( min_lit ) ); + sop_make_cube_free( sop ); + + sop_one_level_zero_kernel_rec( sop, num_lit ); +} + +/*! \brief Finds a quick divisor for a SOP + * + * This method is used to identify and return a quick + * divisor for the SOP. + * + * \param sop + * \param num_lit + */ +inline bool sop_quick_divisor( std::vector const& sop, std::vector& res, uint32_t const num_lit ) +{ + if ( sop.size() <= 1 ) + return false; + + /* each literal appears no more than once */ + if ( detail::sop_literals_occurrences( sop, num_lit ) < 0 ) + return false; + + /* one level 0-kernel */ + res = sop; + sop_one_level_zero_kernel_rec( res, num_lit ); + + assert( res.size() ); + return true; +} + +/*! \brief Finds a good divisor for a SOP + * + * This method is used to identify and return a good + * divisor for the SOP. + * + * \param sop + * \param num_lit + */ +inline bool sop_good_divisor( std::vector& sop, std::vector& res, uint32_t const num_lit ) +{ + if ( sop.size() <= 1 ) + return false; + + /* each literal appears no more than once */ + if ( detail::sop_literals_occurrences( sop, num_lit ) < 0 ) + return false; + + std::vector kernel = sop; + + /* compute all the kernels and return the one with the best factorization value */ + uint32_t best_cost = 0; + sop_best_kernel_rec( sop, kernel, res, 0, best_cost, num_lit ); + + return true; +} + +/*! \brief Translates cubes into products + * + * This method translate SOP of kitty::cubes (bits + mask) + * into SOP of products represented by literals. + * Example for: ab'c* + * - cube: _bits = 1010; _mask = 1110 + * - product: 10011000 + * + * \param cubes Sum-of-products described using cubes + * \param num_vars Number of variables + */ +inline std::vector cubes_to_sop( std::vector const& cubes, uint32_t const num_vars ) +{ + using sop_t = std::vector; + + sop_t sop( cubes.size() ); + + /* Represent literals instead of variables as a a' b b' */ + /* Bit 63 is reserved, up to 31 varibles support */ + auto it = sop.begin(); + for ( auto const& c : cubes ) + { + uint64_t& product = *it++; + for ( auto i = 0; i < num_vars; ++i ) + { + if ( c.get_mask( i ) ) + product |= static_cast( 1 ) << ( 2 * i + static_cast( c.get_bit( i ) ) ); + } + } + + return sop; +} + +} // namespace mockturtle diff --git a/include/mockturtle/utils/standard_cell.hpp b/include/mockturtle/utils/standard_cell.hpp new file mode 100644 index 0000000..917550d --- /dev/null +++ b/include/mockturtle/utils/standard_cell.hpp @@ -0,0 +1,98 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file standard_cell.hpp + \brief Defines logic cells. + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include + +#include "../io/genlib_reader.hpp" + +namespace mockturtle +{ + +struct standard_cell +{ + /* Unique name */ + std::string name; + + /* Unique ID */ + uint32_t id; + + /* Pointer to a gate representing each individual output */ + std::vector gates; + + /* Area */ + double area; +}; + +/*! \brief Reconstruct standard cells from GENLIB gates. + * + * This function returns a vector of standard cells given + * GENLIB gates. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( in, genlib_reader( gates ) ); + + // Extract standard cells + std::vector cells = get_standard_cells( gates ); + \endverbatim + */ +inline std::vector get_standard_cells( std::vector const& gates ) +{ + std::unordered_map name_to_index; + std::vector cells; + + for ( gate const& g : gates ) + { + if ( auto it = name_to_index.find( g.name ); it != name_to_index.end() ) + { + /* add to existing cell (multi-output) */ + cells[it->second].gates.push_back( g ); + } + else + { + name_to_index[g.name] = cells.size(); + cells.emplace_back( standard_cell{ g.name, static_cast( cells.size() ), { g }, g.area } ); + } + } + + return cells; +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/stopwatch.hpp b/include/mockturtle/utils/stopwatch.hpp new file mode 100644 index 0000000..5f1daac --- /dev/null +++ b/include/mockturtle/utils/stopwatch.hpp @@ -0,0 +1,180 @@ +/* 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 stopwatch.hpp + \brief Stopwatch + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Stopwatch interface + * + * This class implements a stopwatch interface to track time. It starts + * tracking time at construction and stops tracking time at deletion + * automatically. A reference to a duration object is passed to the + * constructor. After stopping the time the measured time interval is added + * to the durationr reference. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + { // some block + stopwatch t( time ); + + // do some work + } // stopwatch is stopped here + + std::cout << fmt::format( "{:5.2f} seconds passed\n", to_seconds( time ) ); + \endverbatim + */ +template +class stopwatch +{ +public: + using clock = Clock; + using duration = typename Clock::duration; + using time_point = typename Clock::time_point; + + /*! \brief Default constructor. + * + * Starts tracking time. + */ + explicit stopwatch( duration& dur ) + : dur( dur ), + beg( clock::now() ) + { + } + + /*! \brief Default deconstructor. + * + * Stops tracking time and updates duration. + */ + ~stopwatch() + { + dur += ( clock::now() - beg ); + } + +private: + duration& dur; + time_point beg; +}; + +/*! \brief Calls a function and tracks time. + * + * The function that is passed as second parameter can be any callable object + * that takes no parameters. This construction can be used to avoid + * pre-declaring the result type of a computation that should be tracked. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + auto result = call_with_stopwatch( time, [&]() { return function( parameters ); } ); + \endverbatim + * + * \param dur Duration reference (time will be added to it) + * \param fn Callable object with no arguments + */ +template +std::invoke_result_t call_with_stopwatch( typename Clock::duration& dur, Fn&& fn ) +{ + stopwatch t( dur ); + return fn(); +} + +/*! \brief Constructs an object and calls time. + * + * This function can track the time for the construction of an object and + * returns the constructed object. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + stopwatch<>::duration time{0}; + + // create vector with 100000 elements initialized to 42 + auto result = make_with_stopwatch>( time, 100000, 42 ); + \endverbatim + */ +template +T make_with_stopwatch( typename Clock::duration& dur, Args... args ) +{ + stopwatch t( dur ); + return T{ std::forward( args )... }; +} + +/*! \brief Utility function to convert duration into seconds. */ +template +inline double to_seconds( Duration const& dur ) +{ + return std::chrono::duration_cast>( dur ).count(); +} + +template +class print_time +{ +public: + print_time() + : _t( new stopwatch( _d ) ) + { + } + + ~print_time() + { + delete _t; + std::cout << fmt::format( "[i] run-time: {:5.2f} secs\n", to_seconds( _d ) ); + } + +private: + stopwatch* _t{ nullptr }; + typename stopwatch::duration _d{}; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/string_utils.hpp b/include/mockturtle/utils/string_utils.hpp new file mode 100644 index 0000000..af7101e --- /dev/null +++ b/include/mockturtle/utils/string_utils.hpp @@ -0,0 +1,63 @@ +/* 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 string_utils.hpp + \brief String utils + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +namespace mockturtle +{ + +template +std::invoke_result_t map_and_join( Iterator begin, Iterator end, MapFn&& map_fn, JoinFn&& join_fn ) +{ + if constexpr ( std::is_same_v, std::string> ) + { + return std::accumulate( begin + 1, end, map_fn( *begin ), + [&]( auto const& a, auto const& v ) { + return a + join_fn + map_fn( v ); + } ); + } + else + { + return std::accumulate( begin + 1, end, map_fn( *begin ), + [&]( auto const& a, auto const& v ) { + return join_fn( a, map_fn( v ) ); + } ); + } +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/struct_library.hpp b/include/mockturtle/utils/struct_library.hpp new file mode 100644 index 0000000..165e9eb --- /dev/null +++ b/include/mockturtle/utils/struct_library.hpp @@ -0,0 +1,1542 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file struct_library.hpp + \brief Implements utilities for structural matching + + \author Alessandro Tempia Calvino + \author Gianluca Radi +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "../io/genlib_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct struct_library_params +{ + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Reports loaded gates */ + bool verbose{ false }; +}; + +/*! \brief Library of gates for structural matching + * + * This class creates a technology library from a set + * of input gates. + * + * Gates are processed to derive rules in the AIG format. + * Then, every rule and subrule gets a unique id and the AND table is built. + * Every gate gets a unique label comprehensive of its rule id and whether it is positive or negative. + * + * The template parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * By default, `struct_library` is used in `tech_library` when NInputs is greater than 6. + * + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // struct library + mockturtle::struct_library lib( gates ); + \endverbatim + */ +template +class struct_library +{ +public: + enum class node_type + { + none, + zero_, + pi_, + and_, + or_, + mux_, + xor_ + }; + + struct signal + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + + bool operator==( signal const& other ) const + { + return data == other.data; + } + }; + + /* struct for representing nodes in dsd decomposition */ + struct dsd_node + { + node_type type; + + uint32_t index; + + std::vector fanin = {}; + }; + + /* struct for labels to assign to gates */ + struct label + { + union + { + struct + { + uint32_t inv : 1; + uint32_t index : 31; + }; + uint32_t data; + }; + bool operator==( label const& other ) const + { + return data == other.data; + } + }; + + struct signal_hash + { + std::size_t operator()( signal const& s ) const noexcept + { + return std::hash{}( s.data ); + } + }; + + struct tuple_s_hash + { + std::size_t operator()( std::tuple const& t ) const noexcept + { + size_t h1 = signal_hash()( std::get<0>( t ) ); + size_t h2 = signal_hash()( std::get<1>( t ) ); + return (uint64_t)h1 ^ ( ( (uint64_t)h2 ) << 32 ); // or use boost::hash_combine + } + }; + +private: + static constexpr uint32_t invalid_index = UINT32_MAX; + using supergates_list_t = std::vector>; + using composed_list_t = std::vector>; + using lib_rule = phmap::flat_hash_map, kitty::hash>; + using rule = std::vector; + using lib_table = phmap::flat_hash_map, uint32_t, tuple_s_hash>; + using map_label_gate = std::unordered_map; + +public: + explicit struct_library( std::vector const& gates, struct_library_params const& ps = {} ) + : _gates( gates ), + _ps( ps ), + _supergates(), + _dsd_map(), + _and_table(), + _label_to_gate() + {} + +public: + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + * Variable `min_vars` defines the minimum number of + * gate inputs considered for the library creation. + * 0 < min_vars < UINT32_MAX + */ + void construct( uint32_t min_vars = 2u ) + { + generate_library( min_vars ); + } + + /*! \brief Construct the structural library. + * + * Generates the patterns for structural matching. + */ + const map_label_gate& get_struct_library() const + { + return _label_to_gate; + } + + /*! \brief Get the pattern ID. + * + * \param id1 first pattern id. + * \param id2 second pattern id. + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + const uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + signal l, r; + l.data = id1; + /* ignore input negations */ + if ( l.data == 3 ) + l.data = 2; + r.data = id2; + if ( r.data == 3 ) + r.data = 2; + std::tuple key; + if ( l.index <= r.index ) + key = std::make_tuple( l, r ); + else + key = std::make_tuple( r, l ); + auto match = _and_table.find( key ); + if ( match != _and_table.end() ) + return match->second; + return UINT32_MAX; + } + + /*! \brief Get the gates matching the pattern ID. + * + * Returns a list of gates that match the pattern ID. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + auto match = _label_to_gate.find( ( id << 1 ) | ( phase ? 1 : 0 ) ); + if ( match != _label_to_gate.end() ) + { + return &( match->second ); + } + return nullptr; + } + + /*! \brief Returns the number of large gates. + * + * Number of gates with more than 6 inputs. + */ + const uint32_t get_num_large_gates() const + { + return num_large_gates; + } + + /*! \brief Print and table. + * + */ + void print_and_table() + { + for ( auto elem : _and_table ) + { + auto first0 = std::get<0>( elem.first ); + auto first1 = std::get<1>( elem.first ); + std::cout << "<" << ( first0.inv ? "!" : "" ) << first0.index; + std::cout << ", " << ( first1.inv ? "!" : "" ) << first1.index << "> "; + std::cout << elem.second << "\n"; + } + } + +private: + void generate_library( uint32_t min_vars ) + { + /* select and load gates */ + _supergates.reserve( _gates.size() ); + generate_composed_gates(); + + /* mark dominate gates */ + std::vector skip_gates( _supergates.size(), false ); + filter_gates( skip_gates ); + + std::vector indexes( _supergates.size() ); + std::iota( indexes.begin(), indexes.end(), 0 ); + uint32_t max_label = 1; + uint32_t gate_pol = 0; // polarity of AND equivalent gate + uint32_t shift = 0; + + /* sort cells by increasing order of area */ + std::stable_sort( indexes.begin(), indexes.end(), + [&]( auto const& a, auto const& b ) -> bool { + return _supergates[a].area < _supergates[b].area; + } ); + + for ( uint32_t const ind : indexes ) + { + composed_gate const& gate = _supergates[ind]; + + if ( gate.num_vars < 2 || skip_gates[ind] ) + continue; + + /* DSD decomposition */ + rule rule = {}; + std::vector support = {}; + for ( uint32_t i = 0; i < gate.num_vars; i++ ) + { + rule.push_back( { node_type::pi_, i, {} } ); + support.push_back( i ); + } + auto cpy = gate.function; + gate_disjoint = false; + compute_dsd( cpy, support, rule ); + + /* ignore gates with reconvergence */ + if ( gate_disjoint ) + continue; + + if ( gate.num_vars > 6 ) + { + ++num_large_gates; + } + + _dsd_map.insert( { gate.function, rule } ); + if ( _ps.verbose ) + { + std::cout << "Dsd:\n"; + print_rule( rule, rule[rule.size() - 1] ); + } + + /* Aig conversion */ + auto aig_rule = map_to_aig( rule ); + if ( _ps.verbose ) + { + std::cout << "\nAig:\n"; + print_rule( aig_rule, aig_rule[aig_rule.size() - 1] ); + } + + /* Rules derivation */ + std::vector> der_rules = {}; + der_rules.push_back( aig_rule ); + std::vector> depths = { { get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[0].index] ), get_depth( aig_rule, aig_rule[aig_rule[aig_rule.size() - 1].fanin[1].index] ) } }; + create_rules_from_dsd( der_rules, aig_rule, aig_rule[aig_rule.size() - 1], depths, true, true ); + if ( _ps.verbose ) + { + std::cout << "\nDerived:\n"; + } + + /* Indexing of rules and subrules, and_table construction, and gates' label assignement */ + for ( auto elem : der_rules ) + { + gate_pol = 0; + shift = 0; + std::vector perm( gate.num_vars ); + auto index_rule = do_indexing_rule( elem, elem[elem.size() - 1], max_label, gate_pol, perm, shift ); + + /* skip gate creation for small gates (<`min_vars` inputs) */ + if ( gate.num_vars < min_vars ) + continue; + + supergate sg = { &gate, + static_cast( gate.area ), + gate.tdelay, + perm, + gate_pol }; + + /* permute pin-to-pin delays */ + for ( uint32_t i = 0; i < gate.num_vars; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + auto& v = _label_to_gate[index_rule.data]; + + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + v.insert( it, sg ); + + if ( _ps.verbose ) + { + print_rule( elem, elem[elem.size() - 1] ); + std::cout << "\n"; + for ( const std::pair& elem : _label_to_gate ) + { + std::cout << elem.first << "\n"; + for ( auto sg : elem.second ) + { + std::cout << ( sg.root )->root->expression << "\n"; + } + } + } + } + + if ( _ps.verbose ) + { + std::cout << "\n"; + std::cout << "And table:\n"; + print_and_table(); + std::cout << "\n"; + } + } + if ( _ps.verbose ) + std::cout << "\n"; + } + + void generate_composed_gates() + { + /* filter multi-output gates */ + std::unordered_map multioutput_map; + multioutput_map.reserve( _gates.size() ); + + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + /* filter large gates and multi-output gates */ + if ( g.function.num_vars() > NInputs || multioutput_map[g.name] > 1 ) + { + ++ignored; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::vector& skip_gates ) + { + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( _supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = _supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = _supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( _supergates[i], _supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = _supergates[i].area < _supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[i].tdelay[k] > _supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( _supergates[j].tdelay[k] > _supergates[i].tdelay[k] ) + faster = false; + } + + if ( !smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + + uint32_t try_top_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i = 0; + for ( ; i < num_vars; i++ ) + { + auto res = is_top_dec( tt, i, false ); + if ( res.type != node_type::none ) + break; + } + return i; + } + + dsd_node do_top_dec( kitty::dynamic_truth_table& tt, uint32_t index, std::vector mapped_support ) + { + auto node = is_top_dec( tt, index, false, &tt ); + + node.fanin[0].index = mapped_support[index]; + return node; + } + + std::tuple try_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t num_vars ) + { + uint32_t i; + uint32_t j; + dsd_node res; + for ( i = 0; i < num_vars; i++ ) + { + for ( j = i + 1; j < num_vars; j++ ) + { + res = is_bottom_dec( tt, i, j ); + if ( res.type != node_type::none ) + break; + } + if ( res.type != node_type::none ) + break; + } + std::tuple ret = { i, j }; + return ret; + } + + dsd_node do_bottom_dec( kitty::dynamic_truth_table& tt, uint32_t i, uint32_t j, uint32_t new_index, std::vector& mapped_support ) + { + auto node = is_bottom_dec( tt, i, j, &tt, new_index, false ); + + node.fanin[0].index = mapped_support[i]; + node.fanin[1].index = mapped_support[j]; + + mapped_support[i] = node.index; + return node; + } + + dsd_node do_shannon_dec( kitty::dynamic_truth_table tt, uint32_t index, kitty::dynamic_truth_table& co0, kitty::dynamic_truth_table& co1, std::vector mapped_support ) + { + auto node = shannon_dec( tt, index, &co0, &co1 ); + node.fanin[0].index = mapped_support[index]; + return node; + } + + void update_support( std::vector& v, uint32_t index ) + { + uint32_t i = 0; + for ( ; i < v.size() && i < index; i++ ) + ; + + for ( ; i < v.size() - 1; i++ ) + { + v[i] = v[i + 1]; + } + + v.pop_back(); + } + + template + void min_base_shrink( TT& tt, TT& tt_shr ) + { + kitty::min_base_inplace( tt ); + kitty::shrink_to_inplace( tt_shr, tt ); + } + + uint32_t is_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == var ) + { + return i; + } + } + return invalid_index; + } + + uint32_t is_inv_PI( kitty::dynamic_truth_table const& rem, uint32_t n_vars ) + { + for ( uint32_t i = 0; i < n_vars; i++ ) + { + auto var = rem.construct(); + kitty::create_nth_var( var, i ); + if ( rem == ~var ) + { + return i; + } + } + return invalid_index; + } + + void update_found_rule( kitty::dynamic_truth_table& tt, std::vector& mapped_support, std::vector& rule ) + { + uint32_t count_old = 0; + uint32_t count_curr = 0; + std::vector new_rule; + auto found_rule = get_rules( tt ); + std::copy_if( found_rule.begin(), found_rule.end(), std::back_inserter( new_rule ), []( dsd_node n ) { + return ( n.type != node_type::pi_ ); + } ); + for_each( found_rule.begin(), found_rule.end(), [&]( dsd_node elem ) { + if ( elem.type == node_type::pi_ ) + count_old++; + } ); + for_each( rule.begin(), rule.end(), [&]( dsd_node elem ) { + count_curr++; + } ); + /* update index of node */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + return { n.type, n.index + count_curr - count_old, n.fanin }; + } ); + /* update index of signal of fanins of nodes */ + std::transform( new_rule.begin(), new_rule.end(), new_rule.begin(), [&]( dsd_node& n ) -> dsd_node { + transform( n.fanin.begin(), n.fanin.end(), n.fanin.begin(), [&]( signal s ) -> signal { + if ( s.index >= count_old ) + return { s.inv, s.index + count_curr - count_old }; + else + return { s.inv, mapped_support[s.index] }; + } ); + return { n.type, n.index, n.fanin }; + } ); + rule.insert( rule.end(), new_rule.begin(), new_rule.end() ); + } + + /*! \brief Compute DSD decomposition for a boolean function recursively. + * + * \param tt dynamic truth table representing the function. + * \param mapped_support vector indicating function's support at every recursive step. + * \param rule DSD decomposition of the function. + * Returns index of dsd_node to add to rule. + */ + uint32_t compute_dsd( kitty::dynamic_truth_table& tt, std::vector mapped_support, std::vector& rule ) + { + /* Function has been already found */ + if ( !get_rules( tt ).empty() ) + { + update_found_rule( tt, mapped_support, rule ); + return rule.size() - 1; + } + /* try top decomposition */ + uint32_t i = try_top_dec( tt, tt.num_vars() ); + if ( i < tt.num_vars() ) // it was top decomposable + { + auto res = do_top_dec( tt, i, mapped_support ); + + update_support( mapped_support, i ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + if ( is_PI( tt_shr, tt_shr.num_vars() ) == invalid_index && is_inv_PI( tt_shr, tt_shr.num_vars() ) == invalid_index ) // check if remainder is PI + { + res.fanin.push_back( { 0, compute_dsd( tt_shr, mapped_support, rule ) } ); + } + else + { + if ( is_PI( tt_shr, tt_shr.num_vars() ) != invalid_index ) + { + res.fanin.push_back( { 0, mapped_support[is_PI( tt_shr, tt_shr.num_vars() )] } ); + } + else + { + res.fanin.push_back( { 1, mapped_support[is_inv_PI( tt_shr, tt_shr.num_vars() )] } ); + } + } + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + else /* try bottom decomposition */ + { + auto couple = try_bottom_dec( tt, tt.num_vars() ); + i = std::get<0>( couple ); + uint32_t j = std::get<1>( couple ); + + if ( i < tt.num_vars() ) // it was bottom decomposable + { + auto res = do_bottom_dec( tt, i, j, rule.size(), mapped_support ); + rule.push_back( res ); + + update_support( mapped_support, j ); + + kitty::dynamic_truth_table tt_shr( tt.num_vars() - 1 ); + min_base_shrink( tt, tt_shr ); + + return compute_dsd( tt_shr, mapped_support, rule ); + } + else /* do shannon decomposition */ + { + kitty::dynamic_truth_table co0( tt.num_vars() ); + kitty::dynamic_truth_table co1( tt.num_vars() ); + kitty::dynamic_truth_table co0_shr( tt.num_vars() - 1 ); + kitty::dynamic_truth_table co1_shr( tt.num_vars() - 1 ); + + uint32_t index = find_unate_var( tt ); + + auto res = do_shannon_dec( tt, index, co0, co1, mapped_support ); + + /* check for reconvergence */ + gate_disjoint = true; + + uint32_t inv_var_co1 = is_inv_PI( co1, co1.num_vars() ); + uint32_t var_co1 = is_PI( co1, co1.num_vars() ); + uint32_t inv_var_co0 = is_inv_PI( co0, co0.num_vars() ); + uint32_t var_co0 = is_PI( co0, co0.num_vars() ); + + update_support( mapped_support, index ); + + if ( inv_var_co1 == invalid_index && var_co1 == invalid_index ) // check if co1 is PI + { + min_base_shrink( co1, co1_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co1_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co1 != invalid_index ) + { + uint32_t map_inv_var_co1 = mapped_support[inv_var_co1]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co1 } ); + } + else + { + uint32_t map_var_co1 = mapped_support[var_co1]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co1 } ); + } + } + + if ( inv_var_co0 == invalid_index && var_co0 == invalid_index ) // check if co0 is PI + { + min_base_shrink( co0, co0_shr ); + res.fanin.insert( res.fanin.begin(), { 0, compute_dsd( co0_shr, mapped_support, rule ) } ); + } + else + { + if ( inv_var_co0 != invalid_index ) + { + uint32_t map_inv_var_co0 = mapped_support[inv_var_co0]; + res.fanin.insert( res.fanin.begin(), { 1, map_inv_var_co0 } ); + } + else + { + uint32_t map_var_co0 = mapped_support[var_co0]; + res.fanin.insert( res.fanin.begin(), { 0, map_var_co0 } ); + } + } + + res.index = rule.size(); + rule.push_back( res ); + + return res.index; + } + } + } + + rule get_rules( kitty::dynamic_truth_table const& tt ) + { + auto match = _dsd_map.find( tt ); + if ( match != _dsd_map.end() ) + return match->second; + return {}; + } + + dsd_node* get_father( rule& rule, dsd_node& node ) + { + for ( uint32_t i = 0; i < rule.size(); i++ ) + { + if ( rule[i].type != node_type::pi_ && rule[i].type != node_type::zero_ && ( rule[i].fanin[0].index == node.index || rule[i].fanin[1].index == node.index ) ) + return &rule[i]; + } + return nullptr; + } + + dsd_node* find_node( rule& r, uint32_t i ) + { + for ( uint32_t j = 0; j < r.size(); j++ ) + { + if ( r[j].index == i ) + return &r[j]; + } + return nullptr; + } + + /*! \brief Convert rule derived from DSD decomposition into aig format. + * + * \param r rule to convert. + * Returns rule converted into aig format. + */ + rule map_to_aig( rule& r ) + { + std::vector rule( r ); + std::vector aig_rule; + + std::transform( rule.begin(), rule.end(), rule.begin(), []( dsd_node n ) -> dsd_node { + for ( auto& s : n.fanin ) + { + s.index += 1; + } + return { n.type, n.index + 1, n.fanin }; + } ); + + rule.insert( rule.begin(), { node_type::zero_, 0, {} } ); + + for ( typename std::vector::reverse_iterator i = rule.rbegin(); i != rule.rend(); ++i ) + { + dsd_node n = *i; + dsd_node new_node; + + if ( n.type == node_type::and_ || n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + new_node = n; + } + else if ( n.type == node_type::or_ ) + { + new_node = { node_type::and_, n.index, { { ~n.fanin[0].inv, n.fanin[0].index }, { ~n.fanin[1].inv, n.fanin[1].index } } }; + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() ), { { 1, 0 }, { 1, n.index } } }; + aig_rule.insert( aig_rule.begin(), new_root ); + } + } + else if ( n.type == node_type::mux_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() ) + 1 } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[2].index }, { n.fanin[1].inv, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[2].index }, { n.fanin[0].inv, n.fanin[0].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + else if ( n.type == node_type::xor_ ) + { + if ( get_father( rule, n ) != nullptr ) + { + dsd_node* father = find_node( aig_rule, get_father( rule, n )->index ); + if ( father->fanin[0].index == n.index ) + { + father->fanin[0].inv = ~father->fanin[0].inv; + } + else + { + father->fanin[1].inv = ~father->fanin[1].inv; + } + dsd_node node_or = { node_type::and_, n.index + 2, { { 1, n.index }, { 1, n.index + 1 } } }; + dsd_node node_and1 = { node_type::and_, n.index + 1, { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, n.index, { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + // node already in aig_rule must have index and fanin index update (index += 2, fanin_index -> (>= n.index -> +2; nothing)) + for ( auto& elem : aig_rule ) + { + elem.index += 2; + for ( auto& s : elem.fanin ) + { + if ( s.index >= n.index ) + s.index += 2; + } + } + + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + else // it is root + { + dsd_node new_root = { node_type::and_, static_cast( rule.size() + 2 ), { { 1, 0 }, { 1, static_cast( rule.size() + 1 ) } } }; + dsd_node node_or = { node_type::and_, static_cast( rule.size() + 1 ), { { 1, static_cast( rule.size() - 1 ) }, { 1, static_cast( rule.size() ) } } }; + dsd_node node_and1 = { node_type::and_, static_cast( rule.size() ), { { 0, n.fanin[0].index }, { 1, n.fanin[1].index } } }; + new_node = { node_type::and_, static_cast( rule.size() - 1 ), { { 1, n.fanin[0].index }, { 0, n.fanin[1].index } } }; // and0_node + + aig_rule.insert( aig_rule.begin(), new_root ); + aig_rule.insert( aig_rule.begin(), node_or ); + aig_rule.insert( aig_rule.begin(), node_and1 ); + } + } + aig_rule.insert( aig_rule.begin(), new_node ); + } + return aig_rule; + } + + void swap( rule& rule, dsd_node* node_i, dsd_node* node_j ) + { + auto i = node_i->index; + auto j = node_j->index; + node_i->index = j; + node_j->index = i; + std::swap( rule[i], rule[j] ); + } + + /* makes left or right move to derive a new rule */ + void make_move( rule& rule, dsd_node* target, dsd_node* r, uint8_t left ) + { + auto targ_index = 0 + left; + auto r_index = 1 - targ_index; + auto temp_index = target->fanin[targ_index].index; + auto temp_inv = target->fanin[targ_index].inv; + // swap position internal to rule of the two elements + swap( rule, target, r ); + auto temp = target; + target = r; + r = temp; + + // adjust children + target->fanin[targ_index].index = r->index; + target->fanin[targ_index].inv = 0; + r->fanin[r_index].index = temp_index; + r->fanin[r_index].inv = temp_inv; + } + + /* checks whether a rule with the same left depth or right depth has already been encountered */ + bool check_depths( rule rule, dsd_node root, std::vector> depth_branches, uint32_t left ) // for left = 1 check if left move is possible + { + auto left_node = rule[root.fanin[0].index]; + auto right_node = rule[root.fanin[1].index]; + auto left_depth = get_depth( rule, left_node ); + auto right_depth = get_depth( rule, right_node ); + auto left_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth - 1, right_depth + 1 } ); + auto left_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth + 1, left_depth - 1 } ); + auto right_it1 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ left_depth + 1, right_depth - 1 } ); + auto right_it2 = std::find( depth_branches.begin(), depth_branches.end(), std::tuple{ right_depth - 1, left_depth + 1 } ); + if ( left ) + return left_it1 == depth_branches.end() && left_it2 == depth_branches.end(); + else + return right_it1 == depth_branches.end() && right_it2 == depth_branches.end(); + } + + /*! \brief Recursively create new rules from the given one. + * For every dsd node of the original rule, left and right moves are tried. + * Then, the same algorithm is applied to all derived rules. + * The algorithm stops if no new acceptable rules can be found. + * A new rule is acceptable if no other rule with the same left and right depths has been found. + * \param new_rules vector of derived rules. + * \param rule original rule. + * \param start_node node of rule on which we try the right and left moves. + * \param depth_branches vector of encountered left and right depths. + * \param can_left specifies if we can perform a left move. + * \param can_right specifies if we can perform a right move. + */ + void create_rules_from_dsd( std::vector& new_rules, rule rule, dsd_node start_node, std::vector>& depth_branches, bool can_left, bool can_right ) + { + if ( start_node.type == node_type::pi_ || start_node.type == node_type::zero_ ) // if you cannot produce new rules or you are a PI return + return; + + std::vector left_rule( rule ); + std::vector right_rule( rule ); + std::vector> next_depths = {}; + auto left_node = &left_rule[start_node.fanin[0].index]; + auto right_node = &right_rule[start_node.fanin[1].index]; + bool new_left = false; + bool new_right = false; + + std::tuple depths = { get_depth( rule, *left_node ), get_depth( rule, *right_node ) }; + depth_branches.push_back( depths ); + + /* left move */ + if ( can_left && left_node->type == start_node.type && start_node.fanin[0].inv == 0 && check_depths( rule, start_node, depth_branches, 1 ) ) + { + auto r = &left_rule[start_node.index]; + + make_move( left_rule, left_node, r, 1 ); + + new_rules.push_back( left_rule ); + new_left = true; + depth_branches.push_back( { get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[0].index] ), get_depth( left_rule, left_rule[left_rule[left_node->index].fanin[1].index] ) } ); + } + /* right move */ + if ( can_right && right_node->type == start_node.type && start_node.fanin[1].inv == 0 && check_depths( rule, start_node, depth_branches, 0 ) ) + { + auto r = &right_rule[start_node.index]; + + make_move( right_rule, right_node, r, 0 ); + + new_rules.push_back( right_rule ); + new_right = true; + depth_branches.push_back( { get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[0].index] ), get_depth( right_rule, right_rule[right_rule[right_node->index].fanin[1].index] ) } ); + } + + /* initial rule, start_node left children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[0].index], next_depths, true, true ); + /* initial rule, start_node right children */ + create_rules_from_dsd( new_rules, rule, rule[start_node.fanin[1].index], next_depths, true, true ); + /* left rule, start_node new root */ + if ( new_left ) + { + create_rules_from_dsd( new_rules, left_rule, left_rule[start_node.index], depth_branches, true, false ); + } + /* right rule, start_node new root */ + if ( new_right ) + { + create_rules_from_dsd( new_rules, right_rule, right_rule[start_node.index], depth_branches, false, true ); + } + } + + uint32_t compute_canonized_polarity( uint32_t polarity, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + uint32_t mask_l = 0; + uint32_t mask_r = 0; + uint32_t mask_obs = 0; + for ( uint32_t i = 0; i < obs_pi; i++ ) + { + mask_obs |= ( 1 << i ); + } + for ( uint32_t i = obs_pi; i < obs_pi + left_pi; i++ ) + { + mask_l |= ( 1 << i ); + } + for ( uint32_t i = obs_pi + left_pi; i < obs_pi + left_pi + right_pi; i++ ) + { + mask_r |= ( 1 << i ); + } + return ( ( polarity & mask_l ) << right_pi ) | ( ( polarity & mask_r ) >> left_pi ) | ( polarity & mask_obs ); + } + + void compute_canonized_permutation( std::vector& perm, uint32_t left_pi, uint32_t right_pi, uint32_t obs_pi ) + { + std::vector copy( perm ); + for ( uint32_t i = obs_pi; i < obs_pi + right_pi; i++ ) + { + perm[i] = copy[( i + left_pi )]; + } + for ( uint32_t i = right_pi + obs_pi; i < perm.size(); i++ ) + { + perm[i] = copy[( i - right_pi )]; + } + } + + /*! \brief Recursively assigns indexes to a rule and its subrules and builds and_table. + * It also computes negations and permutations for the gate whose rule is being passed as parameter. + * + * \param r rule to index. + * \param n dsd node to start from. + * \param max max index assigned. + * \param polarity polarity of gate. + * \param perm permutation of gate. + * \param shift specifies the number of PIs encountered. + * Returns label to be assigned to gate whose rule is r. + */ + label do_indexing_rule( rule r, dsd_node n, uint32_t& max, uint32_t& polarity, std::vector& perm, uint32_t& shift ) + { + if ( n.type == node_type::pi_ ) + { + perm[shift] = n.index - 1; + return { 0, 1 }; + } + if ( n.type == node_type::zero_ ) + return { 0, 0 }; + + uint32_t obs_pi = shift; + + /* do indexing on the left */ + uint32_t left_index = do_indexing_rule( r, r[n.fanin[0].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[0].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[0].inv << shift ); + shift++; + } + /* encountered PIs on the left */ + uint32_t left_pi = shift - obs_pi; + + /* do indexing on the right */ + uint32_t right_index = do_indexing_rule( r, r[n.fanin[1].index], max, polarity, perm, shift ).index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + { + polarity |= ( n.fanin[1].inv << shift ); + shift++; + } + /* encountered PIs on the right */ + uint32_t right_pi = shift - obs_pi - left_pi; + + /* check if it is inverted gate */ + if ( n.fanin[0].index == 0 && n.fanin[1].inv && n.index == r.size() - 1 ) + { + return { 1, right_index }; + } + signal left, right; + + /* ignore invertion of PIs */ + if ( r[n.fanin[0].index].type == node_type::pi_ ) + left.inv = 0; + else + left.inv = (uint64_t)n.fanin[0].inv; + left.index = left_index; + if ( r[n.fanin[1].index].type == node_type::pi_ ) + right.inv = 0; + else + right.inv = (uint64_t)n.fanin[1].inv; + right.index = right_index; + + std::tuple t; + + /* canonize and_table on left index being smaller than right one */ + if ( left.index <= right.index ) + t = std::make_tuple( left, right ); + else + { + /* new polarity */ + polarity = compute_canonized_polarity( polarity, left_pi, right_pi, obs_pi ); + + /* new permutation */ + compute_canonized_permutation( perm, left_pi, right_pi, obs_pi ); + + t = std::make_tuple( right, left ); + } + auto match = _and_table.find( t ); + if ( match != _and_table.end() ) + return { 0, match->second }; + max++; + /* insert new value in and_table */ + _and_table.insert( { t, max } ); + return { 0, max }; + } + + template + dsd_node is_top_dec( const TT& tt, uint32_t var_index, bool allow_xor = false, TT* func = nullptr ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + auto var = tt.construct(); + kitty::create_nth_var( var, var_index ); + + if ( kitty::implies( tt, var ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( tt, ~var ) ) + { + if ( func ) + { + *func = kitty::cofactor0( tt, var_index ); + } + dsd_node res = { node_type::and_, var_index, {} }; + res.fanin.push_back( { 1, UINT32_MAX } ); + return res; + } + else if ( kitty::implies( ~var, tt ) ) + { + if ( func ) + { + *func = kitty::cofactor1( tt, var_index ); + } + dsd_node res = { node_type::or_, var_index, {} }; + res.fanin.push_back( { 1, UINT32_MAX } ); + return res; + } + + if ( allow_xor ) + { + /* try XOR */ + const auto co0 = kitty::cofactor0( tt, var_index ); + const auto co1 = kitty::cofactor1( tt, var_index ); + + if ( kitty::equal( co0, ~co1 ) ) + { + if ( func ) + { + *func = co0; + } + dsd_node res = { node_type::xor_, var_index, {} }; + res.fanin.push_back( { 0, UINT32_MAX } ); + return res; + } + } + + return { node_type::none, var_index, {} }; + } + + template + dsd_node is_bottom_dec( const TT& tt, uint32_t var_index1, uint32_t var_index2, TT* func = nullptr, uint32_t new_index = invalid_index, bool allow_xor = false ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, var_index1 ); + const auto tt1 = kitty::cofactor1( tt, var_index1 ); + + const auto tt00 = kitty::cofactor0( tt0, var_index2 ); + const auto tt01 = kitty::cofactor1( tt0, var_index2 ); + const auto tt10 = kitty::cofactor0( tt1, var_index2 ); + const auto tt11 = kitty::cofactor1( tt1, var_index2 ); + + const auto eq01 = kitty::equal( tt00, tt01 ); + const auto eq02 = kitty::equal( tt00, tt10 ); + const auto eq03 = kitty::equal( tt00, tt11 ); + const auto eq12 = kitty::equal( tt01, tt10 ); + const auto eq13 = kitty::equal( tt01, tt11 ); + const auto eq23 = kitty::equal( tt10, tt11 ); + + const auto num_pairs = + static_cast( eq01 ) + + static_cast( eq02 ) + + static_cast( eq03 ) + + static_cast( eq12 ) + + static_cast( eq13 ) + + static_cast( eq23 ); + + if ( num_pairs != 2u && num_pairs != 3 ) + { + return { node_type::none, invalid_index, {} }; + } + + if ( !eq01 && !eq02 && !eq03 ) // 00 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq01 && !eq12 && !eq13 ) // 01 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq02 && !eq12 && !eq23 ) // 10 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt10 ); + } + dsd_node res = { node_type::or_, new_index, {} }; + res.fanin.push_back( { 1, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( !eq03 && !eq13 && !eq23 ) // 11 is different + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt11, tt00 ); + } + dsd_node res = { node_type::and_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + else if ( allow_xor ) // XOR + { + if ( func ) + { + *func = kitty::mux_var( var_index1, tt01, tt00 ); + } + dsd_node res = { node_type::xor_, new_index, {} }; + res.fanin.push_back( { 0, var_index1 } ); + res.fanin.push_back( { 0, var_index2 } ); + return res; + } + + return { node_type::none, invalid_index, {} }; + } + + template + uint32_t find_unate_var( const TT tt ) + { + for ( uint32_t index = 0; index < tt.num_vars() - 2; ++index ) + { + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + if ( ( ( tt0 & tt1 ) == tt0 ) && ( ( tt0 & tt1 ) == tt1 ) ) + return index; + } + + return tt.num_vars() - 1; + } + + template + dsd_node shannon_dec( const TT& tt, uint32_t index, TT* func0 = nullptr, TT* func1 = nullptr ) + { + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto tt0 = kitty::cofactor0( tt, index ); + const auto tt1 = kitty::cofactor1( tt, index ); + + dsd_node res = { node_type::mux_, index, {} }; + res.fanin.push_back( { 0, index } ); + + if ( func0 && func1 ) + { + *func0 = tt0; + *func1 = tt1; + } + + return res; + } + + /*! \brief Get depth of rule starting from a specific dsd_node. + * + * \param rule rule + * \param n dsd_node to start from + * Returns depth of rule starting from n. + */ + uint32_t get_depth( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ || n.type == node_type::zero_ ) + { + return 0; + } + uint32_t max_depth; + uint32_t left_depth = get_depth( rule, rule[n.fanin[0].index] ); + uint32_t right_depth = get_depth( rule, rule[n.fanin[1].index] ); + max_depth = ( left_depth > right_depth ) ? left_depth : right_depth; + return max_depth + 1; + } + +#pragma region Report + std::string to_string( node_type t ) + { + if ( t == node_type::and_ ) + return "*"; + if ( t == node_type::or_ ) + return "+"; + if ( t == node_type::mux_ ) + return "+"; + if ( t == node_type::xor_ ) + return "xor"; + if ( t == node_type::pi_ ) + return "pi"; + if ( t == node_type::none ) + return "none"; + if ( t == node_type::zero_ ) + return "zero"; + } + + void print_dsd_node( dsd_node& n ) + { + std::cout << n.index << " " << to_string( n.type ) << " "; + for ( auto elem : n.fanin ) + std::cout << "{" << elem.index << ", " << elem.inv << "}"; + std::cout << "\n"; + } + + void print_rule( rule& r ) + { + for ( auto elem : r ) + print_dsd_node( elem ); + } + + /*! \brief Print expression of a rule. + * + * \param rule rule. + * \param n dsd_node to start from. + */ + void print_rule( rule rule, dsd_node n ) + { + if ( n.type == node_type::pi_ ) + { + std::cout << char( 'a' + n.index ); + return; + } + if ( n.type == node_type::zero_ ) + { + std::cout << "0"; + return; + } + else + { + std::cout << "("; + if ( n.fanin[0].inv ) + { + std::cout << "!"; + } + if ( n.type == node_type::mux_ ) + { + std::cout << "!" << char( 'a' + n.fanin[2].index ) << " * "; + } + print_rule( rule, rule[n.fanin[0].index] ); + std::cout << " " << to_string( n.type ) << " "; + if ( n.type == node_type::mux_ ) + { + std::cout << char( 'a' + n.fanin[2].index ) << " * "; + } + if ( n.fanin[1].inv ) + { + std::cout << "!"; + } + print_rule( rule, rule[n.fanin[1].index] ); + std::cout << ")"; + } + } +#pragma endregion + +private: + bool gate_disjoint{ false }; /* flag for gate support*/ + uint32_t num_large_gates{ 0 }; + + std::vector const& _gates; /* collection of gates */ + struct_library_params const _ps; + + composed_list_t _supergates; /* list of composed_gates */ + lib_rule _dsd_map; /* hash map for DSD decomposition of gates */ + lib_table _and_table; /* AND table */ + map_label_gate _label_to_gate; /* map label to gate */ +}; + +} // namespace mockturtle diff --git a/include/mockturtle/utils/super_utils.hpp b/include/mockturtle/utils/super_utils.hpp new file mode 100644 index 0000000..c7a74b1 --- /dev/null +++ b/include/mockturtle/utils/super_utils.hpp @@ -0,0 +1,466 @@ +/* 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 super_utils.hpp + \brief Implements utilities to create supergates for technology mapping + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" + +namespace mockturtle +{ + +struct super_utils_params +{ + /*! \brief load multi-output gates in simple supergates */ + bool load_multioutput_in_single{ false }; + + /*! \brief reports loaded supergates */ + bool verbose{ false }; +}; + +/*! \brief Utilities to generate supergates + * + * This class creates supergates starting from supergates + * specifications contained in `supergates_spec` extracted + * from a SUPER file. + * + * Multi-output gates are also extracted from the list of + * GENLIB gates. However multi-output gates are currently not + * supported as supergates members. + * + * This utility is called by `tech_library` to construct + * the library for technology mapping. + */ +template +class super_utils +{ +private: + static constexpr uint32_t truth_table_size = 6; + +public: + explicit super_utils( std::vector const& gates, super_lib const& supergates_spec = {}, super_utils_params const ps = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _supergates(), + _multioutput_gates() + { + if ( _supergates_spec.supergates.size() == 0 ) + { + generate_library_with_genlib(); + } + else + { + generate_library_with_super(); + } + } + + /*! \brief Get the all the supergates. + * + * Returns a list of supergates created accordingly to + * the standard library and the supergates specifications. + */ + const std::deque>& get_super_library() const + { + return _supergates; + } + + /*! \brief Get the number of standard gates. + * + * Returns the number of standard gates contained in the + * supergate library. + */ + const uint32_t get_standard_library_size() const + { + return simple_gates_size; + } + + /*! \brief Get multi-output gates. + * + * Returns a list of multioutput gates. + */ + const std::vector>>& get_multioutput_library() const + { + return _multioutput_gates; + } + +public: + void generate_library_with_genlib() + { + uint32_t initial_size = _supergates.size(); + + std::unordered_map multioutput_map; + std::unordered_map multioutput_idx; + multioutput_map.reserve( _gates.size() ); + + /* look for multi-output gates (gates with the same name) */ + uint32_t multioutput_i = 0; + for ( const auto& g : _gates ) + { + if ( multioutput_map.find( g.name ) != multioutput_map.end() ) + { + /* assign an index */ + if ( multioutput_map[g.name] == 1 ) + multioutput_idx[g.name] = multioutput_i++; + + multioutput_map[g.name] += 1; + } + else + { + multioutput_map[g.name] = 1; + multioutput_idx[g.name] = UINT32_MAX; + } + } + + /* create composed gates */ + uint32_t ignored = 0; + uint32_t ignored_id = 0; + uint32_t large_gates = 0; + for ( const auto& g : _gates ) + { + std::array pin_to_pin_delays{}; + + if ( g.function.num_vars() > NInputs ) + { + ++ignored; + ignored_id = g.id; + continue; + } + if ( g.function.num_vars() > truth_table_size ) + { + ++large_gates; + continue; + } + + auto i = 0u; + for ( auto const& pin : g.pins ) + { + /* use worst pin delay */ + pin_to_pin_delays[i++] = std::max( pin.rise_block_delay, pin.fall_block_delay ); + } + + if ( multioutput_map[g.name] == 1 || _ps.load_multioutput_in_single ) + { + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + + if ( multioutput_map[g.name] > 1 ) + { + uint32_t idx = multioutput_idx[g.name]; + if ( _multioutput_gates.size() <= idx ) + _multioutput_gates.emplace_back( std::vector>() ); + + _multioutput_gates[multioutput_idx[g.name]].emplace_back( + composed_gate{ static_cast( idx ), + false, + &g, + g.num_vars, + g.function, + g.area, + pin_to_pin_delays, + {} } ); + } + } + + simple_gates_size = _supergates.size() - initial_size; + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loading {} simple library cells\n", simple_gates_size + large_gates ); + std::cout << fmt::format( "[i] Loading {} multi-output library cells\n", _multioutput_gates.size() ); + } + + if ( ignored > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} gates IGNORED (e.g., {}), too many inputs for the library settings\n", ignored, _gates[ignored_id].name ); + } + } + + void generate_library_with_super() + { + if ( _supergates_spec.max_num_vars > NInputs || _supergates_spec.max_num_vars > truth_table_size ) + { + std::cerr << fmt::format( + "[e] ERROR: NInputs ({}) should be greater or equal than the max number of variables ({}) in the super file.\n", NInputs, _supergates_spec.max_num_vars ); + std::cerr << "[i] WARNING: ignoring supergates, proceeding with standard library." << std::endl; + generate_library_with_genlib(); + return; + } + + /* create a map for the gates IDs */ + std::unordered_map gates_map; + + for ( auto const& g : _gates ) + { + if ( gates_map.find( g.name ) != gates_map.end() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring genlib gate {}, duplicated name entry in supergates.", g.name ) << std::endl; + } + else + { + gates_map[g.name] = g.id; + } + } + + /* creating input variables */ + for ( uint8_t i = 0; i < _supergates_spec.max_num_vars; ++i ) + { + kitty::dynamic_truth_table tt{ NInputs }; + kitty::create_nth_var( tt, i ); + + _supergates.emplace_back( composed_gate{ static_cast( i ), + false, + nullptr, + 0, + tt, + 0.0, + {}, + {} } ); + } + + generate_library_with_genlib(); + + uint32_t super_count = 0; + + /* add supergates */ + for ( auto const& g : _supergates_spec.supergates ) + { + uint32_t root_match_id; + if ( auto it = gates_map.find( g.name ); it != gates_map.end() ) + { + root_match_id = it->second; + } + else + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, no reference in genlib.", g.id ) << std::endl; + continue; + } + + uint32_t num_vars = _gates[root_match_id].num_vars; + + if ( num_vars != g.fanin_id.size() ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong number of fanins.", g.id ) << std::endl; + continue; + } + if ( num_vars > _supergates_spec.max_num_vars ) + { + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, too many variables for the library settings.", g.id ) << std::endl; + continue; + } + + std::vector*> sub_gates; + + bool error = false; + bool simple_gate = true; + for ( uint32_t f : g.fanin_id ) + { + if ( f >= g.id + _supergates_spec.max_num_vars ) + { + error = true; + std::cerr << fmt::format( "[i] WARNING: ignoring supergate {}, wrong fanins.", g.id ) << std::endl; + } + if ( f < _supergates_spec.max_num_vars ) + { + sub_gates.emplace_back( &_supergates[f] ); + } + else + { + sub_gates.emplace_back( &_supergates[f + simple_gates_size] ); + simple_gate = false; + } + } + + if ( error ) + { + continue; + } + + /* force at `is_super = false` simple gates considered as supergates. + * This is necessary to not have duplicates since tech_library + * computes indipendently the permutations for simple gates. + * Moreover simple gates permutations could be incomplete in SUPER + * libraries which are constrained by the number of gates. */ + bool is_super_verified = g.is_super; + if ( simple_gate ) + { + is_super_verified = false; + } + + float area = compute_area( root_match_id, sub_gates ); + const kitty::dynamic_truth_table tt = compute_truth_table( root_match_id, sub_gates ); + + _supergates.emplace_back( composed_gate{ static_cast( _supergates.size() ), + is_super_verified, + &_gates[root_match_id], + 0, + tt, + area, + {}, + sub_gates } ); + + if ( g.is_super ) + { + ++super_count; + } + + auto& s = _supergates[_supergates.size() - 1]; + s.num_vars = compute_support( s ); + compute_delay_parameters( s ); + } + + /* minimize supergates */ + for ( auto& g : _supergates ) + { + if ( g.is_super ) + { + g.function = shrink_to( g.function, static_cast( g.num_vars ) ); + } + } + + if ( _ps.verbose ) + { + std::cout << fmt::format( "[i] Loaded {} supergates in the library\n", super_count ); + } + } + +private: + inline float compute_area( uint32_t root_id, std::vector*> const& sub_gates ) + { + float area = _gates[root_id].area; + for ( auto const f : sub_gates ) + { + area += f->area; + } + + return area; + } + + inline uint32_t compute_support( composed_gate& s ) + { + std::array used_pins{}; + uint32_t support = 0; + + return compute_support_rec( s, used_pins ); + } + + uint32_t compute_support_rec( composed_gate& s, std::array& used_pins ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + if ( used_pins[s.id]++ == 0u ) + { + return 1; + } + return 0; + } + + uint32_t support = 0; + for ( auto const pin : s.fanin ) + { + support += compute_support_rec( *pin, used_pins ); + } + return support; + } + + inline kitty::dynamic_truth_table compute_truth_table( uint32_t root_id, std::vector*> const& sub_gates ) + { + std::vector ttv; + + for ( auto const f : sub_gates ) + { + ttv.emplace_back( f->function ); + } + + return kitty::compose_truth_table( _gates[root_id].function, ttv ); + } + + inline void compute_delay_parameters( composed_gate& s ) + { + const auto& root = *( s.root ); + + auto i = 0u; + for ( auto const& pin : root.pins ) + { + float worst_delay = std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( s, *( s.fanin[i++] ), worst_delay ); + } + } + + void compute_delay_pin_rec( composed_gate& root, composed_gate& s, float delay ) + { + /* termination: input variable */ + if ( s.root == nullptr ) + { + root.tdelay[s.id] = std::max( root.tdelay[s.id], delay ); + return; + } + + auto i = 0u; + for ( auto const& pin : s.root->pins ) + { + float worst_delay = delay + std::max( pin.rise_block_delay, pin.fall_block_delay ); + + compute_delay_pin_rec( root, *( s.fanin[i++] ), worst_delay ); + } + } + +protected: + uint32_t simple_gates_size{ 0 }; + + std::vector const& _gates; + super_lib const& _supergates_spec; + super_utils_params const _ps; + std::deque> _supergates; + std::vector>> _multioutput_gates; +}; /* class super_utils */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/tech_library.hpp b/include/mockturtle/utils/tech_library.hpp new file mode 100644 index 0000000..1669555 --- /dev/null +++ b/include/mockturtle/utils/tech_library.hpp @@ -0,0 +1,1652 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file tech_library.hpp + \brief Implements utilities to enumerates gates for technology mapping + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Marcel Walter +*/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../io/genlib_reader.hpp" +#include "../io/super_reader.hpp" +#include "include/supergate.hpp" +#include "standard_cell.hpp" +#include "struct_library.hpp" +#include "super_utils.hpp" + +namespace mockturtle +{ + +/* +std::string const mcnc_library = "GATE inv1 1 O=!a; PIN * INV 1 999 0.9 0.3 0.9 0.3\n" + "GATE inv2 2 O=!a; PIN * INV 2 999 1.0 0.1 1.0 0.1\n" + "GATE inv3 3 O=!a; PIN * INV 3 999 1.1 0.09 1.1 0.09\n" + "GATE inv4 4 O=!a; PIN * INV 4 999 1.2 0.07 1.2 0.07\n" + "GATE nand2 2 O=!(a*b); PIN * INV 1 999 1.0 0.2 1.0 0.2\n" + "GATE nand3 3 O=!(a*b*c); PIN * INV 1 999 1.1 0.3 1.1 0.3\n" + "GATE nand4 4 O=!(a*b*c*d); PIN * INV 1 999 1.4 0.4 1.4 0.4\n" + "GATE nor2 2 O=!(a+b); PIN * INV 1 999 1.4 0.5 1.4 0.5\n" + "GATE nor3 3 O=!(a+b+c); PIN * INV 1 999 2.4 0.7 2.4 0.7\n" + "GATE nor4 4 O=!(a+b+c+d); PIN * INV 1 999 3.8 1.0 3.8 1.0\n" + "GATE and2 3 O=a*b; PIN * NONINV 1 999 1.9 0.3 1.9 0.3\n" + "GATE or2 3 O=a+b; PIN * NONINV 1 999 2.4 0.3 2.4 0.3\n" + "GATE xor2a 5 O=a*!b+!a*b; PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "#GATE xor2b 5 O=!(a*b+!a*!b); PIN * UNKNOWN 2 999 1.9 0.5 1.9 0.5\n" + "GATE xnor2a 5 O=a*b+!a*!b; PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "#GATE xnor2b 5 O=!(a*!b+!a*b); PIN * UNKNOWN 2 999 2.1 0.5 2.1 0.5\n" + "GATE aoi21 3 O=!(a*b+c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE aoi22 4 O=!(a*b+c*d); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE oai21 3 O=!((a+b)*c); PIN * INV 1 999 1.6 0.4 1.6 0.4\n" + "GATE oai22 4 O=!((a+b)*(c+d)); PIN * INV 1 999 2.0 0.4 2.0 0.4\n" + "GATE buf 2 O=a; PIN * NONINV 1 999 1.0 0.0 1.0 0.0\n" + "GATE zero 0 O=CONST0;\n" + "GATE one 0 O=CONST1;"; +*/ + +enum class classification_type : uint32_t +{ + /*! \brief generate the NP configurations (n! * 2^n) + * Direct matching: best up to ~200 library gates */ + np_configurations = 0, + + /*! \brief generate the P configurations (n!) + * Matching by N-canonization: best for more + * than ~200 library gates */ + p_configurations = 1, + + /*! \brief generate the n configurations (2^n) + * Direct fast matching, less quality */ + n_configurations = 2, +}; + +struct tech_library_params +{ + /*! \brief Load large gates with more than 6 inputs */ + bool load_large_gates{ true }; + + /*! \brief Loads multioutput gates in the library */ + bool load_multioutput_gates{ true }; + + /*! \brief Don't load symmetrical permutations of gate pins (drastically speeds-up mapping) */ + bool ignore_symmetries{ false }; + + /*! \brief Load gates with minimum size only */ + bool load_minimum_size_only{ true }; + + /*! \brief Remove dominated gates (larger sizes) */ + bool remove_dominated_gates{ true }; + + /*! \brief Loads multioutput gates in single-output library */ + bool load_multioutput_gates_single{ false }; + + /*! \brief reports np enumerations */ + bool verbose{ false }; + + /*! \brief reports all the entries in the library */ + bool very_verbose{ false }; +}; + +namespace detail +{ + +template +struct tuple_tt_hash +{ + inline std::size_t operator()( std::array, NumOutputs> const& tts ) const + { + std::size_t seed = kitty::hash_block( tts[0]._bits ); + + for ( auto i = 1; i < NumOutputs; ++i ) + kitty::hash_combine( seed, kitty::hash_block( tts[i]._bits ) ); + + return seed; + } +}; + +} // namespace detail + +/*! \brief Library of gates for Boolean matching + * + * This class creates a technology library from a set + * of input gates. Each NP- or P-configuration of the gates + * are enumerated and inserted in the library. + * + * The configuration is selected using the template + * parameter `Configuration`. P-configuration is suggested + * for big libraries with few symmetric gates. The template + * parameter `NInputs` selects the maximum number of variables + * allowed for a gate in the library. + * + * The library can be generated also using supergates definitions. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ); + // standard library + mockturtle::tech_library lib( gates ); + + super_lib supergates_spec; + lorina::read_super( "file.super", super_reader( supergates_spec ) ); + // library with supergates + mockturtle::tech_library lib_super( gates, supergates_spec ); + \endverbatim + */ +template +class tech_library +{ +private: + static constexpr float epsilon = 0.0005; + static constexpr uint32_t max_multi_outputs = 2; + static constexpr uint32_t truth_table_size = 6; + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using multi_tt_hash = detail::tuple_tt_hash; + using index_t = phmap::flat_hash_map; + using lib_t = phmap::flat_hash_map; + using multi_relation_t = std::array; + using multi_supergates_list_t = std::array>, max_multi_outputs>; + using multi_lib_t = phmap::flat_hash_map; + using multi_func_t = phmap::flat_hash_map; + using struct_lib_t = phmap::flat_hash_map; + +public: + explicit tech_library( std::vector const& gates, tech_library_params const ps = {}, super_lib const& supergates_spec = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _use_supergates( false ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + explicit tech_library( std::vector const& gates, super_lib const& supergates_spec, tech_library_params const ps = {} ) + : _gates( gates ), + _supergates_spec( supergates_spec ), + _ps( ps ), + _cells( get_standard_cells( _gates ) ), + _super( _gates, _supergates_spec, super_utils_params{ ps.load_multioutput_gates_single, ps.verbose } ), + _use_supergates( true ), + _struct( _gates, struct_library_params{ ps.load_minimum_size_only, ps.very_verbose } ), + _super_lib(), + _multi_lib(), + _struct_lib() + { + static_assert( NInputs < 16, "The technology library database supports NInputs up to 15\n" ); + + generate_library(); + + if ( ps.load_multioutput_gates ) + generate_multioutput_library(); + + if ( ps.load_large_gates ) + { + _struct.construct( 2 ); + } + } + + tech_library ( const tech_library& ) = delete; + tech_library& operator=( const tech_library& ) = delete; + + /*! \brief Get the gates matching the function. + * + * Returns a list of gates that match the function represented + * by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gates matching the function. + * + * Returns a list of multi-output gates that match the function + * represented by the truth table. + */ + const multi_supergates_list_t* get_multi_supergates( std::array const& tts ) const + { + auto match = _multi_lib.find( tts ); + if ( match != _multi_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the multi-output gate function ID for a single output. + * + * Returns the function ID of a multi-output gate output if matched. This function + * supports up to 6 inputs. Returns zero in case of no match. + */ + uint64_t get_multi_function_id( uint64_t const& tt ) const + { + auto match = _multi_funcs.find( tt ); + if ( match != _multi_funcs.end() ) + return match->second; + return 0; + } + + /*! \brief Get the pattern ID for structural matching. + * + * Returns a pattern ID if found, UINT32_MAX otherwise given the + * children IDs. This function works with only AND operators. + */ + uint32_t get_pattern_id( uint32_t id1, uint32_t id2 ) const + { + return _struct.get_pattern_id( id1, id2 ); + } + + /*! \brief Get the gates matching the pattern ID and phase. + * + * Returns a list of gates that match the pattern ID and the given polarity. + */ + const supergates_list_t* get_supergates_pattern( uint32_t id, bool phase ) const + { + return _struct.get_supergates_pattern( id, phase ); + } + + /*! \brief Get inverter information. + * + * Returns area, delay, and ID of the smallest inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_tuple( _inv_area, _inv_delay, _inv_id ); + } + + /*! \brief Get buffer information. + * + * Returns area, delay, and ID of the smallest buffer. + */ + const std::tuple get_buffer_info() const + { + return std::make_tuple( _buf_area, _buf_delay, _buf_id ); + } + + /*! \brief Returns the maximum number of variables of the gates. */ + unsigned max_gate_size() + { + return _max_size; + } + + /*! \brief Returns the original gates. */ + const std::vector get_gates() const + { + return _gates; + } + + /*! \brief Returns the standard cells. */ + const std::vector& get_cells() const + { + return _cells; + } + + /*! \brief Returns multioutput gates. */ + const std::vector>>& get_multioutput_gates() const + { + return _super.get_multioutput_library(); + } + + /*! \brief Returns the number of multi-output gates loaded in the library. */ + const uint32_t num_multioutput_gates() const + { + if ( !_ps.load_multioutput_gates ) + return 0; + return _multi_lib.size(); + } + + /*! \brief Returns the number of gates for structural matching. */ + const uint32_t num_structural_gates() const + { + return _struct.get_struct_library().size(); + } + + /*! \brief Returns the number of gates for structural matching with more than 6 inputs. */ + const uint32_t num_structural_large_gates() const + { + return _struct.get_struct_library().size(); + } + +private: + void generate_library() + { + bool inv = false; + bool buf = false; + + /* extract the smallest inverter and buffer info */ + for ( auto& gate : _gates ) + { + if ( gate.function.num_vars() == 1 ) + { + /* extract inverter delay and area */ + if ( kitty::is_const0( kitty::cofactor1( gate.function, 0 ) ) ) + { + /* get the smallest area inverter */ + if ( !inv || gate.area < _inv_area - epsilon ) + { + _inv_area = gate.area; + _inv_delay = compute_worst_delay( gate ); + _inv_id = gate.id; + inv = true; + } + } + else + { + /* get the smallest area buffer */ + if ( !buf || gate.area < _buf_area - epsilon ) + { + _buf_area = gate.area; + _buf_delay = compute_worst_delay( gate ); + _buf_id = gate.id; + buf = true; + } + } + } + } + + auto const& supergates = _super.get_super_library(); + uint32_t const standard_gate_size = _super.get_standard_library_size(); + + std::vector skip_gates( supergates.size(), false ); + + if ( _ps.load_minimum_size_only || _ps.remove_dominated_gates ) + { + filter_gates( supergates, skip_gates ); + } + + /* generate the configurations for the standard gates */ + uint32_t i = 0u; + uint32_t skip_count = 0; + for ( auto const& gate : supergates ) + { + uint32_t np_count = 0; + + if ( skip_gates[skip_count++] ) + { + /* exclude gate */ + ++i; + continue; + } + + if ( gate.root == nullptr ) + { + /* exclude PIs */ + continue; + } + + _max_size = std::max( _max_size, gate.num_vars ); + + if ( i++ < standard_gate_size ) + { + const auto on_np = [&]( auto const& tt, auto neg, auto const& perm ) { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + 0 }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]( auto const& tt, auto const& perm ) { + /* get all the configurations that lead to the N-class representative */ + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( tt ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && ( _ps.ignore_symmetries || sg.tdelay == it->tdelay ) ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* NP enumeration of the function */ + const auto tt = gate.function; + kitty::exact_np_enumeration( tt, on_np ); + } + else if ( Configuration == classification_type::n_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + std::vector pin_order( tt.num_vars() ); + std::iota( pin_order.begin(), pin_order.end(), 0 ); + kitty::exact_n_enumeration( tt, [&]( auto const& tt, auto neg ) { on_np( tt, neg, pin_order ); } ); + } + else + { + /* P enumeration followed by N canonization of the function */ + const auto tt = gate.function; + kitty::exact_p_enumeration( tt, on_p ); + } + } + else + { + /* process the supergates */ + if ( !gate.is_super ) + { + /* ignore simple gates */ + continue; + } + + const auto on_np = [&]( auto const& tt, auto neg ) { + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( neg ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + }; + + const auto on_p = [&]() { + auto [tt_canon, phases] = kitty::exact_n_canonization_complete( gate.function ); + std::vector perm( gate.num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto phase : phases ) + { + supergate sg = { &gate, + static_cast( gate.area ), + {}, + perm, + static_cast( phase ) }; + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + sg.tdelay[i] = gate.tdelay[perm[i]]; + } + + const auto static_tt = kitty::extend_to( tt_canon ); + + auto& v = _super_lib[static_tt]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v.begin(), v.end(), sg, [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated element due to symmetries */ + while ( it != v.end() ) + { + if ( sg.root->id == it->root->id ) + { + /* if already in the library exit, else ignore permutations if with equal delay cost */ + if ( sg.polarity == it->polarity && sg.tdelay == it->tdelay ) + { + to_add = false; + break; + } + } + else + { + break; + } + ++it; + } + + if ( to_add ) + { + v.insert( it, sg ); + ++np_count; + } + } + }; + + if constexpr ( Configuration == classification_type::np_configurations ) + { + /* N enumeration of the function */ + const auto tt = gate.function; + kitty::exact_n_enumeration( tt, on_np ); + } + else + { + /* N canonization of the function */ + const auto tt = gate.function; + on_p(); + } + } + + if ( _ps.very_verbose ) + { + std::cout << "Gate " << gate.root->name << ", num_vars = " << gate.num_vars << ", np entries = " << np_count << std::endl; + } + } + + if ( !inv ) + { + std::cerr << "[i] WARNING: inverter gate has not been detected in the library" << std::endl; + } + + if ( !buf ) + { + std::cerr << "[i] WARNING: buffer gate has not been detected in the library" << std::endl; + } + + if ( _ps.very_verbose ) + { + for ( auto const& entry : _super_lib ) + { + kitty::print_hex( entry.first ); + std::cout << ": "; + for ( auto const& gate : entry.second ) + { + printf( "%d(a:%.2f, p:%d) ", gate.root->id, gate.area, gate.polarity ); + } + std::cout << std::endl; + } + } + } + + /* Supports only NP configurations */ + void generate_multioutput_library() + { + uint32_t np_count = 0; + std::string ignored_name; + bool consistency_check = true; + + /* load multi-output gates */ + auto const& multioutput_gates = _super.get_multioutput_library(); + + uint32_t ignored_gates = 0; + for ( auto const& multi_gate : multioutput_gates ) + { + /* select the on up to max_multi_outputs outputs */ + if ( multi_gate.size() > max_multi_outputs ) + { + ignored_name = multi_gate[0].root->name; + ++ignored_gates; + continue; + } + + std::array order = { 0 }; + + const auto on_np = [&]( auto const& tts, auto neg, auto const& perm ) { + std::vector> multi_sg; + + for ( auto const& gate : multi_gate ) + { + multi_sg.emplace_back( supergate{ &gate, + static_cast( gate.area ), + {}, + perm, + 0 } ); + } + + for ( auto i = 0u; i < perm.size() && i < NInputs; ++i ) + { + uint32_t j = 0; + for ( auto& sg : multi_sg ) + { + sg.tdelay[i] = multi_gate[j++].tdelay[perm[i]]; + sg.polarity |= ( ( neg >> perm[i] ) & 1 ) << i; /* permutate input negation to match the right pin */ + } + } + + std::array static_tts = {}; + std::array sorted_tts = {}; + + /* canonize output */ + for ( auto i = 0; i < tts.size(); ++i ) + { + static_tts[i] = kitty::extend_to( tts[i] ); + if ( ( static_tts[i]._bits & 1 ) == 1 ) + { + static_tts[i] = ~static_tts[i]; + multi_sg[i].polarity |= 1 << NInputs; /* set flipped output polarity*/ + } + } + + std::iota( order.begin(), order.end(), 0 ); + + std::stable_sort( order.begin(), order.end(), [&]( size_t a, size_t b ) { + return static_tts[a] < static_tts[b]; + } ); + + std::transform( order.begin(), order.end(), sorted_tts.begin(), [&]( size_t a ) { + return static_tts[a]; + } ); + + // std::stable_sort( static_tts.begin(), static_tts.end() ); + + auto& v = _multi_lib[sorted_tts]; + + /* ordered insert by ascending area and number of input pins */ + auto it = std::lower_bound( v[0].begin(), v[0].end(), multi_sg[0], [&]( auto const& s1, auto const& s2 ) { + if ( s1.area < s2.area ) + return true; + if ( s1.area > s2.area ) + return false; + if ( s1.root->num_vars < s2.root->num_vars ) + return true; + if ( s1.root->num_vars > s2.root->num_vars ) + return true; + return s1.root->id < s2.root->id; + } ); + + bool to_add = true; + /* search for duplicated elements due to symmetries */ + while ( it != v[0].end() ) + { + /* if different gate, exit */ + if ( multi_sg[0].root->id != it->root->id ) + break; + + /* if already in the library, exit */ + if ( multi_sg[order[0]].polarity != it->polarity ) + { + ++it; + continue; + } + + bool same_delay = true; + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + if ( multi_sg[order[i]].tdelay != v[i][d].tdelay ) + { + same_delay = false; + break; + } + } + + /* do not add if equivalent to another in the library */ + if ( same_delay ) + { + to_add = false; + break; + } + + ++it; + } + + if ( to_add ) + { + size_t d = std::distance( v[0].begin(), it ); + for ( auto i = 0; i < multi_sg.size(); ++i ) + { + v[i].insert( v[i].begin() + d, multi_sg[order[i]] ); + } + ++np_count; + } + }; + + /* NP enumeration of the function */ + std::vector tts; + for ( auto gate : multi_gate ) + tts.push_back( gate.function ); + kitty::exact_multi_np_enumeration( tts, on_np ); + + /* NPN enumeration of the single outputs */ + uint32_t pin = 0; + for ( auto const& gate : multi_gate ) + { + consistency_check &= check_delay_consistency( gate, pin++ ); + exact_npn_enumeration( gate.function, [&]( auto const& tt, auto neg, auto const& perm ) { + (void)neg; + (void)perm; + _multi_funcs[tt._bits[0]] = gate.function._bits[0]; + } ); + } + } + + /* update area based on the single output contribution */ + multi_update_area(); + + if ( _ps.verbose && ignored_gates > 0 ) + { + std::cerr << fmt::format( "[i] WARNING: {} multi-output gates IGNORED (e.g., {}), too many outputs for the library settings\n", ignored_gates, ignored_name ); + } + + if ( !consistency_check ) + { + std::cerr << "[i] WARNING: technology mapping using multi-output cells with warnings might generate required time violations or circuits with dangling pins\n"; + } + + // std::cout << _multi_lib.size() << "\n"; + } + + void multi_update_area() + { + /* update area for each sub-function in a multi-output gate with their contribution */ + for ( auto& pair : _multi_lib ) + { + auto& multi_gates = pair.second; + for ( auto i = 0; i < multi_gates[0].size(); ++i ) + { + /* get sum of area and area count */ + double area = 0; + uint32_t contribution_count = 0; + std::array area_contribution = { 0 }; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + const TT tt = kitty::extend_to( gate.root->function ); + + /* get the area of the smallest match with a simple gate */ + const auto match = get_supergates( tt ); + if ( match == nullptr ) + continue; + + area_contribution[j] = ( *match )[0].area; + area += area_contribution[j]; + ++contribution_count; + + // std::cout << fmt::format( "Contribution {}\t = {}\n", ( *match )[0].root->root->name, area_contribution[j] ); + } + + /* compute scaling factor and remaining area for non-matched gates */ + double scaling_factor = 1.0; + double remaining_area = 0; + + if ( contribution_count != max_multi_outputs ) + { + scaling_factor = 0.9; + + if ( area > multi_gates[0][i].area ) + scaling_factor -= ( area - multi_gates[0][i].area ) / area; + + remaining_area = ( multi_gates[0][i].area - area * scaling_factor ); + area = area * scaling_factor + remaining_area; + remaining_area /= ( max_multi_outputs - contribution_count ); + } + + /* assign weighted contribution */ + // double area_old = multi_gates[0][i].area; + // double area_check = 0; + for ( auto j = 0; j < max_multi_outputs; ++j ) + { + auto& gate = multi_gates[j][i]; + + if ( area_contribution[j] > 0 ) + gate.area = scaling_factor * area_contribution[j] * gate.area / area; + else + gate.area = remaining_area; + + // area_check += gate.area; + } + + // std::cout << fmt::format( "Area before: {}\t Area after {}\n", area_old, area_check ); + } + } + } + + bool check_delay_consistency( composed_gate const& g, uint32_t pin ) + { + TT tt = kitty::extend_to( g.function ); + uint16_t polarity = 0; + + /* canonicalize in case of P-configurations */ + if constexpr ( Configuration == classification_type::p_configurations ) + { + auto canon = kitty::exact_n_canonization_support( tt, g.num_vars ); + tt = std::get<0>( canon ); + polarity = static_cast( std::get<1>( canon ) ); + } + + auto entry = _super_lib.find( tt ); + if ( entry == _super_lib.end() ) + { + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that can implement output pin {} of the multi-output cell {}\n", pin, g.root->name ); + return false; + } + + /* check delay (at least one entry must have better or equal delay) */ + for ( auto const& sg : entry->second ) + { + bool valid = true; + for ( uint32_t i = 0; i < g.num_vars; ++i ) + { + float pin_delay = sg.tdelay[i]; + if ( ( sg.polarity >> i ) & 1 ) + pin_delay += _inv_delay; + + float mo_pin_delay = g.tdelay[i]; + if ( ( polarity >> i ) & 1 ) + mo_pin_delay += _inv_delay; + + if ( pin_delay > mo_pin_delay ) + { + valid = false; + break; + } + } + + if ( valid ) + { + return true; + } + } + + std::cerr << fmt::format( "[i] WARNING: library does not contain cells that could match the delay of output pin {} of multi-output cell {}\n", pin + 1, g.root->name ); + return false; + } + + float compute_worst_delay( gate const& g ) + { + float worst_delay = 0.0f; + + /* consider only block_delay */ + for ( auto const& pin : g.pins ) + { + float worst_pin_delay = static_cast( std::max( pin.rise_block_delay, pin.fall_block_delay ) ); + worst_delay = std::max( worst_delay, worst_pin_delay ); + } + return worst_delay; + } + + bool compare_sizes( composed_gate const& s1, composed_gate const& s2 ) + { + if ( s1.area < s2.area ) + return true; + else if ( s1.area > s2.area ) + return false; + + /* compute average pin delay */ + float s1_delay = 0, s2_delay = 0; + assert( s1.num_vars == s2.num_vars ); + for ( uint32_t i = 0; i < s1.num_vars; ++i ) + { + s1_delay += s1.tdelay[i]; + s2_delay += s2.tdelay[i]; + } + + if ( s1_delay < s2_delay ) + return true; + else if ( s1_delay > s2_delay ) + return false; + else if ( s1.root->name < s2.root->name ) + return true; + + return false; + } + + void filter_gates( std::deque> const& supergates, std::vector& skip_gates ) + { + assert( supergates.size() >= skip_gates.size() ); + for ( uint32_t i = 0; i < skip_gates.size() - 1; ++i ) + { + if ( supergates[i].root == nullptr ) + continue; + + if ( skip_gates[i] ) + continue; + + auto const& tti = supergates[i].function; + for ( uint32_t j = i + 1; j < skip_gates.size(); ++j ) + { + auto const& ttj = supergates[j].function; + + /* get the same functionality */ + if ( skip_gates[j] || tti != ttj ) + continue; + + if ( _ps.load_minimum_size_only ) + { + if ( compare_sizes( supergates[i], supergates[j] ) ) + { + skip_gates[j] = true; + continue; + } + else + { + skip_gates[i] = true; + break; + } + } + + /* is i smaller than j */ + bool smaller = supergates[i].area <= supergates[j].area; + + /* is i faster for every pin */ + bool faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[i].tdelay[k] > supergates[j].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[j] = true; + continue; + } + + /* is j faster for every pin */ + smaller = supergates[i].area >= supergates[j].area; + faster = true; + for ( uint32_t k = 0; k < tti.num_vars(); ++k ) + { + if ( supergates[j].tdelay[k] > supergates[i].tdelay[k] ) + faster = false; + } + + if ( smaller && faster ) + { + skip_gates[i] = true; + break; + } + } + } + } + +private: + /* inverter info */ + float _inv_area{ 0.0 }; + float _inv_delay{ 0.0 }; + uint32_t _inv_id{ UINT32_MAX }; + + /* buffer info */ + float _buf_area{ 0.0 }; + float _buf_delay{ 0.0 }; + uint32_t _buf_id{ UINT32_MAX }; + + unsigned _max_size{ 0 }; /* max #fanins of the gates in the library */ + + bool _use_supergates; + + std::vector const _gates; /* collection of gates */ + super_lib const _supergates_spec; /* collection of supergates declarations */ + tech_library_params const _ps; + + std::vector const _cells; /* collection of standard cells */ + + super_utils _super; /* supergates generation */ + struct_library _struct; /* library for structural matching */ + lib_t _super_lib; /* library of enumerated gates */ + multi_lib_t _multi_lib; /* library of enumerated multioutput gates */ + multi_func_t _multi_funcs; /* enumerated functions for multioutput gates */ + struct_lib_t _struct_lib; /* library of gates for patterns IDs */ +}; /* class tech_library */ + +template +struct exact_supergate +{ + signal root; + + /* number of inputs of the supergate */ + uint8_t n_inputs{ 0 }; + /* saved polarities for inputs and/or outputs */ + uint8_t polarity{ 0 }; + + /* area */ + float area{ 0 }; + /* worst delay */ + float worstDelay{ 0 }; + /* pin-to-pin delay */ + std::array tdelay{ 0 }; + + exact_supergate( signal const root ) + : root( root ) {} +}; + +struct exact_library_params +{ + /* area of a gate */ + float area_gate{ 1.0f }; + /* area of an inverter */ + float area_inverter{ 0.0f }; + /* delay of a gate */ + float delay_gate{ 1.0f }; + /* delay of an inverter */ + float delay_inverter{ 0.0f }; + + /* classify in NP instead of NPN */ + bool np_classification{ false }; + /* Compute DC classes for matching with don't cares */ + bool compute_dc_classes{ false }; + /* verbose */ + bool verbose{ false }; +}; + +/*! \brief Library of graph structures for Boolean matching + * + * This class creates a technology library from a database + * of structures classified in NPN classes. Each NPN-entry in + * the database is stored in its NP class by removing the output + * inverter if present. The class creates supergates from the + * database computing area and delay information. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + mockturtle::mig_npn_resynthesis mig_resyn{ true }; + mockturtle::exact_library lib( mig_resyn ); + \endverbatim + */ +template +class exact_library +{ + using supergates_list_t = std::vector>; + using TT = kitty::static_truth_table; + using tt_hash = kitty::hash; + using lib_t = std::unordered_map; + using dc_transformation_t = std::tuple>; + using dc_t = std::pair; + using dc_lib_t = std::unordered_map, tt_hash>; + +public: + explicit exact_library( exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + } + + template + explicit exact_library( RewritingFn const& rewriting_fn, exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + generate_library( rewriting_fn ); + } + + template + void add_library( RewritingFn const& rewriting_fn ) + { + generate_library( rewriting_fn ); + } + + /*! \brief Get the structures matching the function. + * + * Returns a list of graph structures that match the function + * represented by the truth table. + */ + const supergates_list_t* get_supergates( TT const& tt ) const + { + auto match = _super_lib.find( tt ); + if ( match != _super_lib.end() ) + return &match->second; + return nullptr; + } + + /*! \brief Get the structures matching the function with DC. + * + * Returns a list of graph structures that match the function + * represented by the truth table and its dont care set. + * This functions also updates the phase and permutation vector + * of the original NPN class to the new one obtained using + * don't cares. + */ + const supergates_list_t* get_supergates( TT const& tt, TT const& dc, uint32_t& phase, std::vector& perm ) const + { + auto match = _super_lib.find( tt ); + if ( match == _super_lib.end() ) + return nullptr; + + /* lookup for don't care optimization */ + auto match_dc = _dc_lib.find( tt ); + if ( dc._bits == 0 || match_dc == _dc_lib.end() ) + return &match->second; + + for ( auto const& entry : match_dc->second ) + { + auto const& dc_entry_tt = std::get<0>( entry ); + + /* check for containment */ + if ( ( dc & dc_entry_tt ) == dc_entry_tt ) + { + auto const& dc_entry = std::get<1>( entry ); + + /* update phase and perm */ + uint32_t dc_entry_phase = std::get<1>( dc_entry ); + auto const& dc_entry_perm = std::get<2>( dc_entry ); + std::vector temp_perm( perm.size() ); + uint32_t temp_phase = dc_entry_phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + temp_perm[dc_entry_perm[i]] = perm[i]; + temp_phase |= ( ( dc_entry_phase >> i ) & 1 ) << perm[i]; + } + phase ^= temp_phase; + std::copy( temp_perm.begin(), temp_perm.end(), perm.begin() ); + return std::get<0>( dc_entry ); + } + } + + /* no dont care optimization found */ + return &match->second; + } + + /*! \brief Returns the NPN database of structures. */ + Ntk& get_database() + { + return _database; + } + + /*! \brief Returns the NPN database of structures. */ + const Ntk& get_database() const + { + return _database; + } + + /*! \brief Get inverter information. + * + * Returns area, and delay cost of the inverter. + */ + const std::tuple get_inverter_info() const + { + return std::make_pair( _ps.area_inverter, _ps.delay_inverter ); + } + +private: + template + void generate_library( RewritingFn const& rewriting_fn ) + { + std::vector> pis; + for ( auto i = 0u; i < NInputs; ++i ) + { + pis.push_back( _database.create_pi() ); + } + + /* Compute NPN classes */ + std::unordered_set classes; + TT tt; + do + { + const auto res = kitty::exact_npn_canonization( tt ); + classes.insert( std::get<0>( res ) ); + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + + /* Constuct supergates */ + for ( auto const& entry : classes ) + { + supergates_list_t supergates_pos; + supergates_list_t supergates_neg; + auto const not_entry = ~entry; + + const auto add_supergate = [&]( auto const& f_new ) { + bool complemented = _database.is_complemented( f_new ); + auto f = f_new; + if ( _ps.np_classification && complemented ) + { + f = !f; + } + exact_supergate sg( f ); + compute_info( sg ); + if ( _ps.np_classification && complemented ) + { + supergates_neg.push_back( sg ); + } + else + { + supergates_pos.push_back( sg ); + } + _database.create_po( f ); + return true; + }; + + kitty::dynamic_truth_table function = kitty::extend_to( entry, NInputs ); + rewriting_fn( _database, function, pis.begin(), pis.end(), add_supergate ); + if ( supergates_pos.size() > 0 ) + { + std::stable_sort( supergates_pos.begin(), supergates_pos.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { entry, supergates_pos } ); + } + if ( _ps.np_classification && supergates_neg.size() > 0 ) + { + std::stable_sort( supergates_neg.begin(), supergates_neg.end(), [&]( auto const& a, auto const& b ) { + return a.area < b.area; + } ); + _super_lib.insert( { not_entry, supergates_neg } ); + } + } + + if ( _ps.compute_dc_classes ) + compute_dont_cares_classes(); + + if ( _ps.verbose ) + { + std::cout << "Classified in " << _super_lib.size() << " entries" << std::endl; + for ( auto const& pair : _super_lib ) + { + kitty::print_hex( pair.first ); + std::cout << ": "; + + for ( auto const& gate : pair.second ) + { + printf( "%.2f,%.2f,%x,%d,:", gate.worstDelay, gate.area, gate.polarity, gate.n_inputs ); + for ( auto j = 0u; j < NInputs; ++j ) + printf( "%.2f/", gate.tdelay[j] ); + std::cout << " "; + } + std::cout << std::endl; + } + } + } + + /* Computes delay and area info */ + void compute_info( exact_supergate& sg ) + { + _database.incr_trav_id(); + /* info does not consider input and output inverters */ + bool compl_root = _database.is_complemented( sg.root ); + auto const root = compl_root ? !sg.root : sg.root; + sg.area = compute_info_rec( sg, root, 0.0f ); + + /* output polarity */ + sg.polarity |= ( unsigned( compl_root ) ) << NInputs; + /* number of inputs */ + for ( auto i = 0u; i < NInputs; ++i ) + { + sg.tdelay[i] *= -1; /* invert to positive value */ + if ( sg.tdelay[i] != 0.0f ) + sg.n_inputs++; + } + sg.worstDelay *= -1; + } + + float compute_info_rec( exact_supergate& sg, signal const& root, float delay ) + { + auto n = _database.get_node( root ); + + if ( _database.is_constant( n ) ) + return 0.0f; + + float area = 0.0f; + float tdelay = delay; + + if ( _database.is_pi( n ) ) + { + sg.tdelay[_database.index_to_node( n ) - 1u] = std::min( sg.tdelay[_database.index_to_node( n ) - 1u], tdelay ); + sg.worstDelay = std::min( sg.worstDelay, tdelay ); + sg.polarity |= ( unsigned( _database.is_complemented( root ) ) ) << ( _database.index_to_node( n ) - 1u ); + return area; + } + + tdelay -= _ps.delay_gate; + + /* add gate area once */ + if ( _database.visited( n ) != _database.trav_id() ) + { + area += _ps.area_gate; + _database.set_value( n, 0u ); + _database.set_visited( n, _database.trav_id() ); + } + + if ( _database.is_complemented( root ) ) + { + tdelay -= _ps.delay_inverter; + /* add inverter area only once (shared by fanout) */ + if ( _database.value( n ) == 0u ) + { + area += _ps.area_inverter; + _database.set_value( n, 1u ); + } + } + + _database.foreach_fanin( n, [&]( auto const& child ) { + area += compute_info_rec( sg, child, tdelay ); + } ); + + return area; + } + + void compute_dont_cares_classes() + { + _dc_lib.clear(); + + /* save the size for each NPN class */ + std::unordered_map class_sizes; + for ( auto const& entry : _super_lib ) + { + const unsigned numgates = static_cast( std::get<1>( entry ).front().area ); + class_sizes.insert( { std::get<0>( entry ), numgates } ); + } + + uint32_t conflict_found = 0; + uint32_t total_exploration = 0; + + /* find don't care links */ + for ( auto entry_i = class_sizes.begin(); entry_i != class_sizes.end(); ++entry_i ) + { + auto const& tt_i = std::get<0>( *entry_i ); + auto const current_size = std::get<1>( *entry_i ); + + /* use a map to link the dont cares to the new size, NPN class, negations, and permutation vector */ + using dc_transf_t = std::tuple>; + std::unordered_map dc_sets; + + for ( auto entry_j = class_sizes.begin(); entry_j != class_sizes.end(); ++entry_j ) + { + auto const& tt_j = std::get<0>( *entry_j ); + uint32_t size = std::get<1>( *entry_j ); + + /* evaluate DC only for size improvement */ + if ( size >= current_size ) + continue; + + /* skip the same NPN class if gates are constructed in NP classes */ + if ( _ps.np_classification && tt_i == ~tt_j ) + continue; + + exact_npn_enumeration( tt_j, [&]( auto const& tt, uint32_t phase, std::vector const& perm ) { + /* extract the DC set */ + const auto dc = tt_i ^ tt; + + /* limit the explosion of DC combinations to evaluate */ + // if ( kitty::count_ones( dc ) > 3 ) + // return; + + ++total_exploration; + + /* check existance: filters ~12% of conflicts */ + if ( auto const& p = dc_sets.find( dc ); p != dc_sets.end() ) + { + if ( size < std::get<0>( std::get<1>( *p ) ) ) + dc_sets[dc] = std::make_tuple( size, tt_j, phase, perm ); + + ++conflict_found; + return; + } + + /* check dominance */ + auto it = dc_sets.begin(); + while ( it != dc_sets.end() ) + { + auto const& dc_set_tt = std::get<0>( *it ); + auto const& and_tt = dc_set_tt & dc; + + if ( dc_set_tt == and_tt && std::get<0>( std::get<1>( *it ) ) <= size ) + { + return; + } + else if ( dc == and_tt && size <= std::get<0>( std::get<1>( *it ) ) ) + { + it = dc_sets.erase( it ); + } + else + { + ++it; + } + } + + /* permute phase */ + uint32_t phase_perm = phase & ( 1 << NInputs ); + for ( auto i = 0u; i < NInputs; ++i ) + { + phase_perm |= ( ( phase >> perm[i] ) & 1 ) << i; + } + + /* insert in the dc_sets */ + dc_sets[dc] = std::make_tuple( size, tt_j, phase_perm, perm ); + } ); + } + + /* add entries to the main data structure */ + std::vector dc_transformations; + dc_transformations.reserve( dc_sets.size() ); + + std::array permutation; + + /* insert in a sorted way based on gain */ + /* TODO: optimize to reduce the number of cycles */ + for ( auto i = 0u; i < std::get<1>( *entry_i ); ++i ) + { + for ( auto const& dc : dc_sets ) + { + auto const& transf = std::get<1>( dc ); + + if ( std::get<0>( transf ) != i ) + { + continue; + } + + supergates_list_t const* sg = &_super_lib[std::get<1>( transf )]; + auto const& perm = std::get<3>( transf ); + + assert( perm.size() == NInputs ); + + for ( auto j = 0u; j < NInputs; ++j ) + { + permutation[j] = perm[j]; + } + + dc_transformations.emplace_back( std::make_pair( std::get<0>( dc ), std::make_tuple( sg, std::get<2>( transf ), permutation ) ) ); + } + } + + if ( !dc_transformations.empty() ) + _dc_lib.insert( { tt_i, dc_transformations } ); + } + } + +private: + Ntk _database; + exact_library_params const _ps; + lib_t _super_lib; + dc_lib_t _dc_lib; +}; /* class exact_library */ + +} // namespace mockturtle diff --git a/include/mockturtle/utils/truth_table_cache.hpp b/include/mockturtle/utils/truth_table_cache.hpp new file mode 100644 index 0000000..2224e18 --- /dev/null +++ b/include/mockturtle/utils/truth_table_cache.hpp @@ -0,0 +1,172 @@ +/* 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 truth_table_cache.hpp + \brief Truth table cache + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace mockturtle +{ + +/*! \brief Truth table cache. + * + * A truth table cache is used to store truth tables. Many applications + * require to assign truth tables to nodes in a network. But since the truth + * tables often repeat, it is more convenient to store an index to a cache + * that stores the truth table. In order to reduce space, only one entry for + * a truth table and its complement are stored in the cache. To distinguish + * between both versions, the index to an entry in the truth table cache is + * represented as literal. The truth table whose function maps the input + * assignment \f$0, \dots, 0\f$ to \f$0\f$ is considered the *normal* truth + * table, while its complement is considered the complemented version. Only + * the normal truth table is stored at some index \f$i\f$. A positive literal + * \f$2i\f$ points to the normal truth table at index \f$i\f$. A negative + * literal \f$2i + 1\f$ points to the same truth table but returns its + * complement. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + truth_table_cache cache; + + kitty::dynamic_truth_table maj( 3 ); + kitty::create_majority( maj ); + auto l1 = cache.insert( maj ); // index is 0 + + auto tt = cache[l1 ^ 1]; // tt is ~maj + auto l2 = cache.insert( tt ); // index is 1 + + auto s = cache.size(); // size is 1 + \endverbatim + */ +template +class truth_table_cache +{ +public: + /*! \brief Creates a truth table cache and reserves memory. */ + truth_table_cache( uint32_t capacity = 1000u ); + + /*! \brief Inserts a truth table and returns a literal. + * + * To save space, only normal functions are stored in the truth table cache. + * A function is normal, if the input pattern \f$0, \dots, 0\f$ maps to + * \f$0\f$. If a function is not normal, its complement is inserted into the + * cache and a negative literal is returned. + * + * The default convention for literals is assumed. That is an index \f$i\f$ + * (starting) from \f$0\f$ has positive literal \f$2i\f$ and negative literal + * \f$2i + 1\f$. + * + * \param tt Truth table to insert + * \return Literal of position in cache + */ + uint32_t insert( TT tt ); + + /*! \brief Returns truth table for a given literal. + * + * The function requires that `lit` is smaller than `size()`. + */ + TT operator[]( uint32_t lit ) const; + + /*! \brief Returns number of normalized truth tables in the cache. */ + auto size() const { return _data.size(); } + + /*! \brief Resizes the cache. + * + * Reserve additional space for cache and data. + */ + void resize( uint32_t capacity ); + +private: + phmap::flat_hash_map> _indexes; + std::vector _data; +}; + +template +truth_table_cache::truth_table_cache( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +template +uint32_t truth_table_cache::insert( TT tt ) +{ + uint32_t is_compl{ 0 }; + + if ( kitty::get_bit( tt, 0 ) ) + { + is_compl = 1; + tt = ~tt; + } + + /* is truth table already in cache? */ + const auto it = _indexes.find( tt ); + if ( it != _indexes.end() ) + { + return static_cast( 2 * it->second + is_compl ); + } + + /* add truth table to end of cache */ + const auto size = _data.size(); + const auto index = static_cast( 2 * size + is_compl ); + _data.push_back( tt ); + _indexes[tt] = static_cast( size ); + return index; +} + +template +TT truth_table_cache::operator[]( uint32_t index ) const +{ + auto& entry = _data[index >> 1]; + return ( index & 1 ) ? ~entry : entry; +} + +template +void truth_table_cache::resize( uint32_t capacity ) +{ + _indexes.reserve( capacity ); + _data.reserve( capacity ); +} + +} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/truth_table_utils.hpp b/include/mockturtle/utils/truth_table_utils.hpp new file mode 100644 index 0000000..2a0d4fb --- /dev/null +++ b/include/mockturtle/utils/truth_table_utils.hpp @@ -0,0 +1,63 @@ +/* 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 truth_table_utils.hpp + \brief Truth table manipulation utils + + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include + +namespace mockturtle +{ + +/*! \brief Replacement rule of MAJ3 + * + * Given a majority gate with fanin functions `fanin0`, `fanin1` and `fanin2`, + * check if the first fanin `fanin0` can be replaced by `replacement` + * without changing the output function of the majority gate. + * + * By the replacement rule, ` = ` if and only if + * `(x ^ w)(y ^ z) = 0`, i.e., `y != z` implies `w = x`. + * + * This check is used in relevance optimization in MIG resubstitution. + * + * \param fanin0 Truth table of the first fanin function; also the fanin to be replaced. + * \param fanin1 Truth table of the second fanin function. + * \param fanin2 Truth table of the third fanin function. + * \param replacement Truth table of the candidate to replace `fanin0`. + * \return ` = ` + */ +template::value>> +bool can_replace_majority_fanin( TT const& fanin0, TT const& fanin1, TT const& fanin2, TT const& replacement ) +{ + return kitty::is_const0( ( ( fanin0 ^ replacement ) & ( fanin1 ^ fanin2 ) ) ); +} + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/utils/window_utils.hpp b/include/mockturtle/utils/window_utils.hpp new file mode 100644 index 0000000..6fce12d --- /dev/null +++ b/include/mockturtle/utils/window_utils.hpp @@ -0,0 +1,1046 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_utils.hpp + \brief Utilities to collect small-scale sets of nodes + + \author Heinz Riener +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +inline void collect_nodes_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using signal = typename Ntk::signal; + + if ( ntk.eval_color( n, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + return; + } + ntk.paint( n ); + + ntk.foreach_fanin( n, [&]( signal const& fi ) { + if ( ntk.is_constant( ntk.get_node( fi ) ) ) + return; + collect_nodes_recur( ntk, ntk.get_node( fi ), nodes ); + } ); + nodes.push_back( n ); +} + +} /* namespace detail */ + +/*! \brief Collect nodes in between of two node sets + * + * \param ntk A network + * \param inputs A node set + * \param outputs A signal set + * \return Nodes enclosed by inputs and outputs + * + * The output set has to be chosen in a way such that every path from + * PIs to outputs passes through at least one input. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template>> +inline std::vector collect_nodes( Ntk const& ntk, + std::vector const& inputs, + std::vector const& outputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* convert output signals to nodes */ + std::vector _outputs; + std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ), + [&ntk]( signal const& s ) { + return ntk.get_node( s ); + } ); + return collect_nodes( ntk, inputs, _outputs ); +} + +/*! \brief Collect nodes in between of two node sets + * + * \param ntk A network + * \param inputs A node set + * \param outputs A node set + * \return Nodes enclosed by inputs and outputs + * + * The output set has to be chosen in a way such that every path from + * PIs to outputs passes through at least one input. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template +inline std::vector collect_nodes( Ntk const& ntk, + std::vector const& inputs, + std::vector const& outputs ) +{ + using node = typename Ntk::node; + + ntk.new_color(); + + /* mark inputs visited */ + for ( auto const& i : inputs ) + { + if ( ntk.eval_color( i, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + ntk.paint( i ); + } + + /* recursively collect all nodes in between inputs and outputs */ + std::vector nodes; + for ( auto const& o : outputs ) + { + detail::collect_nodes_recur( ntk, o, nodes ); + } + return nodes; +} + +/*! \brief Identify inputs using reference counting + * + * Uses a new_color and marks all nodes and inputs. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `new_color` + * - `paint` + */ +template +std::vector collect_inputs( Ntk const& ntk, std::vector const& nodes ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* mark all nodes with a new color */ + ntk.new_color(); + for ( const auto& n : nodes ) + { + ntk.paint( n ); + } + + /* if a fanin is not colored, then it's an input */ + std::vector inputs; + for ( const auto& n : nodes ) + { + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const i = ntk.get_node( fi ); + if ( ntk.eval_color( i, [&ntk]( auto c ) { return c != ntk.current_color(); } ) ) + { + if ( std::find( std::begin( inputs ), std::end( inputs ), i ) == std::end( inputs ) ) + { + inputs.push_back( i ); + } + } + return true; + } ); + } + + /* mark all inputs */ + for ( const auto& n : inputs ) + { + ntk.paint( n ); + } + + return inputs; +} + +/*! \brief Identify outputs using reference counting + * + * Identify outputs using a reference counting approach. The + * algorithm counts the references of the fanins of all nodes and + * compares them with the fanout_sizes of the respective nodes. If + * reference count and fanout_size do not match, then the node is + * references outside of the node set and the respective is identified + * as an output. + * + * \param ntk A network + * \param inputs Inputs of a window + * \param nodes Inner nodes of a window (i.e., the intersection of + * inputs and nodes is assumed to be empty) + * \param refs Reference counters (in the size of the network and + * initialized to 0) + * \return Output signals of the window + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `fanout_size` + * - `foreach_fanin` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `make_signal` + */ +template +inline std::vector collect_outputs( Ntk const& ntk, + std::vector const& inputs, + std::vector const& nodes, + std::vector& refs ) +{ + using signal = typename Ntk::signal; + + std::vector outputs; + + /* mark the inputs visited */ + ntk.new_color(); + for ( auto const& i : inputs ) + { + ntk.paint( i ); + } + + /* reference fanins of nodes */ + for ( auto const& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + assert( !ntk.is_constant( n ) && !ntk.is_ci( n ) ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + refs[ntk.get_node( fi )] += 1; + } ); + } + + /* if the fanout_size of a node does not match the reference count, + the node has fanouts outside of the window is an output */ + for ( const auto& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + if ( ntk.fanout_size( n ) != refs[n] ) + { + outputs.emplace_back( ntk.make_signal( n ) ); + } + } + + /* dereference fanins of nodes */ + for ( auto const& n : nodes ) + { + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + continue; + } + + assert( !ntk.is_constant( n ) && !ntk.is_ci( n ) ); + ntk.foreach_fanin( n, [&]( signal const& fi ) { + refs[ntk.get_node( fi )] -= 1; + } ); + } + + return outputs; +} + +namespace detail +{ + +template +inline bool cut_is_trivial( Ntk const& ntk, std::vector const& inputs ) +{ + for ( const auto& n : inputs ) + { + if ( !ntk.is_constant( n ) && !ntk.is_ci( n ) ) + { + return false; + } + } + return true; +} + +} // namespace detail + +/*! \brief Performs in-place zero-cost expansion of a set of nodes towards TFI + * + * The algorithm attempts to derive a different cut of the same size + * that is closer to the network's PIs. This expansion towards TFI is + * called zero-cost because it merges nodes only if the number of + * inputs does not increase. + * + * Precondition: This procedure presumes that nodes and inputs are + * painted in the current color. + * + * Uses the current color to mark nodes. Only nodes not painted with + * the current color are considered for expanding the cut. Nodes + * marked are considered already in the cut. + * + * \param ntk A network + * \param inputs Input nodes + * \return True if and only if the inputs form a trivial cut that + * cannot be further extended, e.g., when the cut only + * consists of PIs. + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `get_node` + * - `paint` + * - `size` + */ +template +bool expand0_towards_tfi( Ntk const& ntk, std::vector& inputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /* we call a set of inputs (= a cut) trivial if all nodes are either + constants or CIs, such that they cannot be further expanded towards + the TFI */ + bool trivial_cut{ true }; + + /* repeat expansion towards TFI until a fix-point is reached */ + bool changed{ true }; + std::vector new_inputs; + while ( changed ) + { + trivial_cut = true; + changed = false; + + for ( auto it = std::begin( inputs ); it != std::end( inputs ); ) + { + assert( ntk.color( *it ) == ntk.current_color() ); + if ( ntk.is_constant( *it ) || ntk.is_ci( *it ) ) + { + ++it; + continue; + } + trivial_cut = false; + + /* count how many fanins are already in the cut */ + uint32_t count_fanin_outside{ 0 }; + std::optional ep; + ntk.foreach_fanin( *it, [&]( signal const& fi ) { + node const n = ntk.get_node( fi ); + if ( ntk.eval_color( n, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + ++count_fanin_outside; + } + else + { + ep = n; + } + } ); + + /* if the expansion is not cost-free, then proceeded with the next leaf */ + if ( count_fanin_outside + 1 < ntk.fanin_size( *it ) ) + { + ++it; + continue; + } + + if ( ep ) + { + if ( ntk.eval_color( *ep, [&ntk]( auto c ) { return c != ntk.current_color(); } ) ) + { + new_inputs.push_back( *ep ); + ntk.paint( *ep ); + } + } + it = inputs.erase( it ); + changed = true; + } + + std::copy( std::begin( new_inputs ), std::end( new_inputs ), + std::back_inserter( inputs ) ); + new_inputs.clear(); + } + + assert( trivial_cut == detail::cut_is_trivial( ntk, inputs ) ); + return trivial_cut; +} + +namespace detail +{ + +template +inline void evaluate_fanin( typename Ntk::node const& n, std::vector>& candidates ) +{ + auto it = std::find_if( std::begin( candidates ), std::end( candidates ), + [&n]( auto const& p ) { + return p.first == n; + } ); + if ( it == std::end( candidates ) ) + { + /* new fanin: referenced for the 1st time */ + candidates.push_back( std::make_pair( n, 1u ) ); + } + else + { + /* otherwise, if not new, then just increase the reference counter */ + ++it->second; + } +} + +template +inline typename Ntk::node select_next_fanin_to_expand_tfi( Ntk const& ntk, std::vector const& inputs ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + assert( inputs.size() > 0u && "inputs must not be empty" ); + assert( !cut_is_trivial( ntk, inputs ) ); + + /* evaluate the fanins with respect to their costs (how often are they referenced?) */ + std::vector> candidates; + for ( auto const& i : inputs ) + { + if ( ntk.is_constant( i ) || ntk.is_ci( i ) ) + { + continue; + } + + ntk.foreach_fanin( i, [&]( signal const& fi ) { + if ( ntk.is_constant( ntk.get_node( fi ) ) ) + { + return true; + } + detail::evaluate_fanin( ntk.get_node( fi ), candidates ); + return true; + } ); + } + + assert( candidates.size() > 0u ); + + /* select the fanin with maximum reference count; if two fanins have equal reference count, select the one with more fanouts */ + std::pair best_fanin{ candidates[0] }; + for ( auto const& candidate : candidates ) + { + if ( candidate.second > best_fanin.second || + ( candidate.second == best_fanin.second && ntk.fanout_size( candidate.first ) > ntk.fanout_size( best_fanin.first ) ) ) + { + best_fanin = candidate; + } + } + + /* as long as the inputs do not form a trivial cut, this procedure will always find a fanin to expand */ + assert( best_fanin.first != 0 ); + + return best_fanin.first; +} + +} /* namespace detail */ + +/*! \brief Performs in-place expansion of a set of nodes towards TFI + * + * Expand the inputs towards TFI by iteratively selecting the fanins + * with the highest reference count within the cut and highest number + * of fanouts. Expansion continues until either `inputs` forms a + * trivial cut or the `inputs`'s size reaches `input_limit`. The + * procedure allows a temporary increase of `inputs` beyond the + * `input_limit` for at most `MAX_ITERATIONS`. + * + * Precondition: This procedure presumes that nodes and inputs are + * painted in the current color. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes + * \param input_limit Size limit for the maximum number of input nodes + */ +template +void expand_towards_tfi( Ntk const& ntk, std::vector& inputs, uint32_t input_limit ) +{ + using node = typename Ntk::node; + + static constexpr uint32_t const MAX_ITERATIONS{ 5u }; + + if ( expand0_towards_tfi( ntk, inputs ) ) + { + return; + } + + std::optional> best_cut; + if ( inputs.size() <= input_limit ) + { + best_cut = inputs; + } + + bool trivial_cut = false; + uint32_t iterations{ 0 }; + while ( !trivial_cut && ( inputs.size() <= input_limit || iterations < MAX_ITERATIONS ) ) + { + node const n = detail::select_next_fanin_to_expand_tfi( ntk, inputs ); + inputs.push_back( n ); + ntk.paint( n ); + + trivial_cut = expand0_towards_tfi( ntk, inputs ); + assert( trivial_cut == detail::cut_is_trivial( ntk, inputs ) ); + + iterations = inputs.size() > input_limit ? iterations + 1 : 0; + if ( inputs.size() <= input_limit && + ( !best_cut || best_cut->size() <= inputs.size() ) ) + { + best_cut = inputs; + } + } + + if ( best_cut ) + { + inputs = *best_cut; + } + else + { + assert( inputs.size() > input_limit ); + } +} + +/*! \brief Performs in-place expansion of a set of nodes towards TFO + * + * Iteratively expands the inner nodes of the window with those + * fanouts that are supported by the window until a fixed-point is + * reached. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes of a window + * \param nodes Inner nodes of a window + * + * **Required network functions:** + * - `current_color` + * - `eval_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `new_color` + */ +template +void expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + auto explore_fanouts = [&]( Ntk const& ntk, node const& n, std::set& result ) { + ntk.foreach_fanout( n, [&]( node const& fo, uint64_t index ) { + /* only look at the first few fanouts */ + if ( index > 5 ) + { + return false; + } + /* skip all nodes that are already in nodes */ + if ( ntk.eval_color( fo, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + return true; + } + result.insert( fo ); + return true; + } ); + }; + + /* create a new traversal ID */ + ntk.new_color(); + + /* mark the inputs visited */ + std::for_each( std::begin( inputs ), std::end( inputs ), + [&ntk]( node const& n ) { ntk.paint( n ); } ); + + /* mark the nodes visited */ + std::for_each( std::begin( nodes ), std::end( nodes ), + [&ntk]( node const& n ) { ntk.paint( n ); } ); + + /* collect all nodes that have fanouts not yet contained in nodes */ + std::set eps; + for ( const auto& i : inputs ) + { + explore_fanouts( ntk, i, eps ); + } + for ( const auto& n : nodes ) + { + explore_fanouts( ntk, n, eps ); + } + + bool changed = true; + std::set new_eps; + while ( changed ) + { + new_eps.clear(); + changed = false; + + auto it = std::begin( eps ); + while ( it != std::end( eps ) ) + { + node const ep = *it; + if ( ntk.eval_color( ep, [&]( auto c ) { return c == ntk.current_color(); } ) ) + { + it = eps.erase( it ); + continue; + } + + bool all_children_belong_to_window = true; + ntk.foreach_fanin( ep, [&]( signal const& fi ) { + node const child = ntk.get_node( fi ); + if ( ntk.eval_color( child, [&]( auto c ) { return c != ntk.current_color(); } ) ) + { + all_children_belong_to_window = false; + return false; + } + return true; + } ); + + if ( all_children_belong_to_window ) + { + assert( ep != 0 ); + assert( !ntk.is_ci( ep ) ); + nodes.emplace_back( ep ); + ntk.paint( ep ); + it = eps.erase( it ); + + explore_fanouts( ntk, ep, new_eps ); + } + + if ( it != std::end( eps ) ) + { + ++it; + } + } + + if ( !new_eps.empty() ) + { + eps.insert( std::begin( new_eps ), std::end( new_eps ) ); + changed = true; + } + } +} + +namespace detail +{ + +template +void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes, + std::vector>& levels ) +{ + using node = typename Ntk::node; + + static constexpr uint32_t const MAX_FANOUTS{ 5u }; + + ntk.new_color(); + + /* mapping from level to nodes (which nodes are on a certain level?) */ + levels.resize( ntk.depth() + 1 ); + + /* list of indices of used levels (avoid iterating over all levels) */ + std::vector used; + + /* mark all inputs and fill their level information into `levels` and `used` */ + for ( const auto& i : inputs ) + { + uint32_t const node_level = ntk.level( i ); + ntk.paint( i ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( i ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + } + } + + /* mark all nodes and fill their level information into `levels` and `used` */ + for ( const auto& n : nodes ) + { + uint32_t const node_level = ntk.level( n ); + ntk.paint( n ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( n ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + } + } + + std::stable_sort( std::begin( used ), std::end( used ) ); + + for ( uint32_t index = 0u; index < used.size(); ++index ) + { + std::vector& level = levels.at( used[index] ); + for ( auto j = 0u; j < level.size(); ++j ) + { + ntk.foreach_fanout( level[j], [&]( node const& fo, uint64_t index ) { + /* avoid getting stuck on nodes with many fanouts */ + if ( index == MAX_FANOUTS ) + { + return false; + } + + /* ignore nodes without fanins */ + if ( ntk.is_constant( fo ) || ntk.is_ci( fo ) ) + { + return true; + } + + if ( ntk.eval_color( fo, [&ntk]( auto c ) { return c != ntk.current_color(); } ) && + ntk.eval_fanins_color( fo, [&ntk]( auto c ) { return c == ntk.current_color(); } ) ) + { + /* add fanout to nodes */ + nodes.push_back( fo ); + + /* update data structured */ + uint32_t const node_level = ntk.level( fo ); + ntk.paint( fo ); + if constexpr ( auto_resize ) + { + if ( levels.size() <= node_level ) + { + levels.resize( std::max( uint32_t( 2 * levels.size() ), node_level ) ); + } + } + levels.at( node_level ).push_back( fo ); + if ( std::find( std::begin( used ), std::end( used ), node_level ) == std::end( used ) ) + { + used.push_back( node_level ); + std::stable_sort( std::begin( used ), std::end( used ) ); + } + } + + return true; + } ); + } + level.clear(); + } +} + +} // namespace detail + +/*! \brief Performs in-place expansion of a set of nodes towards TFO + * + * Iteratively expands the inner nodes of the window with those + * fanouts that are supported by the window. Explores the fanouts + * level by level. Starting with those that are closest to the + * inputs. + * + * Uses a new color. + * + * \param ntk A network + * \param inputs Input nodes of a window + * \param nodes Inner nodes of a window + * + * **Required network functions:** + * - `current_color` + * - `depth` + * - `eval_color` + * - `eval_fanins_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `level` + * - `new_color` + * - `paint` + */ +template +void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes ) +{ + std::vector> levels; + detail::levelized_expand_towards_tfo( ntk, inputs, nodes, levels ); +} + +namespace detail +{ + +template +void cover_recursive( Ntk const& ntk, typename Ntk::node const& root, std::vector& nodes ) +{ + if ( ntk.color( root ) == ntk.current_color() ) + { + return; + } + + ntk.foreach_fanin( root, [&]( auto const& fi ) { + cover_recursive( ntk, ntk.get_node( fi ), nodes ); + } ); + + nodes.push_back( root ); +} + +} // namespace detail + +template +std::vector cover( Ntk const& ntk, typename Ntk::node const& root, std::vector const& leaves ) +{ + ntk.new_color(); + for ( auto const& l : leaves ) + { + ntk.paint( l ); + } + + std::vector nodes; + detail::cover_recursive( ntk, root, nodes ); + + /* remove duplicates */ + std::stable_sort( std::begin( nodes ), std::end( nodes ) ); + auto last = std::unique( std::begin( nodes ), std::end( nodes ) ); + nodes.erase( last, std::end( nodes ) ); + + return nodes; +} + +/*! \brief Create a (l,k)-window around a pivot. + * + * Expands a reconvergency rooted in a given pivot node `p` into a + * window with l inputs and k outputs. + * + * Uses a new color. + * + * **Required network functions:** + * - `current_color` + * - `depth` + * - `eval_color` + * - `eval_fanins_color` + * - `foreach_fanin` + * - `foreach_fanout` + * - `get_node` + * - `is_ci` + * - `is_constant` + * - `level` + * - `new_color` + * - `paint` + */ +template +class create_window_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct window + { + std::vector inputs; + std::vector nodes; + std::vector outputs; + }; + +protected: + /* constant node used to denotes invalid window element */ + static constexpr node INVALID_NODE{ 0 }; + + /* number of iterations */ + static constexpr uint32_t NUM_ITERATIONS{ 5 }; + +public: + create_window_impl( Ntk const& ntk ) + : ntk( ntk ), path( ntk.size() ), refs( ntk.size() ) + { + } + + void resize( uint32_t size ) + { + path.resize( size ); + refs.resize( size ); + } + + std::optional run( node const& pivot, uint32_t cut_size, uint32_t num_levels ) + { + /* find a reconvergence from the pivot and collect the nodes */ + std::optional> nodes; + if ( !( nodes = identify_reconvergence( pivot, num_levels ) ) ) + { + /* if there is no reconvergence, then optimization is not possible */ + return std::nullopt; + } + + /* collect the fanins for these nodes */ + std::vector inputs = collect_inputs( ntk, *nodes ); + if ( inputs.size() <= cut_size + 3 ) + { + /* expand the nodes towards the TFI */ + expand_towards_tfi( ntk, inputs, cut_size ); + + /* compute the cover of the (pivot, inputs)-cut */ + *nodes = cover( ntk, pivot, inputs ); + + /* expand the nodes towards the TFO */ + std::stable_sort( std::begin( inputs ), std::end( inputs ) ); + detail::levelized_expand_towards_tfo( ntk, inputs, *nodes, levels ); + } + + if ( inputs.size() > cut_size || nodes->empty() ) + { + return std::nullopt; + } + + /* top. sort nodes */ + std::stable_sort( std::begin( inputs ), std::end( inputs ) ); + std::stable_sort( std::begin( *nodes ), std::end( *nodes ) ); + + /* collect the nodes with fanout outside of nodes */ + std::vector outputs = collect_outputs( ntk, inputs, *nodes, refs ); + assert( outputs.size() > 0u ); + + return window{ inputs, *nodes, outputs }; + } + +protected: + std::optional> identify_reconvergence( node const& pivot, uint64_t num_iterations ) + { + assert( !ntk.is_ci( pivot ) && !ntk.is_constant( pivot ) ); + + ntk.new_color(); + visited.clear(); + ntk.foreach_fanin( pivot, [&]( signal const& fi ) { + uint32_t const color = ntk.new_color(); + node const& n = ntk.get_node( fi ); + path[n] = INVALID_NODE; + visited.push_back( n ); + ntk.paint( n, color ); + } ); + + uint64_t start{ 0 }; + uint64_t stop; + for ( uint32_t iteration = 0u; iteration < num_iterations; ++iteration ) + { + stop = visited.size(); + for ( uint32_t i = start; i < stop; ++i ) + { + node const n = visited.at( i ); + std::optional meet = explore_frontier_of_node( n ); + if ( meet ) + { + visited.clear(); + gather_nodes_recursively( path[*meet] ); + gather_nodes_recursively( n ); + visited.push_back( pivot ); + return visited; + } + } + start = stop; + } + + return std::nullopt; + } + + std::optional explore_frontier_of_node( node const& n ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) ) + { + return std::nullopt; + } + + std::optional meet; + ntk.foreach_fanin( n, [&]( signal const& fi ) { + node const& fi_node = ntk.get_node( fi ); + if ( ntk.eval_color( n, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) && + ntk.eval_color( fi_node, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) && + ntk.eval_color( n, fi_node, []( auto c0, auto c1 ) { return c0 != c1; } ) ) + { + meet = fi_node; + return false; + } + + if ( ntk.eval_color( fi_node, [this]( auto c ) { return c > ntk.current_color() - ntk.max_fanin_size; } ) ) + { + return true; /* next */ + } + + ntk.paint( fi_node, n ); + path[fi_node] = n; + visited.push_back( fi_node ); + + return true; /* next */ + } ); + + return meet; + } + + /* collect nodes recursively following along the `path` until INVALID_NODE is reached */ + void gather_nodes_recursively( node const& n ) + { + if ( n == INVALID_NODE ) + { + return; + } + + visited.push_back( n ); + + node const pred = path[n]; + if ( pred == INVALID_NODE ) + { + return; + } + + assert( ntk.eval_color( n, pred, []( auto c0, auto c1 ) { return c0 == c1; } ) ); + gather_nodes_recursively( pred ); + } + +protected: + Ntk const& ntk; + std::vector visited; + std::vector path; + std::vector refs; + std::vector> levels; +}; /* create_window_impl */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/views/binding_view.hpp b/include/mockturtle/views/binding_view.hpp new file mode 100644 index 0000000..1eb529a --- /dev/null +++ b/include/mockturtle/views/binding_view.hpp @@ -0,0 +1,261 @@ +/* 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 binding_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../io/genlib_reader.hpp" +#include "../utils/node_map.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Adds bindings to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements gates contained in a technology library. This view + * is returned by the technology mapping command `map`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_binding`, + * `remove_binding`, `add_binding`, `add_binding_with_check`, `get_binding`, + * `get_binding_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_gates_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + binding_view res = map( aig, tech_lib ); + + // prints stats and gates usage + res.report_stats(); + res.report_gates_usage(); + + // write the mapped network in verilog + write_verilog_with_binding( res, "file.v" ); + \endverbatim + */ +template +class binding_view : public Ntk +{ +public: + using node = typename Ntk::node; + +public: + explicit binding_view( std::vector const& library ) + : Ntk(), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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" ); + } + + explicit binding_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _bindings( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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" ); + } + + binding_view& operator=( binding_view const& binding_ntk ) + { + Ntk::operator=( binding_ntk ); + _library = binding_ntk._library; + _bindings = binding_ntk._bindings; + return *this; + } + + void add_binding( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + _bindings[n] = gate_id; + } + + bool add_binding_with_check( node const& n, uint32_t gate_id ) + { + assert( gate_id < _library.size() ); + + auto const& cell = _library[gate_id]; + + if ( Ntk::node_function( n ) == cell.function ) + { + _bindings[n] = gate_id; + return true; + } + return false; + } + + void remove_binding( node const& n ) const + { + _bindings.erase( n ); + } + + const gate& get_binding( node const& n ) const + { + return _library[_bindings[n]]; + } + + bool has_binding( node const& n ) const + { + return _bindings.has( n ); + } + + unsigned int get_binding_index( node const& n ) const + { + return _bindings[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + area += get_binding( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + node_map delays( *this ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n] = 0; + return true; + } + + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + double gate_delay = 0; + Ntk::foreach_fanin( n, [&]( auto const& f, auto i ) { + gate_delay = std::max( gate_delay, (double)( delays[f] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ) ); + } ); + delays[n] = gate_delay; + worst_delay = std::max( worst_delay, gate_delay ); + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_gates_usage( std::ostream& os = std::cout ) const + { + std::vector gates_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_binding( n ) ) + { + auto const& g = get_binding( n ); + ++gates_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report gates usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < gates_profile.size(); ++i ) + { + if ( gates_profile[i] > 0u ) + { + float tot_gate_area = gates_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", gates_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_gate_area ) + << fmt::format( " {:>8.2f} %\n", tot_gate_area / area * 100 ); + + tot_instances += gates_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _bindings; +}; /* binding_view */ + +template +binding_view( T const& ) -> binding_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/cell_view.hpp b/include/mockturtle/views/cell_view.hpp new file mode 100644 index 0000000..505cea8 --- /dev/null +++ b/include/mockturtle/views/cell_view.hpp @@ -0,0 +1,297 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file cell_view.hpp + \brief Implements methods to bind the network to a standard cell library + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../utils/node_map.hpp" +#include "../utils/standard_cell.hpp" +#include "../views/topo_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Adds cells to a technology library and mapping API methods. + * + * This view adds methods to create and manage a mapped network that + * implements cells contained in a technology library. This view + * is returned by the technology mapping command `emap`. It can be used + * to report statistics about the network and write the network into + * a verilog file. It always adds the functions `has_cell`, + * `remove_cell`, `add_cell`, `add_cell_with_check`, `get_cell`, + * `get_cell_index`, `get_library`, `compute_area`, `compute_worst_delay`, + * `report_stats`, and `report_cells_usage`. + * + * **Required network functions:** + * - `size` + * - `foreach_node` + * - `foreach_fanin` + * - `is_constant` + * - `is_pi` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // read cell library in genlib format + std::vector gates; + lorina::read_genlib( "file.genlib", genlib_reader( gates ) ) + tech_library tech_lib( gates ); + + // call technology mapping to obtain the view + cell_view res = emap_block( aig, tech_lib ); + + // prints stats and cells usage + res.report_stats(); + res.report_cells_usage(); + \endverbatim + */ +template +class cell_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit cell_view( std::vector const& library ) + : Ntk(), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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" ); + } + + explicit cell_view( Ntk const& ntk, std::vector const& library ) + : Ntk( ntk ), _library{ library }, _cells( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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" ); + } + + cell_view& operator=( cell_view const& cell_ntk ) + { + Ntk::operator=( cell_ntk ); + _library = cell_ntk._library; + _cells = cell_ntk._cells; + return *this; + } + + void add_cell( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + _cells[n] = cell_id; + } + + bool add_cell_with_check( node const& n, uint32_t cell_id ) + { + assert( cell_id < _library.size() ); + + auto const& cell = _library[cell_id]; + + if constexpr ( has_num_outputs_v && has_node_function_v ) + { + if ( Ntk::num_outputs( n ) != cell.gates.size() ) + return false; + + for ( uint32_t i = 0; i < Ntk::num_outputs( n ); ++i ) + { + if ( Ntk::node_function_pin( n, i ) != cell.gates[i].function ) + { + return false; + } + } + + _cells[n] = cell_id; + return true; + } + + if ( cell.gates.size() > 1 ) + return false; + + if ( Ntk::node_function( n ) == cell.gates[0].function ) + { + _cells[n] = cell_id; + return true; + } + + return false; + } + + void remove_cell( node const& n ) const + { + _cells.erase( n ); + } + + const standard_cell& get_cell( node const& n ) const + { + return _library[_cells[n]]; + } + + bool has_cell( node const& n ) const + { + return _cells.has( n ); + } + + unsigned int get_cell_index( node const& n ) const + { + return _cells[n]; + } + + const std::vector& get_library() const + { + return _library; + } + + double compute_area() const + { + double area = 0; + Ntk::foreach_node( [&]( auto const& n, auto ) { + if ( has_cell( n ) ) + { + area += get_cell( n ).area; + } + } ); + + return area; + } + + double compute_worst_delay() const + { + topo_view ntk_topo{ *this }; + std::vector> delays( Ntk::size() ); + double worst_delay = 0; + + ntk_topo.foreach_node( [&]( auto const& n, auto ) { + if ( Ntk::is_constant( n ) || Ntk::is_pi( n ) ) + { + delays[n].push_back( 0 ); + return true; + } + + if ( has_cell( n ) ) + { + auto const& cell = get_cell( n ); + + for ( gate const& g : cell.gates ) + { + double cell_delay = 0; + if constexpr ( has_get_output_pin_v ) + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )][Ntk::get_output_pin( f )] + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + else + { + Ntk::foreach_fanin( n, [&]( signal const& f, auto i ) { + cell_delay = std::max( cell_delay, delays[Ntk::get_node( f )].front() + std::max( g.pins[i].rise_block_delay, g.pins[i].fall_block_delay ) ); + } ); + } + delays[n].push_back( cell_delay ); + worst_delay = std::max( worst_delay, cell_delay ); + } + } + else + { + worst_delay = -1; + return false; + } + return true; + } ); + + return worst_delay; + } + + void report_stats( std::ostream& os = std::cout ) const + { + os << fmt::format( "[i] Report stats: area = {:>5.2f}; delay = {:>5.2f};\n", compute_area(), compute_worst_delay() ); + } + + void report_cells_usage( std::ostream& os = std::cout ) const + { + std::vector cells_profile( _library.size(), 0u ); + + double area = 0; + Ntk::foreach_node( [&]( node const& n, auto ) { + if ( has_cell( n ) ) + { + auto const& g = get_cell( n ); + ++cells_profile[g.id]; + area += g.area; + } + } ); + + os << "[i] Report cells usage:\n"; + + uint32_t tot_instances = 0u; + for ( auto i = 0u; i < cells_profile.size(); ++i ) + { + if ( cells_profile[i] > 0u ) + { + float tot_cell_area = cells_profile[i] * _library[i].area; + + os << fmt::format( "[i] {:<25}", _library[i].name ) + << fmt::format( "\t Instance = {:>10d}", cells_profile[i] ) + << fmt::format( "\t Area = {:>12.2f}", tot_cell_area ) + << fmt::format( " {:>8.2f} %\n", tot_cell_area / area * 100 ); + + tot_instances += cells_profile[i]; + } + } + + os << fmt::format( "[i] {:<25}", "TOTAL" ) + << fmt::format( "\t Instance = {:>10d}", tot_instances ) + << fmt::format( "\t Area = {:>12.2f} 100.00 %\n", area ); + } + +private: + std::vector _library; + node_map> _cells; +}; /* cell_view */ + +template +cell_view( T const& ) -> cell_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/choice_view.hpp b/include/mockturtle/views/choice_view.hpp new file mode 100644 index 0000000..a65eb3c --- /dev/null +++ b/include/mockturtle/views/choice_view.hpp @@ -0,0 +1,592 @@ +/* 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 choice_view.hpp + \brief Implement choices in network + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../networks/events.hpp" +#include "../traits.hpp" + +namespace mockturtle +{ + +struct choice_view_params +{ + bool add_choices_on_substitute{ true }; + bool update_on_add{ true }; +}; + +/*! \brief Implements choices in network + * + * Overrides the interface methods `substitute_node`, `incr_fanout_size`, + * `decr_fanout_size`, `fanout_size`. + * + * This class manages equivalent nodes keeping them saved as alternatives in + * the network. Each node belongs to an equivalence class in which a node + * is the class representative, by default the one with the lowest index. + * Equivalence classes are saved as linked lists. The `_choice_repr` vector + * associates each node to the next one in the linked list (the one closer + * to the representative). The representative is the tail and "points" at itself. + * The `_choice_phase` vector is used to save the polarity of each node in the + * class with respect to the representative. The representative uses its field + * to point to the head of the list. + * + * This view is not compatible with `fanout_view`. + * + * **Required network functions:** + * - `get_node` + * - `size` + * - `node_to_index` + * - `index_to_node` + * - `is_complemented` + * - `make_signal` + */ +template> +class choice_view +{ +}; + +template +class choice_view : public Ntk +{ +public: + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class choice_view : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + choice_view( choice_view_params const& ps = {} ) + : Ntk(), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view( Ntk const& ntk, choice_view_params const& ps = {} ) + : Ntk( ntk ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_index_to_node_v, "Ntk does not implement the index_to_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + + init_choice_classes(); + + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + } + + choice_view& operator=( choice_view const& choice_ntk ) + { + Ntk::operator=( choice_ntk ); + if ( this != &choice_ntk ) + { + this->_choice_repr = choice_ntk._choice_repr; + this->_choice_phase = choice_ntk._choice_phase; + this->_ps = choice_ntk._ps; + } + if ( _ps.update_on_add ) + { + _add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + on_add( n ); + } ); + } + return *this; + } + + ~choice_view() + { + if ( _ps.update_on_add ) + { + Ntk::events().release_add_event( _add_event ); + } + } + + template + std::enable_if_t, void> add_choice( node const& n1, node const& n2 ) + { + add_choice( n1, Ntk::make_signal( n2 ) ); + } + + void add_choice( node const& n1, signal const& s2 ) + { + auto const n2 = Ntk::get_node( s2 ); + auto const id1 = Ntk::node_to_index( n1 ); + auto const id2 = Ntk::node_to_index( n2 ); + + if ( id1 == id2 ) + { + /* same node */ + return; + } + + auto rep1 = get_choice_representative( n1 ); + auto rep2 = get_choice_representative( n2 ); + + auto idrep1 = Ntk::node_to_index( rep1 ); + auto idrep2 = Ntk::node_to_index( rep2 ); + + if ( idrep1 == idrep2 ) + { + /* already in the same equivalence class */ + return; + } + + /* set the representative as the node with lowest index */ + if ( idrep1 > idrep2 ) + { + std::swap( rep1, rep2 ); + std::swap( idrep1, idrep2 ); + } + + /* merge the eq lists */ + bool inv = false; + if ( ( ( _choice_repr->at( id1 ) != n1 && Ntk::is_complemented( _choice_phase->at( id1 ) ) ) != Ntk::is_complemented( s2 ) ) != + ( _choice_repr->at( id2 ) != n2 && Ntk::is_complemented( _choice_phase->at( id2 ) ) ) ) + { + /* before merging, complement nodes accordingly to the new representative phase, if needed */ + invert_phases_in_class( rep2 ); + inv = true; + } + _choice_repr->at( idrep2 ) = Ntk::get_node( _choice_phase->at( idrep1 ) ); + _choice_phase->at( idrep1 ) = _choice_phase->at( idrep2 ); + /* store the right phase */ + _choice_phase->at( idrep2 ) = Ntk::make_signal( rep2 ) ^ inv; + } + + /* Set sig as an equivalence representative of n without including n in the choice list */ + void set_representative( node const& n, signal const& sig ) + { + /* TODO: TEST */ + auto nsig = Ntk::get_node( sig ); + auto repr = get_choice_representative( nsig ); + bool c = false; + + if ( repr != nsig ) + { + c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( nsig ) ) ); + } + _choice_repr->at( Ntk::node_to_index( n ) ) = repr; + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( n ) ^ ( Ntk::is_complemented( sig ) ^ c ); + } + + void update_choice_representative( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + if ( is_choice_representative( n ) ) + return; + + bool inv = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + auto repr = get_choice_representative( n ); + _choice_phase->at( Ntk::node_to_index( n ) ) = Ntk::make_signal( _choice_repr->at( Ntk::node_to_index( n ) ) ); + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + _choice_repr->at( Ntk::node_to_index( repr ) ) = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ); + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( repr ); + if ( inv ) + { + invert_phases_in_class( n ); + } + } + + /* returns the new class representative */ + std::optional remove_choice( node const& n ) + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto next = Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( n ) ) ); + auto repr = Ntk::node_to_index( get_choice_representative( next ) ); + auto tail = Ntk::node_to_index( Ntk::get_node( _choice_phase->at( Ntk::node_to_index( repr ) ) ) ); + + /* if n is a representative, recompute the representative of the new class */ + if ( repr == Ntk::node_to_index( n ) && tail != Ntk::node_to_index( n ) ) + { + auto new_repr = tail; + node pred = tail; + foreach_choice( tail, [&]( auto const& g ) { + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( g ) < new_repr ) + { + new_repr = Ntk::node_to_index( g ); + } + if ( Ntk::node_to_index( g ) != repr && Ntk::node_to_index( _choice_repr->at( g ) ) == repr ) + { + pred = Ntk::node_to_index( g ); + } + return true; + } ); + auto const new_repr_signal = _choice_phase->at( new_repr ); + bool polarity = Ntk::is_complemented( new_repr_signal ); + if ( new_repr == pred ) + { + _choice_phase->at( new_repr ) = _choice_phase->at( repr ); + } + else + { + _choice_phase->at( new_repr ) = Ntk::make_signal( _choice_repr->at( new_repr ) ); + _choice_repr->at( pred ) = Ntk::index_to_node( tail ); + } + _choice_repr->at( new_repr ) = Ntk::index_to_node( new_repr ); + if ( polarity ) + { + invert_phases_in_class( new_repr ); + } + return new_repr_signal; + } + else if ( tail == n ) + { + _choice_phase->at( Ntk::node_to_index( repr ) ) = Ntk::make_signal( next ); + } + else + { + while ( Ntk::node_to_index( _choice_repr->at( tail ) ) != Ntk::node_to_index( n ) ) + { + tail = Ntk::node_to_index( _choice_repr->at( tail ) ); + } + _choice_repr->at( tail ) = Ntk::index_to_node( next ); + } + + _choice_repr->at( Ntk::node_to_index( n ) ) = n; + return std::nullopt; + } + + void clear_choices() + { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + bool delete_choice_from_network( node const& n ) + { + if ( Ntk::is_dead( n ) || !is_choice( n ) ) + { + /* node is already dead or not dangling */ + return false; + } + + Ntk::take_out_node( n ); + remove_choice( n ); + return true; + } + + node get_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + + auto rep = _choice_repr->at( Ntk::node_to_index( n ) ); + while ( Ntk::node_to_index( rep ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( rep ) ) ) ) + { + rep = _choice_repr->at( Ntk::node_to_index( rep ) ); + } + return rep; + } + + bool is_choice_representative( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + return _choice_repr->at( Ntk::node_to_index( n ) ) == n; + } + + signal get_choice_representative_signal( node const& n ) const + { + auto repr = Ntk::make_signal( get_choice_representative( n ) ); + + if ( Ntk::get_node( repr ) == n ) + { + return repr; + } + + return repr ^ Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ); + } + + template + std::enable_if_t, typename T::signal> get_choice_representative_signal( signal const& sig ) const + { + auto n = Ntk::get_node( sig ); + auto repr = get_choice_representative( n ); + + if ( repr == n ) + { + return sig; + } + + bool c = Ntk::is_complemented( _choice_phase->at( Ntk::node_to_index( n ) ) ) != Ntk::is_complemented( sig ); + return Ntk::make_signal( repr ) ^ c; + } + + uint32_t count_choices( node const& n ) const + { + assert( Ntk::node_to_index( n ) < Ntk::size() ); + uint32_t size = 1u; + auto p = n; + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + size++; + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + size++; + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + return size; + } + + template + void foreach_choice( node const& n, Fn&& fn ) const + { + auto p = n; + if ( !fn( p ) ) + { + return; + } + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + p = _choice_repr->at( Ntk::node_to_index( p ) ); + if ( !fn( p ) ) + { + return; + } + } + p = Ntk::get_node( _choice_phase->at( Ntk::node_to_index( p ) ) ); + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( n ) ) + { + if ( !fn( p ) ) + { + return; + } + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + /* redefine node substitution */ + void substitute_node( node const& old_node, signal const& new_signal ) + { + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + if ( _ps.add_choices_on_substitute ) + { + add_choice( _old, _new ); + } + // TODO: add replace choice mode + + for ( auto idx = 1u; idx < Ntk::_storage->nodes.size(); ++idx ) + { + if ( Ntk::is_ci( idx ) || Ntk::is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = Ntk::replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + Ntk::replace_in_outputs( _old, _new ); + + // set old node as choice, reset fanout + Ntk::_storage->nodes[_old].data[0].h1 &= UINT32_C( 0xC0000000 ); + take_out_choice( _old ); + + if ( is_choice( Ntk::get_node( _new ) ) && fanout_size( Ntk::get_node( _new ) ) > 0u ) + { + take_in_choice( Ntk::get_node( _new ) ); + } + } + } + + void take_out_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + set_choice_flag( n ); + Ntk::_storage->hash.erase( nobj ); + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + /* set childrens in MFFC as choice, decrement the fanout count */ + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_choice( nobj.children[i].index ); + } + } + } + + void take_in_choice( node const& n ) + { + /* we cannot delete CIs or constants */ + if ( n == 0 || Ntk::is_ci( n ) ) + return; + + auto& nobj = Ntk::_storage->nodes[n]; + reset_choice_flag( n ); + Ntk::_storage->hash[nobj] = n; + + for ( auto i = 0u; i < Ntk::fanin_size( n ); ++i ) + { + /* restore choice childrens in MFFC, increment the fanout count */ + if ( incr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_in_choice( nobj.children[i].index ); + } + } + } + + /* redefine methods for choice flag: storage h1 = dead(31), choice(30), fanout_size(29 to 0) */ + uint32_t fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t incr_fanout_size( node const& n ) const + { + return Ntk::_storage->nodes[n].data[0].h1++ & UINT32_C( 0x3FFFFFFF ); + } + + uint32_t decr_fanout_size( node const& n ) const + { + return --Ntk::_storage->nodes[n].data[0].h1 & UINT32_C( 0x3FFFFFFF ); + } + + inline bool is_choice( node const& n ) const + { + return ( Ntk::_storage->nodes[n].data[0].h1 >> 30 ) & 1; + } + +private: + inline void set_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 |= UINT32_C( 0x40000000 ); + } + + inline void reset_choice_flag( node const& n ) const + { + Ntk::_storage->nodes[n].data[0].h1 &= UINT32_C( 0xBFFFFFFF ); + } + + void init_choice_classes() + { + _choice_repr = std::make_shared>( Ntk::size() ); + _choice_phase = std::make_shared>( Ntk::size() ); + // Ntk::foreach_node( [&]( auto n ) { + for ( auto i = 0u; i < Ntk::size(); i++ ) + { + _choice_repr->at( i ) = Ntk::index_to_node( i ); + _choice_phase->at( i ) = Ntk::make_signal( Ntk::index_to_node( i ) ); + } + } + + void invert_phases_in_class( node const& rep ) + { + assert( Ntk::node_to_index( rep ) < Ntk::size() ); + assert( is_choice_representative( rep ) ); + + auto p = Ntk::get_node( _choice_phase->at( rep ) ); + + while ( Ntk::node_to_index( p ) != Ntk::node_to_index( _choice_repr->at( Ntk::node_to_index( p ) ) ) ) + { + _choice_phase->at( p ) = !_choice_phase->at( p ); + p = _choice_repr->at( Ntk::node_to_index( p ) ); + } + } + + void on_add( node const& n ) + { + if ( Ntk::size() > _choice_repr->size() ) + { + _choice_repr->push_back( n ); + _choice_phase->push_back( Ntk::make_signal( n ) ); + } + } + +private: + std::shared_ptr> _choice_repr; + std::shared_ptr> _choice_phase; + choice_view_params _ps; + std::shared_ptr::add_event_type> _add_event; +}; + +template +choice_view( T const&, choice_view_params const& ps = {} ) -> choice_view; + +} // namespace mockturtle diff --git a/include/mockturtle/views/cnf_view.hpp b/include/mockturtle/views/cnf_view.hpp new file mode 100644 index 0000000..240153d --- /dev/null +++ b/include/mockturtle/views/cnf_view.hpp @@ -0,0 +1,691 @@ +/* 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 cnf_view.hpp + \brief Creates a CNF while creating a network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../algorithms/cnf.hpp" +#include "../traits.hpp" +#include "../utils/include/percy.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +struct cnf_view_params +{ + /*! \brief Write DIMACS file, whenever solve is called. */ + std::optional write_dimacs{}; + + /*! \brief Automatically update clauses when network is modified. + Only meaningful when AllowModify = true. */ + bool auto_update{ true }; +}; + +/* forward declaration */ +template +class cnf_view; + +namespace detail +{ + +template +class cnf_view_impl : public Ntk +{ +public: + cnf_view_impl( CnfView& cnf_view ) + : Ntk() + { + (void)cnf_view; + } +}; + +template +class cnf_view_impl : public Ntk +{ + friend class cnf_view; + using node = typename Ntk::node; + +public: + cnf_view_impl( CnfView& cnf_view ) + : Ntk(), cnf_view_( cnf_view ), literals_( *this ) + { + } + + cnf_view_impl( CnfView& cnf_view, Ntk& ntk ) + : Ntk( ntk ), cnf_view_( cnf_view ), literals_( *this ) + { + } + + ~cnf_view_impl() + { + } + + void init() + { + cnf_view_.solver_.add_variables( Ntk::size() ); + + /* unit clause for constants */ + bill::lit_type lit_const( 0, bill::lit_type::polarities::negative ); + cnf_view_.add_clause( lit_const ); + literals_[Ntk::get_constant( false )] = lit_const; + if ( Ntk::get_node( Ntk::get_constant( false ) ) != Ntk::get_node( Ntk::get_constant( true ) ) ) + { + literals_[Ntk::get_constant( true )] = ~lit_const; + } + + uint32_t v = 0; + Ntk::foreach_pi( [&]( auto const& n ) { + literals_[n] = bill::lit_type( ++v, bill::lit_type::polarities::positive ); + } ); + + Ntk::foreach_gate( [&]( auto const& n ) { + literals_[n] = bill::lit_type( ++v, bill::lit_type::polarities::positive ); + cnf_view_.on_add( n, false ); + } ); + } + + inline bill::var_type add_var() + { + return cnf_view_.solver_.add_variable(); + } + + /*! \brief Returns the switching literal associated to a node. */ + inline bill::lit_type switch_lit( node const& n ) const + { + assert( !Ntk::is_pi( n ) && !Ntk::is_constant( n ) && "PI and constant node are not switch-able" ); + return switches_[Ntk::node_to_index( n )]; + } + + /*! \brief Whether a node is currently activated (included in CNF). */ + inline bool is_activated( node const& n ) const + { + return switch_lit( n ).is_complemented(); + /* clauses are activated if switch literal is complemented */ + } + + /*! \brief Deactivates the clauses for a node. */ + void deactivate( node const& n ) + { + if ( is_activated( n ) ) + { + switches_[Ntk::node_to_index( n )].complement(); + } + } + + /*! \brief (Re-)activates the clauses for a node. */ + void activate( node const& n ) + { + if ( !is_activated( n ) ) + { + switches_[Ntk::node_to_index( n )].complement(); + } + } + + void on_modified( node const& n ) + { + deactivate( n ); + cnf_view_.add_clause( switch_lit( n ) ); + cnf_view_.on_add( n, false ); + /* reuse literals_[n] (so that the fanout clauses are still valid), + but create a new switches_[n] to control a new set of gate clauses */ + } + + void on_delete( node const& n ) + { + deactivate( n ); + } + +private: + CnfView& cnf_view_; + + node_map literals_; + std::vector switches_; +}; + +} /* namespace detail */ + +/*! \brief A view to connect logic network creation to SAT solving. + * + * When using this view to create a new network, it creates a CNF internally + * while nodes are added to the network. It also contains a SAT solver. The + * network can be solved by calling the `solve` method, which by default assumes + * that each output should compute `true` (an overload of the `solve` method can + * override this default behaviour and apply custom assumptions). Further, the + * methods `model_value` and `pi_vmodel_alues` can be used to access model + * values in case solving was satisfiable. Finally, methods `var` and `lit` can + * be used to access variable and literal information for nodes and signals, + * respectively, in order to add custom clauses with the `add_clause` methods. + * + * The `cnf_view` can also be wrapped around an existing network by setting the + * `AllowModify` template parameter to true. Then it also updates the CNF when + * nodes are deleted or modified. This comes with an addition cost in variable + * and clause size. + */ +template +class cnf_view : public detail::cnf_view_impl, Ntk, AllowModify, Solver> +{ + friend class detail::cnf_view_impl, Ntk, AllowModify, Solver>; + +public: + using cnf_view_impl_t = detail::cnf_view_impl, Ntk, AllowModify, Solver>; + + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + // can only be constructed as empty network + explicit cnf_view( cnf_view_params const& ps = {} ) + : cnf_view_impl_t( *this ), + ps_( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + if constexpr ( AllowModify ) + { + cnf_view_impl_t::init(); + } + else + { + const auto v = solver_.add_variable(); /* for the constant input */ + assert( v == var( Ntk::get_node( Ntk::get_constant( false ) ) ) ); + add_clause( bill::lit_type( v, bill::lit_type::polarities::negative ) ); + + if ( Ntk::get_node( Ntk::get_constant( true ) ) != Ntk::get_node( Ntk::get_constant( false ) ) ) + { + const auto v = solver_.add_variable(); /* for the constant input */ + assert( v == var( Ntk::get_node( Ntk::get_constant( true ) ) ) ); + add_clause( bill::lit_type( v, bill::lit_type::polarities::positive ) ); + } + } + + register_events(); + } + + template> + explicit cnf_view( Ntk& ntk, cnf_view_params const& ps = {} ) + : cnf_view_impl_t( *this, ntk ), + ps_( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_node_function_v, "Ntk does not implement the node_function method" ); + + cnf_view_impl_t::init(); + + register_events(); + } + + ~cnf_view() + { + if ( add_event ) + { + Ntk::events().release_add_event( add_event ); + } + if ( modified_event ) + { + Ntk::events().release_modified_event( modified_event ); + } + if ( delete_event ) + { + Ntk::events().release_delete_event( delete_event ); + } + } + + signal create_pi() + { + const auto f = Ntk::create_pi(); + + const auto v = solver_.add_variable(); + + if constexpr ( AllowModify ) + { + cnf_view_impl_t::literals_.resize( bill::lit_type( 0, bill::lit_type::polarities::positive ) ); + cnf_view_impl_t::literals_[f] = bill::lit_type( v, bill::lit_type::polarities::positive ); + return f; + } + + assert( v == var( Ntk::get_node( f ) ) ); + (void)v; + + return f; + } + + /* \brief Returns the variable associated to a node. */ + inline bill::var_type var( node const& n ) const + { + if constexpr ( AllowModify ) + { + return cnf_view_impl_t::literals_[n].variable(); + } + return Ntk::node_to_index( n ); + } + + /*! \brief Returns the literal associated to a node. */ + inline bill::lit_type lit( node const& n ) const + { + return bill::lit_type( var( n ), bill::lit_type::polarities::positive ); + } + + /*! \brief Returns the literal associated to a signal. */ + template>> + inline bill::lit_type lit( signal const& f ) const + { + return bill::lit_type( var( Ntk::get_node( f ) ), Ntk::is_complemented( f ) ? bill::lit_type::polarities::negative : bill::lit_type::polarities::positive ); + } + + /*! \brief Solves the network with a set of custom assumptions. + * + * This function does not assert any primary output, unless specified + * explicitly through the assumptions. + * + * The function returns `nullopt`, if no solution can be found (due to a + * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT. + * + * \param assumptions Vector of literals to be assumped when solving + * \param limit Conflict limit (unlimited if 0) + */ + inline std::optional solve( bill::result::clause_type const& assumptions, uint32_t limit = 0 ) + { + const auto _write_dimacs = [&]( bill::result::clause_type const& assumps ) { + if ( ps_.write_dimacs ) + { + for ( const auto& a : assumps ) + { + auto l = pabc::Abc_Var2Lit( a.variable(), a.is_complemented() ); + dimacs_.add_clause( &l, &l + 1 ); + } + dimacs_.set_nr_vars( solver_.num_variables() ); +#ifdef _MSC_VER + FILE* fd = nullptr; + fopen_s( &fd, ps_.write_dimacs->c_str(), "w" ); +#else + FILE* fd = fopen( ps_.write_dimacs->c_str(), "w" ); +#endif + dimacs_.to_dimacs( fd ); + fclose( fd ); + } + }; + + const auto _solve = [&]( bill::result::clause_type const& assumps ) -> std::optional { + const auto res = solver_.solve( assumps, limit ); + + switch ( res ) + { + case bill::result::states::satisfiable: + model_ = solver_.get_model().model(); + return true; + case bill::result::states::unsatisfiable: + return false; + default: + return std::nullopt; + } + + return std::nullopt; + }; + + if constexpr ( AllowModify ) + { + bill::result::clause_type assumptions_copy = assumptions; + for ( auto i = 1u; i < cnf_view_impl_t::switches_.size(); ++i ) + { + if ( !Ntk::is_pi( Ntk::index_to_node( i ) ) ) + { + assumptions_copy.push_back( cnf_view_impl_t::switches_[i] ); + } + } + + _write_dimacs( assumptions_copy ); + return _solve( assumptions_copy ); + } + + _write_dimacs( assumptions ); + return _solve( assumptions ); + } + + /*! \brief Solves the network by asserting all primary outputs to be true + * + * The function returns `nullopt`, if no solution can be found (due to a + * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT. + * + * \param limit Conflict limit (unlimited if 0) + */ + inline std::optional solve( int limit = 0 ) + { + bill::result::clause_type assumptions; + Ntk::foreach_po( [&]( auto const& f ) { + assumptions.push_back( lit( f ) ); + } ); + return solve( assumptions, limit ); + } + + /*! \brief Return model value for a node. */ + inline bool model_value( node const& n ) const + { + return model_.at( var( n ) ) == bill::lbool_type::true_; + } + + /*! \brief Return model value for a node (takes complementation into account). */ + template>> + inline bool model_value( signal const& f ) const + { + return model_value( Ntk::get_node( f ) ) != Ntk::is_complemented( f ); + } + + /* \brief Returns all model values for all primary inputs. */ + std::vector pi_model_values() + { + std::vector values( Ntk::num_pis() ); + Ntk::foreach_pi( [&]( auto const& n, auto i ) { + values[i] = model_value( n ); + } ); + return values; + } + + /*! \brief Blocks last model for primary input values. */ + void block() + { + bill::result::clause_type blocking_clause; + Ntk::foreach_pi( [&]( auto const& n ) { + blocking_clause.push_back( bill::lit_type( var( n ), model_value( n ) ? bill::lit_type::polarities::negative : bill::lit_type::polarities::positive ) ); + } ); + add_clause( blocking_clause ); + } + + /*! \brief Number of variables. */ + inline uint32_t num_vars() const + { + return solver_.num_variables(); + } + + /*! \brief Number of clauses. */ + inline uint32_t num_clauses() const + { + return solver_.num_clauses(); + } + + /*! \brief Adds a clause to the solver. */ + void add_clause( bill::result::clause_type const& clause ) + { + if ( ps_.write_dimacs ) + { + std::vector lits; + for ( auto c : clause ) + { + lits.push_back( pabc::Abc_Var2Lit( c.variable(), c.is_complemented() ) ); + } + dimacs_.add_clause( &lits[0], &lits[0] + lits.size() ); + } + solver_.add_clause( clause ); + } + + /*! \brief Adds a clause from signals to the solver. */ + void add_clause( std::vector const& clause ) + { + bill::result::clause_type lits; + std::transform( clause.begin(), clause.end(), std::back_inserter( lits ), [&]( auto const& s ) { return lit( s ); } ); + add_clause( lits ); + } + + /*! \brief Adds a clause to the solver. + * + * Entries are either all literals or network signals. + */ + template...>, + std::conjunction...>>>> + void add_clause( Lit... lits ) + { + if constexpr ( std::conjunction_v...> ) + { + add_clause( bill::result::clause_type{ { lits... } } ); + } + else + { + add_clause( bill::result::clause_type{ { lit( lits )... } } ); + } + } + +private: + void register_events() + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + modified_event = Ntk::events().register_modified_event( [this]( auto const& n, auto const& previous ) { + (void)previous; + if constexpr ( AllowModify ) + { + if ( ps_.auto_update ) + { + cnf_view_impl_t::on_modified( n ); + } + return; + } + + (void)n; + (void)this; + assert( false && "nodes should not be modified in cnf_view" ); + std::abort(); + } ); + delete_event = Ntk::events().register_delete_event( [this]( auto const& n ) { + if constexpr ( AllowModify ) + { + if ( ps_.auto_update ) + { + cnf_view_impl_t::on_delete( n ); + } + return; + } + + (void)n; + (void)this; + assert( false && "nodes should not be deleted in cnf_view" ); + std::abort(); + } ); + } + + void on_add( node const& n, bool add_var = true ) /* add_var is only used when AllowModify = true */ + { + bill::lit_type node_lit; + bill::lit_type switch_lit; + + if constexpr ( AllowModify ) + { + if ( add_var ) + { + node_lit = bill::lit_type( solver_.add_variable(), bill::lit_type::polarities::positive ); + cnf_view_impl_t::literals_.resize(); + cnf_view_impl_t::literals_[n] = node_lit; + } + else + { + node_lit = cnf_view_impl_t::literals_[n]; + } + + switch_lit = bill::lit_type( solver_.add_variable(), bill::lit_type::polarities::positive ); + cnf_view_impl_t::switches_.resize( Ntk::size() ); + cnf_view_impl_t::switches_[Ntk::node_to_index( n )] = ~switch_lit; + } + else + { + (void)add_var; + const auto v = solver_.add_variable(); + assert( v == var( n ) ); + (void)v; + + node_lit = lit( Ntk::make_signal( n ) ); + } + + const auto _add_clause = [&]( bill::result::clause_type const& clause ) { + if constexpr ( AllowModify ) + { + bill::result::clause_type clause_ = clause; + clause_.push_back( switch_lit ); + add_clause( clause_ ); + } + else + { + add_clause( clause ); + } + }; + + bill::result::clause_type child_lits; + Ntk::foreach_fanin( n, [&]( auto const& f ) { + child_lits.push_back( lit( f ) ); + } ); + + if constexpr ( has_is_and_v ) + { + if ( Ntk::is_and( n ) ) + { + detail::on_and( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_or_v ) + { + if ( Ntk::is_or( n ) ) + { + detail::on_or( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_xor_v ) + { + if ( Ntk::is_xor( n ) ) + { + detail::on_xor( node_lit, child_lits[0], child_lits[1], _add_clause ); + return; + } + } + + if constexpr ( has_is_maj_v ) + { + if ( Ntk::is_maj( n ) ) + { + detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_ite_v ) + { + if ( Ntk::is_ite( n ) ) + { + detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_xor3_v ) + { + if ( Ntk::is_xor3( n ) ) + { + detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause ); + return; + } + } + + if constexpr ( has_is_nary_and_v ) + { + if ( Ntk::is_nary_and( n ) ) + { + fmt::print( stderr, "[e] nary-AND not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + if constexpr ( has_is_nary_or_v ) + { + if ( Ntk::is_nary_or( n ) ) + { + fmt::print( stderr, "[e] nary-OR not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + if constexpr ( has_is_nary_xor_v ) + { + if ( Ntk::is_nary_xor( n ) ) + { + fmt::print( stderr, "[e] nary-XOR not yet supported in generate_cnf" ); + std::abort(); + return; + } + } + + detail::on_function( node_lit, child_lits, Ntk::node_function( n ), _add_clause ); + } + +private: + bill::solver solver_; + bill::result::model_type model_; + percy::cnf_formula dimacs_; + + cnf_view_params ps_; + + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +template +cnf_view( T const& ) -> cnf_view; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/views/color_view.hpp b/include/mockturtle/views/color_view.hpp new file mode 100644 index 0000000..a9c7cc0 --- /dev/null +++ b/include/mockturtle/views/color_view.hpp @@ -0,0 +1,244 @@ +/* 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 color_view.hpp + \brief Manager view for traversal IDs, called colors + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +namespace mockturtle +{ + +/*!\brief Manager view for traversal IDs (in-place storage). + * + * Traversal IDs, called colors, are unsigned integers that can be + * assigned to nodes. The corresponding values are stored in-place in + * the flags of the underlying of the network. + */ +template +class color_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit color_view( Ntk const& ntk ) + : Ntk( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + /*! \brief Returns a new color and increases the current color */ + uint32_t new_color() const + { + return ++this->_storage->trav_id; + // return ++value; + } + + /*! \brief Returns the current color */ + uint32_t current_color() const + { + return this->_storage->trav_id; + // return value; + } + + /*! \brief Assigns all nodes to `color` */ + void clear_colors( uint32_t color = 0 ) const + { + std::for_each( this->_storage->nodes.begin(), this->_storage->nodes.end(), + [color]( auto& n ) { n.data[1].h1 = color; } ); + } + + /*! \brief Returns the color of a node */ + auto color( node const& n ) const + { + return this->_storage->nodes[n].data[1].h1; + } + + /*! \brief Returns the color of a node */ + template>> + auto color( signal const& n ) const + { + return this->_storage->nodes[this->get_node( n )].data[1].h1; + } + + /*! \brief Assigns the current color to a node */ + void paint( node const& n ) const + { + this->_storage->nodes[n].data[1].h1 = current_color(); + } + + /*! \brief Assigns `color` to a node */ + void paint( node const& n, uint32_t color ) const + { + this->_storage->nodes[n].data[1].h1 = color; + } + + /*! \brief Copies the color from `other` to `n` */ + void paint( node const& n, node const& other ) const + { + this->_storage->nodes[n].data[1].h1 = color( other ); + } + + /*! \brief Evaluates a predicate on the color of a node */ + template + bool eval_color( node const& n, Pred&& pred ) const + { + return pred( color( n ) ); + } + + /*! \brief Evaluates a predicate on the colors of two nodes */ + template + bool eval_color( node const& a, node const& b, Pred&& pred ) const + { + return pred( color( a ), color( b ) ); + } + + /*! \brief Evaluates a predicate on the colors of the fanins of a node */ + template + bool eval_fanins_color( node const& n, Pred&& pred ) const + { + bool result = true; + this->foreach_fanin( n, [&]( signal const& fi ) { + if ( !pred( color( this->get_node( fi ) ) ) ) + { + result = false; + return false; + } + return true; + } ); + return result; + } + +protected: + // mutable uint32_t value{0}; +}; /* color_view */ + +/*!\brief Manager view for traversal IDs (out-of-place storage). + * + * Traversal IDs, called colors, are unsigned integers that can be + * assigned to nodes. The corresponding values are stored + * out-of-place in this view. + */ +template +class out_of_place_color_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit out_of_place_color_view( Ntk const& ntk ) + : Ntk( ntk ), values( ntk.size() ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + uint32_t new_color() const + { + return ++value; + } + + uint32_t current_color() const + { + return value; + } + + void clear_colors( uint32_t color = 0 ) const + { + std::for_each( std::begin( values ), std::end( values ), + [color]( auto& v ) { v = color; } ); + } + + auto color( node const& n ) const + { + return values[n]; + } + + template>> + auto color( signal const& n ) const + { + return values[this->get_node( n )]; + } + + void paint( node const& n ) const + { + values[n] = value; + } + + void paint( node const& n, uint32_t color ) const + { + values[n] = color; + } + + void paint( node const& n, node const& other ) const + { + values[n] = values[other]; + } + + /*! \brief Evaluates a predicate on the color of a node */ + template + bool eval_color( node const& n, Pred&& pred ) const + { + return pred( color( n ) ); + } + + /*! \brief Evaluates a predicate on the colors of two nodes */ + template + bool eval_color( node const& a, node const& b, Pred&& pred ) const + { + return pred( color( a ), color( b ) ); + } + + /*! \brief Evaluates a predicate on the colors of the fanins of a node */ + template + bool eval_fanins_color( node const& n, Pred&& pred ) const + { + bool result = true; + this->foreach_fanin( n, [&]( signal const& fi ) { + if ( !pred( color( this->get_node( fi ) ) ) ) + { + result = false; + return false; + } + return true; + } ); + return result; + } + +protected: + mutable std::vector values; + mutable uint32_t value{ 0 }; +}; /* out_of_place_color_view */ + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/cost_view.hpp b/include/mockturtle/views/cost_view.hpp new file mode 100644 index 0000000..eb6be69 --- /dev/null +++ b/include/mockturtle/views/cost_view.hpp @@ -0,0 +1,281 @@ +/* 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 cost_view.hpp + \brief Implements various cost estimation methods for a network + + \author Hanyu Wang +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "../utils/recursive_cost_functions.hpp" +#include "immutable_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements `get_cost` methods for networks. + * + * This view computes the cost of the entire network, a subnetwork, and + * also fanin cone of a single node. It maintains the context of each + * node, which is the aggregated variables that affect the cost a node. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `visited` + * - `set_visited` + * - `foreach_fanin` + * - `foreach_po` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + xag_network xag = ...; + + // create a cost view on the network, for example size cost + auto viewed = cost_view( xag, xag_size_cost_function() ); + + // print size + std::cout << "size: " << viewed.get_cost() << "\n"; + \endverbatim + */ +template +class cost_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using context_t = typename RecCostFn::context_t; + + explicit cost_view( RecCostFn const& cost_fn = {} ) + : Ntk(), + _cost_fn( cost_fn ), + context( *this ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + explicit cost_view( Ntk const& ntk, RecCostFn const& cost_fn = {} ) + : Ntk( ntk ), + _cost_fn( cost_fn ), + context( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + + update_cost(); + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + explicit cost_view( cost_view const& other ) + : Ntk( other ), + _cost_fn( other._cost_fn ), + context( other.context ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + cost_view& operator=( cost_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + context = other.context; + _cost_fn = other._cost_fn; + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~cost_view() + { + Ntk::events().release_add_event( add_event ); + } + + /*! \brief Returns the context of node n */ + context_t get_context( node const& n ) const + { + return context[n]; + } + + /*! \brief Assigns the context of node n */ + void set_context( node const& n, context_t cost_val ) + { + context[n] = cost_val; + this->set_visited( n, this->trav_id() ); + } + + /*! \brief Returns the cost of the entire network */ + uint32_t get_cost() const + { + return _cost; + } + + /*! \brief Returns the cost of node n's fanin cone */ + uint32_t get_cost( node const& n ) + { + uint32_t _c = 0u; + this->incr_trav_id(); + compute_cost( n, _c ); + return _c; + } + + /*! \brief Returns the cost between node n and divs */ + uint32_t get_cost( node const& n, std::vector const& divs ) + { + uint32_t _c = 0u; + this->incr_trav_id(); + for ( signal s : divs ) + { + this->set_visited( this->get_node( s ), this->trav_id() ); + } + compute_cost( n, _c ); + return _c; + } + + /*! \brief Updates the context and cost of the entire network */ + void update_cost() + { + context.reset( context_t{} ); + this->incr_trav_id(); + compute_cost(); + } + + void on_add( node const& n ) + { + context.resize(); + + std::vector fanin_costs; + this->foreach_fanin( n, [&]( auto const& f ) { + fanin_costs.emplace_back( context[this->get_node( f )] ); + } ); + context[n] = _cost_fn( *this, n, fanin_costs ); + _cost_fn( *this, n, _cost, context[n] ); + } + + /*! \brief Creates a PI with context assigned */ + signal create_pi( context_t pi_cotext ) + { + signal s = Ntk::create_pi(); + context.resize(); + set_context( this->get_node( s ), pi_cotext ); + return s; + } + + signal create_pi() + { + signal s = Ntk::create_pi(); + context.resize(); + return s; + } + + void create_po( signal const& f ) + { + Ntk::create_po( f ); + } + +private: + context_t compute_cost( node const& n, uint32_t& _c ) + { + context_t _context{}; + if ( this->visited( n ) == this->trav_id() ) + { + _context = context[n]; // do not update context + _cost_fn( *this, n, _c, _context ); + return _context; + } + if ( this->is_constant( n ) ) + { + _context = context[n] = context_t{}; + } + else if ( this->is_pi( n ) ) + { + _context = context[n] = _cost_fn( *this, n ); + } + else + { + std::vector fanin_costs; + this->foreach_fanin( n, [&]( auto const& f ) { + fanin_costs.emplace_back( compute_cost( this->get_node( f ), _c ) ); + } ); + _context = context[n] = _cost_fn( *this, n, fanin_costs ); + } + _cost_fn( *this, n, _c, _context ); + this->set_visited( n, this->trav_id() ); + return _context; + } + void compute_cost() + { + _cost = 0u; /* must define the zero initialization */ + this->foreach_po( [&]( auto const& f ) { + compute_cost( this->get_node( f ), _cost ); + } ); + } + + node_map context; + uint32_t _cost; + RecCostFn _cost_fn; + + std::shared_ptr::add_event_type> add_event; +}; + +template +cost_view( T const& ) -> cost_view; + +template +cost_view( T const&, RecCostFn const& ) -> cost_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/cut_view.hpp b/include/mockturtle/views/cut_view.hpp new file mode 100644 index 0000000..eb0b57f --- /dev/null +++ b/include/mockturtle/views/cut_view.hpp @@ -0,0 +1,232 @@ +/* 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 cut_view.hpp + \brief Implements an isolated view on a single cut in a network + + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on a single cut in a network. + * + * This view can create a network from a single cut in a largest network. This + * cut has a single output `root` and set of `leaves`. The view reimplements + * the methods `size`, `num_pis`, `num_pos`, `foreach_pi`, `foreach_po`, + * `foreach_node`, `foreach_gate`, `is_pi`, `node_to_index`, and + * `index_to_node`. + * + * This view assumes that all nodes' visited flags are set 0 before creating + * the view. The view guarantees that all the nodes in the view will have a 0 + * visited flag after the construction. + * + * **Required network functions:** + * - `set_visited` + * - `visited` + * - `get_node` + * - `get_constant` + * - `is_constant` + * - `make_signal` + */ +template +class cut_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool is_topologically_sorted = true; + +public: + explicit cut_view( Ntk const& ntk, std::vector const& leaves, signal const& root ) + : immutable_view( ntk ), _root( root ) + { + construct( leaves ); + } + + template>> + explicit cut_view( Ntk const& ntk, std::vector const& leaves, signal const& root ) + : immutable_view( ntk ), _root( root ) + { + construct( leaves ); + } + +private: + template + void construct( std::vector const& leaves ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_make_signal_v, "Ntk does not implement the make_signal method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( std::is_same_v || std::is_same_v, "leaves must be vector of either node or signal" ); + + this->incr_trav_id(); + + /* constants */ + add_constants(); + + /* primary inputs */ + for ( auto const& leaf : leaves ) + { + if constexpr ( std::is_same_v ) + { + add_leaf( leaf ); + } + else + { + add_leaf( this->get_node( leaf ) ); + } + } + + traverse( this->get_node( _root ) ); + + /* restore visited */ + // for ( auto const& n : _nodes ) + //{ + // this->set_visited( n, 0 ); + // } + } + +public: + inline auto size() const { return _nodes.size(); } + inline auto num_pis() const { return _num_leaves; } + inline auto num_pos() const { return 1; } + inline auto num_gates() const { return _nodes.size() - _num_leaves - _num_constants; } + + inline auto node_to_index( const node& n ) const { return _node_to_index.at( n ); } + inline auto index_to_node( uint32_t index ) const { return _nodes[index]; } + + template + void foreach_po( Fn&& fn ) const + { + std::vector roots{ { _root } }; + detail::foreach_element( roots.begin(), roots.end(), fn ); + } + + inline bool is_pi( node const& pi ) const + { + const auto beg = _nodes.begin() + _num_constants; + return std::find( beg, beg + _num_leaves, pi ) != beg + _num_leaves; + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin() + _num_constants, _nodes.begin() + _num_constants + _num_leaves, fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin(), _nodes.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _nodes.begin() + _num_constants + _num_leaves, _nodes.end(), fn ); + } + +private: + inline void add_constants() + { + add_node( this->get_node( this->get_constant( false ) ) ); + this->set_visited( this->get_node( this->get_constant( false ) ), this->trav_id() ); + if ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ) + { + add_node( this->get_node( this->get_constant( true ) ) ); + this->set_visited( this->get_node( this->get_constant( true ) ), this->trav_id() ); + ++_num_constants; + } + } + + inline void add_leaf( node const& leaf ) + { + if ( this->visited( leaf ) == this->trav_id() ) + return; + + add_node( leaf ); + this->set_visited( leaf, this->trav_id() ); + ++_num_leaves; + } + + inline void add_node( node const& n ) + { + _node_to_index[n] = static_cast( _nodes.size() ); + _nodes.push_back( n ); + } + + void traverse( node const& n ) + { + if ( this->visited( n ) == this->trav_id() ) + return; + + this->foreach_fanin( n, [&]( const auto& f ) { + traverse( this->get_node( f ) ); + } ); + + add_node( n ); + this->set_visited( n, this->trav_id() ); + } + +public: + unsigned _num_constants{ 1 }; + unsigned _num_leaves{ 0 }; + std::vector _nodes; + phmap::flat_hash_map _node_to_index; + signal _root; +}; + +template +cut_view( T const&, std::vector> const&, signal const& ) -> cut_view; + +template>> +cut_view( T const&, std::vector> const&, signal const& ) -> cut_view; + +} /* namespace mockturtle */ diff --git a/include/mockturtle/views/depth_view.hpp b/include/mockturtle/views/depth_view.hpp new file mode 100644 index 0000000..32941ec --- /dev/null +++ b/include/mockturtle/views/depth_view.hpp @@ -0,0 +1,363 @@ +/* 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 depth_view.hpp + \brief Implements depth and level for a network + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/cost_functions.hpp" +#include "../utils/node_map.hpp" +#include "immutable_view.hpp" + +#include +#include + +namespace mockturtle +{ + +struct depth_view_params +{ + /*! \brief Take complemented edges into account for depth computation. */ + bool count_complements{ false }; + + /*! \brief Whether PIs have costs. */ + bool pi_cost{ false }; +}; + +/*! \brief Implements `depth` and `level` methods for networks. + * + * This view computes the level of each node and also the depth of + * the network. It implements the network interface methods + * `level` and `depth`. The levels are computed at construction + * and can be recomputed by calling the `update_levels` method. + * + * It also automatically updates levels, and depth when creating nodes or + * creating a PO on a depth_view, however, it does not update the information, + * when modifying or deleting nodes, neither will the critical paths be + * recalculated (due to efficiency reasons). In order to recalculate levels, + * depth, and critical paths, one can call `update_levels` instead. + * + * **Required network functions:** + * - `size` + * - `get_node` + * - `visited` + * - `set_visited` + * - `foreach_fanin` + * - `foreach_po` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // create a depth view on the network + depth_view aig_depth{aig}; + + // print depth + std::cout << "Depth: " << aig_depth.depth() << "\n"; + \endverbatim + */ +template, bool has_depth_interface = has_depth_v&& has_level_v&& has_update_levels_v> +class depth_view +{ +}; + +template +class depth_view : public Ntk +{ +public: + depth_view( Ntk const& ntk, depth_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class depth_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit depth_view( NodeCostFn const& cost_fn = {}, depth_view_params const& ps = {} ) + : Ntk(), _ps( ps ), _levels( *this ), _crit_path( *this ), _cost_fn( cost_fn ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Standard constructor. + * + * \param ntk Base network + */ + explicit depth_view( Ntk const& ntk, NodeCostFn const& cost_fn = {}, depth_view_params const& ps = {} ) + : Ntk( ntk ), _ps( ps ), _levels( ntk ), _crit_path( ntk ), _cost_fn( cost_fn ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_levels(); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Copy constructor. */ + explicit depth_view( depth_view const& other ) + : Ntk( other ), _ps( other._ps ), _levels( other._levels ), _crit_path( other._crit_path ), _depth( other._depth ), _cost_fn( other._cost_fn ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + depth_view& operator=( depth_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + _ps = other._ps; + _levels = other._levels; + _crit_path = other._crit_path; + _depth = other._depth; + _cost_fn = other._cost_fn; + + /* register new event in the other network */ + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~depth_view() + { + Ntk::events().release_add_event( add_event ); + } + + uint32_t depth() const + { + return _depth; + } + + uint32_t level( node const& n ) const + { + return _levels[n]; + } + + bool is_on_critical_path( node const& n ) const + { + return _crit_path[n]; + } + + void set_level( node const& n, uint32_t level ) + { + _levels[n] = level; + } + + void set_depth( uint32_t level ) + { + _depth = level; + } + + void update_levels() + { + _levels.reset( 0 ); + _crit_path.reset( false ); + + this->incr_trav_id(); + compute_levels(); + } + + void resize_levels() + { + _levels.resize(); + } + + void create_po( signal const& f ) + { + Ntk::create_po( f ); + _depth = std::max( _depth, _levels[f] ); + } + +private: + uint32_t compute_levels( node const& n ) + { + if ( this->visited( n ) == this->trav_id() ) + { + return _levels[n]; + } + this->set_visited( n, this->trav_id() ); + + if ( this->is_constant( n ) ) + { + return _levels[n] = 0; + } + if ( this->is_ci( n ) ) + { + assert( !_ps.pi_cost || _cost_fn( *this, n ) >= 1 ); + return _levels[n] = _ps.pi_cost ? _cost_fn( *this, n ) - 1 : 0; + } + + uint32_t level{ 0 }; + this->foreach_fanin( n, [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + level = std::max( level, clevel ); + } ); + + return _levels[n] = level + _cost_fn( *this, n ); + } + + void compute_levels() + { + _depth = 0; + this->foreach_po( [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + _depth = std::max( _depth, clevel ); + } ); + + if constexpr ( has_foreach_ri_v ) + { + this->foreach_ri( [&]( auto const& f ) { + auto clevel = compute_levels( this->get_node( f ) ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + _depth = std::max( _depth, clevel ); + } ); + } + + this->foreach_po( [&]( auto const& f ) { + const auto n = this->get_node( f ); + if ( _levels[n] == _depth ) + { + set_critical_path( n ); + } + } ); + + if constexpr ( has_foreach_ri_v ) + { + this->foreach_ri( [&]( auto const& f ) { + const auto n = this->get_node( f ); + if ( _levels[n] == _depth ) + { + set_critical_path( n ); + } + } ); + } + } + + void set_critical_path( node const& n ) + { + _crit_path[n] = true; + if ( !this->is_constant( n ) && !( _ps.pi_cost && this->is_pi( n ) ) ) + { + const auto lvl = _levels[n]; + this->foreach_fanin( n, [&]( auto const& f ) { + const auto cn = this->get_node( f ); + auto offset = _cost_fn( *this, n ); + if ( _ps.count_complements && this->is_complemented( f ) ) + { + offset++; + } + if ( _levels[cn] + offset == lvl && !_crit_path[cn] ) + { + set_critical_path( cn ); + } + } ); + } + } + + void on_add( node const& n ) + { + _levels.resize(); + + uint32_t level{ 0 }; + this->foreach_fanin( n, [&]( auto const& f ) { + auto clevel = _levels[f]; + if ( _ps.count_complements && this->is_complemented( f ) ) + { + clevel++; + } + level = std::max( level, clevel ); + } ); + + _levels[n] = level + _cost_fn( *this, n ); + } + + depth_view_params _ps; + node_map _levels; + node_map _crit_path; + uint32_t _depth{}; + NodeCostFn _cost_fn; + + std::shared_ptr::add_event_type> add_event; +}; + +template +depth_view( T const& ) -> depth_view; + +template> +depth_view( T const&, NodeCostFn const&, depth_view_params const& ) -> depth_view; + +} // namespace mockturtle diff --git a/include/mockturtle/views/dont_care_view.hpp b/include/mockturtle/views/dont_care_view.hpp new file mode 100644 index 0000000..f08d605 --- /dev/null +++ b/include/mockturtle/views/dont_care_view.hpp @@ -0,0 +1,481 @@ +/* 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 dont_care_view.hpp + \brief Implements methods to store external don't-cares + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "../traits.hpp" +#include "../algorithms/simulation.hpp" +#include "../algorithms/cnf.hpp" +#include "../utils/node_map.hpp" +#include "../utils/window_utils.hpp" +#include "../views/color_view.hpp" +#include "../views/window_view.hpp" + +#include + +#include +#include + +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief A view holding external don't care information of a network + * + * This view helps storing and managing external don't care information. + * There are two types of external don't cares that may be given together + * or independently: + * + * External controllability don't cares (EXCDCs) are primary input patterns + * that will never happen or can be ignored. They are given as another + * network `cdc_ntk` having the same number of PIs as the main network and + * one PO. An input assignment making `cdc_ntk` output 1 is an EXCDC. + * + * External observability don't cares (EXODCs) are conditions for one or + * more primary outputs under which their values can be altered. EXODCs may + * be given for a PO as value combinations of other POs (for example, “the + * second PO is don't care whenever the first PO is 1”). They may also be + * given as pairs of observably equivalent PO values (for example, “the output + * values 01 and 10 are considered equivalent and interchangable”). + * + * By wrapping a network with this view and giving some external don't care + * conditions, some algorithms supporting the consideration of external don't + * cares can make use of them. Currently, only `sim_resubstitution` + * (which makes use of `circuit_validator`) and `equivalence_checking_bill` + * support external don't cares. + * + * \tparam hasEXCDC Enables interfaces and data structure holding external + * controllability don't cares (external don't cares at the primary inputs) + * \tparam hasEXODC Enables interfaces and data structure holding external + * observability don't cares (external dont cares at the primary outputs) + */ +template +class dont_care_view; + +namespace detail +{ + +/*! \brief A manager to classify bit-strings into equivalence classes + * + * This data structure holds and manages equivalence classes of + * bit-strings of the same length (i.e. complete or partial binary + * truth tables). + * The three properties of an equivalence relation are maintained: + * - Reflexive (x = x) + * - Symmetric (if x = y then y = x) + * - Transitive (if x = y and y = z then x = z) + * + * In the current implementation, the big-string length may not be + * larger than 31. + */ +class equivalence_classes_mgr +{ +public: + equivalence_classes_mgr() {} + + equivalence_classes_mgr( uint32_t num_bits ) : _num_bits( num_bits ) + { + assert( num_bits < 32 ); + uint32_t max_val = 1u << num_bits; + _classes.resize( max_val ); + for ( uint32_t i = 0u; i < max_val; ++i ) + { + _classes[i] = i; + } + } + + equivalence_classes_mgr& operator=( equivalence_classes_mgr const& other ) + { + _num_bits = other._num_bits; + _classes = other._classes; + return *this; + } + + void set_equivalent( uint32_t const& a, uint32_t const& b ) + { + uint32_t repr_class = _classes.at( a ); + uint32_t to_be_replaced = _classes.at( b ); + for ( auto i = 0u; i < _classes.size(); ++i ) + { + if ( _classes[i] == to_be_replaced ) + _classes[i] = repr_class; + } + } + + /*! \brief Set two bit strings to be equivalent. */ + void set_equivalent( std::vector const& a, std::vector const& b ) + { + set_equivalent( vector_bool_to_uint32( a ), vector_bool_to_uint32( b ) ); + } + + /*! \brief Check equivalence of fully-assigned bit-strings */ + bool are_equivalent( uint32_t const& a, uint32_t const& b ) const + { + return _classes.at( a ) == _classes.at( b ); + } + + /*! \brief Check equivalence of fully-assigned bit-strings */ + bool are_equivalent( std::vector const& a, std::vector const& b ) const + { + return are_equivalent( vector_bool_to_uint32( a ), vector_bool_to_uint32( b ) ); + } + + /*! \brief Check equivalence of partially-assigned bit-strings + * + * The don't-care bit positions in the two cubes should be the same. + * Two cubes are equivalent if for all possible assignments to the + * don't-care bits, they are always equivalent. + */ + bool are_equivalent( kitty::cube const& a, kitty::cube const& b ) const + { + assert( a._mask == b._mask && "The don't-care bit positions in the two cubes should be the same." ); + return are_equivalent_rec( a, b, 0 ); + } + + uint32_t num_classes() const + { + std::set unique_ids; + for ( auto const& id : _classes ) + { + unique_ids.insert( id ); + } + return unique_ids.size(); + } + + template + void foreach_class( Fn&& fn ) const + { + std::unordered_map> class2pats; + for ( auto pat = 0u; pat < _classes.size(); ++pat ) + { + auto const& id = _classes[pat]; + class2pats.try_emplace( id ); + class2pats[id].emplace_back( pat ); + } + + for ( auto const& p : class2pats ) + { + if ( !fn( p.second ) ) + break; + } + } + +private: + bool are_equivalent_rec( kitty::cube const& a, kitty::cube const& b, uint32_t i ) const + { + if ( i == _num_bits ) + { + return are_equivalent( cube_to_uint32( a ), cube_to_uint32( b ) ); + } + + if ( a.get_mask( i ) ) + { + return are_equivalent_rec( a, b, i + 1 ); + } + else + { + kitty::cube a0 = a; + a0.set_mask( i ); + kitty::cube b0 = b; + b0.set_mask( i ); + if ( !are_equivalent_rec( a0, b0, i + 1 ) ) + return false; + a0.set_bit( i ); + b0.set_bit( i ); + return are_equivalent_rec( a0, b0, i + 1 ); + } + } + + uint32_t vector_bool_to_uint32( std::vector const& vec ) const + { + assert( vec.size() == _num_bits ); + uint32_t res{0u}; + for ( auto i = 0u; i < _num_bits; ++i ) + { + if ( vec[i] ) + res |= 1u << i; + } + return res; + } + + uint32_t cube_to_uint32( kitty::cube const& c ) const + { + assert( c.num_literals() == _num_bits ); // fully assigned + return c._bits; + } + +private: + uint32_t _num_bits; + std::vector _classes; +}; // equivalence_classes_mgr + +template +class dont_care_view_impl : public Ntk +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template> + dont_care_view_impl( Ntk const& ntk ) + : Ntk( ntk ) + {} + + template> + dont_care_view_impl( Ntk const& ntk, Ntk const& cdc_ntk ) + : Ntk( ntk ), _excdc( cdc_ntk ) + { + assert( cdc_ntk.num_pis() == ntk.num_pis() ); + assert( cdc_ntk.num_pos() == 1 ); + } + + /*! \brief Checks whether an input pattern is EXCDC + * + * \param pattern The PI value combination to be checked + * \return Whether `pattern` is EXCDC + */ + template> + bool pattern_is_EXCDC( std::vector const& pattern ) const + { + assert( pattern.size() == this->num_pis() ); + + default_simulator sim( pattern ); + auto const vals = simulate( _excdc, sim ); + return vals[0]; + } + + template> + void add_EXCDC_clauses( solver_t& solver ) const + { + using add_clause_fn_t = std::function const& )>; + add_clause_fn_t const add_clause_fn = [&]( auto const& clause ) { solver.add_clause( clause ); }; + + // topological order of the gates in _excdc is assumed + node_map cdc_lits( _excdc ); + cdc_lits[_excdc.get_constant( false )] = bill::lit_type( 0, bill::lit_type::polarities::positive ); + if ( _excdc.get_node( _excdc.get_constant( false ) ) != _excdc.get_node( _excdc.get_constant( true ) ) ) + { + cdc_lits[_excdc.get_constant( true )] = bill::lit_type( 0, bill::lit_type::polarities::negative ); + } + _excdc.foreach_pi( [&]( auto const& n, auto i ) { + cdc_lits[n] = bill::lit_type( i + 1, bill::lit_type::polarities::positive ); + } ); + + _excdc.foreach_gate( [&]( auto const& n ){ + cdc_lits[n] = bill::lit_type( solver.add_variable(), bill::lit_type::polarities::positive ); + }); + + auto out_lits = generate_cnf( _excdc, add_clause_fn, cdc_lits ); + solver.add_clause( {~out_lits[0]} ); + } + +private: + Ntk _excdc; +}; /* dont_care_view_impl */ +} // namespace detail + +template +class dont_care_view : public detail::dont_care_view_impl +{ +public: + static constexpr bool has_EXCDC_interface = hasEXCDC; + static constexpr bool has_EXODC_interface = false; + +public: + template> + dont_care_view( Ntk const& ntk ) + : detail::dont_care_view_impl( ntk ) + {} + + template> + dont_care_view( Ntk const& ntk, Ntk const& cdc_ntk ) + : detail::dont_care_view_impl( ntk, cdc_ntk ) + {} +}; + +template +class dont_care_view : public detail::dont_care_view_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool has_EXCDC_interface = hasEXCDC; + static constexpr bool has_EXODC_interface = true; + +public: + /*! \brief Constructor when no EXCDC is provided + * + * \param ntk The main network + */ + template> + dont_care_view( Ntk const& ntk ) + : detail::dont_care_view_impl( ntk ), _exoec( ntk.num_pos() ) + {} + + /*! \brief Constructor when EXCDC is provided + * + * \param ntk The main network + * \param cdc_ntk The network representing EXCDC conditions, having the same + * number of PIs as `ntk` and one PO + */ + template> + dont_care_view( Ntk const& ntk, Ntk const& cdc_ntk ) + : detail::dont_care_view_impl( ntk, cdc_ntk ), _exoec( ntk.num_pos() ) + {} + + /*! \brief Adds an EXODC condition for a PO in terms of other POs + * + * \param cond Condition in terms of other POs for the concerned PO to be don't care + * \param po_id Index of the concerned PO + */ + void add_EXODC( kitty::cube const& cond, uint32_t po_id ) + { + cond.foreach_minterm( this->num_pos(), [&]( kitty::cube const& c ){ + assert( c.num_literals() == this->num_pos() ); + if ( c.get_bit( po_id ) ) return true; + kitty::cube c2 = c; + c2.set_bit( po_id ); + _exoec.set_equivalent( c._bits, c2._bits ); + return true; + }); + } + + /*! \brief Adds a pair of PO values that are considered observably equivalent + * + * \param pat1 The first PO value combination + * \param pat2 The second PO value combination + */ + void add_EXOEC_pair( std::vector const& pat1, std::vector const& pat2 ) + { + _exoec.set_equivalent( pat1, pat2 ); + } + + /*! \brief Checks whether a pair of PO value assignments are observably equivalent + * + * \param pat1 The first PO value combination + * \param pat2 The second PO value combination + * \return Whether `pat1` and `pat2` are observably equivalent + */ + bool are_observably_equivalent( std::vector const& pat1, std::vector const& pat2 ) const + { + return _exoec.are_equivalent( pat1, pat2 ); + } + + /*! \brief Checks whether a pair of partial PO value assignments are observably equivalent + * + * For a pair of partial assignments to be equivalent, all pairs of expansions of + * the partial assignments have to be equivalent. + * + * \param pat1 The first partial PO value + * \param pat2 The second partial PO value + * \return Whether `pat1` and `pat2` are observably equivalent + */ + bool are_observably_equivalent( kitty::cube const& pat1, kitty::cube const& pat2 ) const + { + return _exoec.are_equivalent( pat1, pat2 ); + } + + /*! \brief Builds an observability-equivalence miter network + * + * An observability-equivalence miter network is a generalization of a miter network. + * This network takes two PO value combinations as inputs (thus it has + * `2 * ntk.num_pos()` PIs) and outputs 1 if the two PO value combinations are + * _not_ observably equivalent. + * + * \param miter An empty network where the miter will be built + */ + void build_oe_miter( Ntk& miter ) const + { + std::vector pos1, pos2; + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + pos1.emplace_back( miter.create_pi() ); + } + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + pos2.emplace_back( miter.create_pi() ); + } + build_oe_miter( miter, pos1, pos2 ); + } + + /*! \brief Builds an observability-equivalence miter network + * + * An observability-equivalence miter network is a generalization of a miter network. + * This network takes two PO value combinations as inputs (thus it has + * `2 * ntk.num_pos()` PIs) and outputs 1 if the two PO value combinations are + * _not_ observably equivalent. + * + * \param miter The network where the miter will be built + * \param pos1 Signals of the first set of (main network's) POs + * \param pos2 Signals of the second set of (main network's) POs + */ + template + void build_oe_miter( NtkMiter& miter, std::vector const& pos1, std::vector const& pos2 ) const + { + assert( pos1.size() == this->num_pos() ); + assert( pos2.size() == this->num_pos() ); + + std::vector are_both_in_class_i; + std::vector is_in_class1, is_in_class2; + std::vector ins1, ins2; + ins1.resize( this->num_pos() ); + ins2.resize( this->num_pos() ); + _exoec.foreach_class( [&]( std::vector const& pats ){ + is_in_class1.clear(); + is_in_class2.clear(); + for ( uint32_t pat : pats ) + { + for ( auto i = 0u; i < this->num_pos(); ++i ) + { + ins1[i] = ( pat & 0x1 ) ? pos1[i] : !pos1[i]; + ins2[i] = ( pat & 0x1 ) ? pos2[i] : !pos2[i]; + pat >>= 1; + } + is_in_class1.emplace_back( miter.create_nary_and( ins1 ) ); + is_in_class2.emplace_back( miter.create_nary_and( ins2 ) ); + } + are_both_in_class_i.emplace_back( miter.create_and( miter.create_nary_or( is_in_class1 ), miter.create_nary_or( is_in_class2 ) ) ); + return true; + }); + miter.create_po( !miter.create_nary_or( are_both_in_class_i ) ); + /* miter output = 1 <=> there is no class i that pos1 and pos2 are both in <=> pos1 and pos2 are not OE */ + } + +private: + detail::equivalence_classes_mgr _exoec; +}; /* dont_care_view */ + +} // namespace mockturtle diff --git a/include/mockturtle/views/dont_touch_view.hpp b/include/mockturtle/views/dont_touch_view.hpp new file mode 100644 index 0000000..7f70712 --- /dev/null +++ b/include/mockturtle/views/dont_touch_view.hpp @@ -0,0 +1,142 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file dont_touch_view.hpp + \brief Select nodes to be "don't touch" + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Mark nodes as "don't touch". + * + * This view adds methods to mark nodes as don't touch. A don't touch node + * will be skipped during logic optimization or mapping. + * It always adds the functions `select_dont_touch`, `remove_dont_touch`, + * `is_dont_touch`. + * + * **Required network functions:** + * - `size` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + klut_network klut = ...; + dont_touch_view klut_dont_touch{ klut }; + + // select dont touch nodes + klut_dont_touch.select_dont_touch( 20 ); + + // call technology mapping to map the rest of the network + binding_view res = emap( klut_dont_touch, tech_lib ); + \endverbatim + */ +template +class dont_touch_view : public Ntk +{ +public: + using node = typename Ntk::node; + +public: + explicit dont_touch_view() + : Ntk(), _dont_touch() + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + } + + explicit dont_touch_view( Ntk const& ntk ) + : Ntk( ntk ), _dont_touch() + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + } + + dont_touch_view& operator=( dont_touch_view const& dont_touch_ntk ) + { + Ntk::operator=( dont_touch_ntk ); + _dont_touch = dont_touch_ntk._dont_touch; + return *this; + } + + void select_dont_touch( node const& n ) + { + _dont_touch.insert( Ntk::node_to_index( n ) ); + } + + void remove_dont_touch( node const& n ) + { + if ( auto it = _dont_touch.find( Ntk::node_to_index( n ) ); it != _dont_touch.end() ) + { + _dont_touch.erase( it ); + } + } + + bool is_dont_touch( node const& n ) const + { + return _dont_touch.find( Ntk::node_to_index( n ) ) != _dont_touch.end(); + } + + template + void foreach_dont_touch( Fn&& fn ) const + { + constexpr auto is_bool_f = std::is_invocable_r_v; + constexpr auto is_void_f = std::is_invocable_r_v; + + for ( auto el : _dont_touch ) + { + if constexpr ( is_bool_f ) + { + if ( !fn( Ntk::index_to_node( el ) ) ) + return; + } + else + { + fn( Ntk::index_to_node( el ) ); + } + } + } + +private: + std::unordered_set _dont_touch; +}; /* dont_touch_view */ + +template +dont_touch_view( T const& ) -> dont_touch_view; + +} // namespace mockturtle diff --git a/include/mockturtle/views/fanout_limit_view.hpp b/include/mockturtle/views/fanout_limit_view.hpp new file mode 100644 index 0000000..acbef80 --- /dev/null +++ b/include/mockturtle/views/fanout_limit_view.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 fanout_limit_view.hpp + \brief View that replicates nodes whose fanout size exceed a limit + + \author Heinz Riener + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/mig.hpp" +#include "../utils/node_map.hpp" + +namespace mockturtle +{ + +struct fanout_limit_view_params +{ + uint64_t fanout_limit{ 16 }; +}; + +template +class fanout_limit_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + fanout_limit_view( fanout_limit_view_params const ps = {} ) + : replicas( *this ), ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + assert( ps.fanout_limit > 0u ); + } + + uint32_t create_po( signal const& f ) + { + if ( Ntk::is_maj( Ntk::get_node( f ) ) && Ntk::fanout_size( Ntk::get_node( f ) ) + 1 > ps.fanout_limit ) + { + return Ntk::create_po( replicate_node( f ) ); + } + else + { + return Ntk::create_po( f ); + } + } + + signal create_maj( signal const& a, signal const& b, signal const& c ) + { + std::array fanins; + fanins[0u] = ( Ntk::is_maj( Ntk::get_node( a ) ) && Ntk::fanout_size( Ntk::get_node( a ) ) > ps.fanout_limit - 1 ) ? replicate_node( a ) : a; + fanins[1u] = ( Ntk::is_maj( Ntk::get_node( b ) ) && Ntk::fanout_size( Ntk::get_node( b ) ) > ps.fanout_limit - 1 ) ? replicate_node( b ) : b; + fanins[2u] = ( Ntk::is_maj( Ntk::get_node( c ) ) && Ntk::fanout_size( Ntk::get_node( c ) ) > ps.fanout_limit - 1 ) ? replicate_node( c ) : c; + return Ntk::create_maj( fanins[0u], fanins[1u], fanins[2u] ); + } + + signal create_and( signal const& a, signal const& b ) + { + return create_maj( Ntk::get_constant( false ), a, b ); + } + + signal create_nand( signal const& a, signal const& b ) + { + return !create_and( a, b ); + } + + signal create_or( signal const& a, signal const& b ) + { + return create_maj( Ntk::get_constant( true ), a, b ); + } + + signal create_nor( signal const& a, signal const& b ) + { + return !create_or( a, b ); + } + + signal create_lt( signal const& a, signal const& b ) + { + return create_and( !a, b ); + } + + signal create_le( signal const& a, signal const& b ) + { + return !create_and( a, !b ); + } + + signal create_xor( signal const& a, signal const& b ) + { + const auto fcompl = a.complement ^ b.complement; + const auto c1 = create_and( +a, -b ); + const auto c2 = create_and( +b, -a ); + return create_and( !c1, !c2 ) ^ !fcompl; + } + + signal create_ite( signal cond, signal f_then, signal f_else ) + { + bool f_compl{ false }; + if ( f_then.index < f_else.index ) + { + std::swap( f_then, f_else ); + cond.complement ^= 1; + } + if ( f_then.complement ) + { + f_then.complement = 0; + f_else.complement ^= 1; + f_compl = true; + } + + return create_and( !create_and( !cond, f_else ), !create_and( cond, f_then ) ) ^ !f_compl; + } + + signal create_xor3( signal const& a, signal const& b, signal const& c ) + { + const auto f = create_maj( a, !b, c ); + const auto g = create_maj( a, b, !c ); + return create_maj( !a, f, g ); + } +#pragma endregion + +#pragma region Create nary functions + signal create_nary_and( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( true ), [this]( auto const& a, auto const& b ) { return create_and( a, b ); } ); + } + + signal create_nary_or( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( false ), [this]( auto const& a, auto const& b ) { return create_or( a, b ); } ); + } + + signal create_nary_xor( std::vector const& fs ) + { + return tree_reduce( fs.begin(), fs.end(), Ntk::get_constant( false ), [this]( auto const& a, auto const& b ) { return create_xor( a, b ); } ); + } +#pragma endregion + +#pragma region Create arbitrary functions + signal clone_node( mig_network const& other, node const& source, std::vector const& children ) + { + (void)other; + (void)source; + assert( children.size() == 3u ); + return create_maj( children[0u], children[1u], children[2u] ); + } +#pragma endregion + + signal replicate_node( signal const& s ) + { + return Ntk::is_complemented( s ) ? !replicate_node( Ntk::get_node( s ) ) : replicate_node( Ntk::get_node( s ) ); + } + + signal replicate_node( node const& n ) + { + if ( replicas.has( n ) ) + { + auto replica = replicas[n]; + if ( Ntk::fanout_size( replica ) < ps.fanout_limit ) + { + return Ntk::make_signal( replica ); + } + } + + std::array fanins; + Ntk::foreach_fanin( n, [&]( signal const& f, auto index ) { + fanins[index] = ( Ntk::is_maj( Ntk::get_node( f ) ) && Ntk::fanout_size( Ntk::get_node( f ) ) > ps.fanout_limit - 1u ) ? replicate_node( f ) : f; + } ); + + auto const new_signal = create_maj_overwrite_strash( fanins[0u], fanins[1u], fanins[2u] ); + replicas[n] = Ntk::get_node( new_signal ); + return new_signal; + } + + uint32_t num_gates() const + { + return Ntk::num_gates() + count_hash_overwrites; + } + +protected: + signal create_maj_overwrite_strash( 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; + } + + /* complemented edges minimization */ + auto node_complement = false; + if ( static_cast( a.complement ) + static_cast( b.complement ) + + static_cast( c.complement ) >= + 2u ) + { + node_complement = true; + a.complement = !a.complement; + b.complement = !b.complement; + c.complement = !c.complement; + } + + typename storage::element_type::node_type node; + node.children[0] = a; + node.children[1] = b; + node.children[2] = c; + + /* structural hashing */ + if ( true ) + { + const auto it = Ntk::_storage->hash.find( node ); + if ( it != Ntk::_storage->hash.end() ) + { + ++count_hash_overwrites; + } + } + + 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( node ); + Ntk::_storage->hash[node] = 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, node_complement }; + } + +protected: + uint32_t count_hash_overwrites{ 0 }; + unordered_node_map replicas; + fanout_limit_view_params const ps; +}; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/views/fanout_view.hpp b/include/mockturtle/views/fanout_view.hpp new file mode 100644 index 0000000..257b31c --- /dev/null +++ b/include/mockturtle/views/fanout_view.hpp @@ -0,0 +1,350 @@ +/* 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 fanout_view.hpp + \brief Implements fanout for a network + + \author Alessandro Tempia Calvino + \author Hanyu Wang + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../networks/events.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "immutable_view.hpp" + +#include +#include +#include + +namespace mockturtle +{ + +struct fanout_view_params +{ + bool update_on_add{ true }; + bool update_on_modified{ true }; + bool update_on_delete{ true }; +}; + +/*! \brief Implements `foreach_fanout` methods for networks. + * + * This view computes the fanout of each node of the network. + * It implements the network interface method `foreach_fanout`. The + * fanout are computed at construction and can be recomputed by + * calling the `update_fanout` method. + * + * **Required network functions:** + * - `foreach_node` + * - `foreach_fanin` + * + */ +template> +class fanout_view +{ +}; + +template +class fanout_view : public Ntk +{ +public: + fanout_view( Ntk const& ntk, fanout_view_params const& ps = {} ) : Ntk( ntk ) + { + (void)ps; + } +}; + +template +class fanout_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit fanout_view( fanout_view_params const& ps = {} ) + : Ntk(), _fanout( *this ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_fanout(); + + register_events(); + } + + explicit fanout_view( Ntk const& ntk, fanout_view_params const& ps = {} ) + : Ntk( ntk ), _fanout( ntk ), _ps( ps ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + + update_fanout(); + + register_events(); + } + + /*! \brief Copy constructor. */ + fanout_view( fanout_view const& other ) + : Ntk( other ), _fanout( other._fanout ), _ps( other._ps ) + { + register_events(); + } + + fanout_view& operator=( fanout_view const& other ) + { + release_events(); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + _ps = other._ps; + _fanout = other._fanout; + + register_events(); + + return *this; + } + + ~fanout_view() + { + release_events(); + } + + template + void foreach_fanout( node const& n, Fn&& fn ) const + { + assert( n < this->size() ); + detail::foreach_element( _fanout[n].begin(), _fanout[n].end(), fn ); + } + + void update_fanout() + { + compute_fanout(); + } + + std::vector fanout( node const& n ) const /* deprecated */ + { + return _fanout[n]; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + if ( Ntk::get_node( new_signal ) == old_node && !Ntk::is_complemented( new_signal ) ) + return; + + if ( Ntk::is_dead( Ntk::get_node( new_signal ) ) ) + { + Ntk::revive_node( Ntk::get_node( new_signal ) ); + } + + std::unordered_map old_to_new; + std::stack> to_substitute; + to_substitute.push( { old_node, new_signal } ); + + while ( !to_substitute.empty() ) + { + const auto [_old, _curr] = to_substitute.top(); + to_substitute.pop(); + + signal _new = _curr; + /* find the real new node */ + if ( Ntk::is_dead( Ntk::get_node( _new ) ) ) + { + auto it = old_to_new.find( Ntk::get_node( _new ) ); + while ( it != old_to_new.end() ) + { + _new = Ntk::is_complemented( _new ) ? Ntk::create_not( it->second ) : it->second; + it = old_to_new.find( Ntk::get_node( _new ) ); + } + } + /* revive */ + if ( Ntk::is_dead( Ntk::get_node( _new ) ) ) + { + Ntk::revive_node( Ntk::get_node( _new ) ); + } + + if ( Ntk::get_node( _new ) == _old && !Ntk::is_complemented( _new ) ) + continue; + + const auto parents = _fanout[_old]; + for ( auto n : parents ) + { + if ( const auto repl = Ntk::replace_in_node( n, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + Ntk::replace_in_outputs( _old, _new ); + + /* reset fan-in of old node */ + if ( _old != Ntk::get_node( _new ) ) /* substitute a node using itself*/ + { + old_to_new.insert( { _old, _new } ); + Ntk::take_out_node( _old ); + } + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + if ( Ntk::get_node( new_signal ) == old_node && !Ntk::is_complemented( new_signal ) ) + return; + + if ( Ntk::is_dead( Ntk::get_node( new_signal ) ) ) + { + Ntk::revive_node( Ntk::get_node( new_signal ) ); + } + + const auto parents = _fanout[old_node]; + for ( auto n : parents ) + { + Ntk::replace_in_node_no_restrash( n, old_node, new_signal ); + } + + /* check outputs */ + Ntk::replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + Ntk::take_out_node( old_node ); + } + } + +private: + void register_events() + { + if ( _ps.update_on_add ) + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { + _fanout.resize(); + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].push_back( n ); + } ); + } ); + } + + if ( _ps.update_on_modified ) + { + modified_event = Ntk::events().register_modified_event( [this]( auto const& n, auto const& previous ) { + (void)previous; + for ( auto const& f : previous ) + { + _fanout[f].erase( std::remove( _fanout[f].begin(), _fanout[f].end(), n ), _fanout[f].end() ); + } + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].push_back( n ); + } ); + } ); + } + + if ( _ps.update_on_delete ) + { + delete_event = Ntk::events().register_delete_event( [this]( auto const& n ) { + _fanout[n].clear(); + Ntk::foreach_fanin( n, [&, this]( auto const& f ) { + _fanout[f].erase( std::remove( _fanout[f].begin(), _fanout[f].end(), n ), _fanout[f].end() ); + } ); + } ); + } + } + + void release_events() + { + if ( add_event ) + { + Ntk::events().release_add_event( add_event ); + } + + if ( modified_event ) + { + Ntk::events().release_modified_event( modified_event ); + } + + if ( delete_event ) + { + Ntk::events().release_delete_event( delete_event ); + } + } + + void compute_fanout() + { + _fanout.reset(); + + /* Compute fanout also for buffers in buffered networks */ + if constexpr ( is_buffered_network_type_v ) + { + this->foreach_node( [&]( auto const& n ) { + if ( this->is_pi( n ) || this->is_constant( n ) ) + return true; + this->foreach_fanin( n, [&]( auto const& c ) { + auto& fanout = _fanout[c]; + if ( std::find( fanout.begin(), fanout.end(), n ) == fanout.end() ) + { + fanout.push_back( n ); + } + } ); + return true; + } ); + } + else + { + this->foreach_gate( [&]( auto const& n ) { + this->foreach_fanin( n, [&]( auto const& c ) { + auto& fanout = _fanout[c]; + if ( std::find( fanout.begin(), fanout.end(), n ) == fanout.end() ) + { + fanout.push_back( n ); + } + } ); + } ); + } + } + + node_map, Ntk> _fanout; + fanout_view_params _ps; + + std::shared_ptr::add_event_type> add_event; + std::shared_ptr::modified_event_type> modified_event; + std::shared_ptr::delete_event_type> delete_event; +}; + +template +fanout_view( T const&, fanout_view_params const& ps = {} ) -> fanout_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/immutable_view.hpp b/include/mockturtle/views/immutable_view.hpp new file mode 100644 index 0000000..f5ee118 --- /dev/null +++ b/include/mockturtle/views/immutable_view.hpp @@ -0,0 +1,89 @@ +/* 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 immutable_view.hpp + \brief Disables all methods to change the network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +namespace mockturtle +{ + +/*! \brief Deletes all methods that can change the network. + * + * This view deletes all methods that can change the network structure such as + * `create_not`, `create_and`, or `create_node`. This view is convenient to + * use as a base class for other views that make some computations based on the + * structure when being constructed. Then, changes to the structure invalidate + * these data. + */ +template +class immutable_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Default constructor. + * + * Constructs immutable view on another network. + */ + immutable_view( Ntk const& ntk ) : Ntk( ntk ) + { + } + + signal create_pi() = delete; + void create_po( signal const& s ) = delete; + signal create_ro() = delete; + void create_ri( signal const& s ) = delete; + signal create_buf( signal const& f ) = delete; + signal create_not( signal const& f ) = delete; + signal create_and( signal const& f, signal const& g ) = delete; + signal create_nand( signal const& f, signal const& g ) = delete; + signal create_or( signal const& f, signal const& g ) = delete; + signal create_nor( signal const& f, signal const& g ) = delete; + signal create_lt( signal const& f, signal const& g ) = delete; + signal create_le( signal const& f, signal const& g ) = delete; + signal create_gt( signal const& f, signal const& g ) = delete; + signal create_ge( signal const& f, signal const& g ) = delete; + signal create_xor( signal const& f, signal const& g ) = delete; + signal create_xnor( signal const& f, signal const& g ) = delete; + signal create_maj( signal const& f, signal const& g, signal const& h ) = delete; + signal create_ite( signal const& cond, signal const& f_then, signal const& f_else ) = delete; + signal create_node( std::vector const& fanin, kitty::dynamic_truth_table const& function ) = delete; + signal clone_node( immutable_view const& other, node const& source, std::vector const& fanin ) = delete; + void substitute_node( node const& old_node, node const& new_node ) = delete; +}; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/mapping_view.hpp b/include/mockturtle/views/mapping_view.hpp new file mode 100644 index 0000000..075e61d --- /dev/null +++ b/include/mockturtle/views/mapping_view.hpp @@ -0,0 +1,276 @@ +/* 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 mapping_view.hpp + \brief Implements mapping methods to create mapped networks + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/truth_table_cache.hpp" +#include "../views/immutable_view.hpp" + +#include + +namespace mockturtle +{ + +namespace detail +{ + +template +struct mapping_view_storage; + +template<> +struct mapping_view_storage +{ + std::vector mappings; + uint32_t mapping_size{ 0 }; + std::vector functions; + truth_table_cache cache; +}; + +template<> +struct mapping_view_storage +{ + std::vector mappings; + uint32_t mapping_size{ 0 }; +}; + +} // namespace detail + +template +inline constexpr bool implements_mapping_interface_v = has_has_mapping_v && ( !StoreFunction || has_cell_function_v ); + +/*! \brief Adds mapping API methods to network. + * + * This view adds methods of the mapping API methods to a network. It always + * adds the functions `has_mapping`, `is_cell_root`, `clear_mapping`, + * `num_cells`, `add_to_mapping`, `remove_from_mapping`, and + * `foreach_cell_fanin`. If the template argument `StoreFunction` is set to + * `true`, it also adds functions for `cell_function` and `set_cell_function`. + * For the latter case, this view requires more memory to also store the cells' + * truth tables. + * + * These methods are used to represent a mapping that is annotated to a + * subject graph. The interface can, e.g., be used for LUT mapping or standard + * cell mapping. For a common terminology, we call a collection of nodes that + * belong to the same unit a cell, which has a single root. The *mapped node* is + * the cell root. A cell root, and therefore the cell it represents, may be + * assigned a function by means of a truth table. + * + * **Required network functions:** + * - `size` + * - `node_to_index` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // in order to apply mapping, wrap network in mapping view + mapping_view mapped_aig{aig}; + + // call LUT mapping algorithm + lut_mapping( mapped_aig ); + + // nodes of aig and mapped_aig are the same + aig.foreach_node( [&]( auto n ) { + std::cout << n << " has mapping? " << mapped_aig.is_cell_root( n ) << "\n"; + } ); + \endverbatim + */ +template> +class mapping_view +{ +}; + +template +class mapping_view : public Ntk +{ +public: + mapping_view( Ntk const& ntk ) : Ntk( ntk ) + { + } +}; + +template +class mapping_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /*! \brief Default constructor. + * + * Constructs mapping view on another network. + */ + mapping_view( Ntk const& ntk ) + : immutable_view( ntk ), + _mapping_storage( std::make_shared() ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + _mapping_storage->mappings.resize( ntk.size(), 0 ); + + if constexpr ( StoreFunction ) + { + /* insert 0 truth table */ + _mapping_storage->cache.insert( kitty::dynamic_truth_table( 0 ) ); + + /* default each truth table to 0 */ + _mapping_storage->functions.resize( ntk.size(), 0 ); + } + } + + /*! \brief Returns true, if network has a mapping. */ + bool has_mapping() const + { + return _mapping_storage->mapping_size > 0; + } + + /*! \brief Returns true, if node is the root of a mapped cell. */ + bool is_cell_root( node const& n ) const + { + return _mapping_storage->mappings[this->node_to_index( n )] != 0; + } + + /*! \brief Clears a mapping. */ + void clear_mapping() + { + _mapping_storage->mappings.clear(); + _mapping_storage->mappings.resize( this->size(), 0 ); + _mapping_storage->mapping_size = 0; + } + + /*! \brief Number of cells, i.e, mapped nodes. */ + uint32_t num_cells() const + { + return _mapping_storage->mapping_size; + } + + /*! \brief Adds a node to the mapping. */ + template + void add_to_mapping( node const& n, LeavesIterator begin, LeavesIterator end ) + { + auto& mindex = _mapping_storage->mappings[this->node_to_index( n )]; + + /* increase mapping size? */ + if ( mindex == 0 ) + { + _mapping_storage->mapping_size++; + } + + /* set starting index of leafs */ + mindex = static_cast( _mapping_storage->mappings.size() ); + + /* insert number of leafs */ + _mapping_storage->mappings.push_back( static_cast( std::distance( begin, end ) ) ); + + /* insert leaf indexes */ + while ( begin != end ) + { + _mapping_storage->mappings.push_back( this->node_to_index( *begin++ ) ); + } + } + + /*! \brief Remove from mapping. */ + void remove_from_mapping( node const& n ) + { + auto& mindex = _mapping_storage->mappings[this->node_to_index( n )]; + + if ( mindex != 0 ) + { + _mapping_storage->mapping_size--; + } + + _mapping_storage->mappings[this->node_to_index( n )] = 0; + } + + /*! \brief Gets function of the cell. + * + * The parameter `n` is a node that must be a cell root. + */ + template && enabled>> + kitty::dynamic_truth_table cell_function( node const& n ) const + { + return _mapping_storage->cache[_mapping_storage->functions[this->node_to_index( n )]]; + } + + /*! \brief Sets cell function. + * + * The parameter `n` is a node that must be a cell root. + */ + template && enabled>> + void set_cell_function( node const& n, kitty::dynamic_truth_table const& function ) + { + _mapping_storage->functions[this->node_to_index( n )] = _mapping_storage->cache.insert( function ); + } + + /*! \brief Iterators over cell's fan-ins. + * The parameter `n` is a node that must be a cell root. + * The parameter ``fn`` is any callable that must have one of the + * following four signatures. + * - ``void(node const&)`` + * - ``void(node const&, uint32_t)`` + * - ``bool(node const&)`` + * - ``bool(node const&, uint32_t)`` + */ + template + void foreach_cell_fanin( node const& n, Fn&& fn ) const + { + auto it = _mapping_storage->mappings.begin() + _mapping_storage->mappings[this->node_to_index( n )]; + const auto size = *it++; + using IteratorType = decltype( it ); + detail::foreach_element_transform( + it, it + size, + [&]( auto i ) { return this->index_to_node( i ); }, fn ); + } + +private: + std::shared_ptr> _mapping_storage; +}; + +template +mapping_view( T const& ) -> mapping_view; + +} // namespace mockturtle diff --git a/include/mockturtle/views/mffc_view.hpp b/include/mockturtle/views/mffc_view.hpp new file mode 100644 index 0000000..8ab2639 --- /dev/null +++ b/include/mockturtle/views/mffc_view.hpp @@ -0,0 +1,310 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mffc_view.hpp + \brief Implements an isolated view on a single cut in a network + + \author Alessandro Tempia Calvino + \author Bruno Schmitt + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include +#include +#include + +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on the MFFC of a node. + * + * The network is constructed from a given root node which is traversed towards + * the primary inputs. Nodes are collected as long they only fanout into nodes + * which are already among the visited nodes. Therefore the final view only + * has outgoing edges to nodes not in the view from the given root node or from + * the newly generated primary inputs. + * + * The view reimplements the methods `size`, `num_pis`, `num_pos`, `foreach_pi`, + * `foreach_po`, `foreach_node`, `foreach_gate`, `is_pi`, `node_to_index`, and + * `index_to_node`. + * + * The view requires that the nodes' values contain their reference counts, + * i.e., they are assigned their fanout size. The values are restored by the + * view. + * + * **Required network functions:** + * - `get_node` + * - `decr_value` + * - `value` + * - `foreach_fanin` + * - `is_constant` + * - `node_to_index` + */ +template +class mffc_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit mffc_view( Ntk const& ntk, node const& root ) + : immutable_view( ntk ), _root( root ) + { + 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_decr_value_v, "Ntk does not implement the decr_value method" ); + static_assert( has_incr_value_v, "Ntk does not implement the incr_value method" ); + static_assert( has_value_v, "Ntk does not implement the value method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin 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_node_to_index_v, "Ntk does not implement the node_to_index method" ); + + const auto c0 = this->get_node( this->get_constant( false ) ); + _constants.push_back( c0 ); + _node_to_index.emplace( c0, _node_to_index.size() ); + + const auto c1 = this->get_node( this->get_constant( true ) ); + if ( c1 != c0 ) + { + _constants.push_back( c1 ); + _node_to_index.emplace( c1, _node_to_index.size() ); + ++_num_constants; + } + + _leaves.reserve( 16 ); + _nodes.reserve( _limit ); + _inner.reserve( _limit ); + update_mffcs(); + } + + inline auto size() const { return _num_constants + _num_leaves + _inner.size(); } + inline auto num_pis() const { return _num_leaves; } + inline auto num_pos() const { return _empty ? 0u : 1u; } + inline auto num_gates() const { return _inner.size(); } + + inline bool is_pi( node const& pi ) const + { + return std::find( _leaves.begin(), _leaves.end(), pi ) != _leaves.end(); + } + + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( _leaves.begin(), _leaves.end(), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + if ( _empty ) + return; + std::vector signals( 1, this->make_signal( _root ) ); + detail::foreach_element( signals.begin(), signals.end(), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( _inner.begin(), _inner.end(), fn ); + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( _constants.begin(), _constants.end(), fn ); + detail::foreach_element( _leaves.begin(), _leaves.end(), fn, _num_constants ); + detail::foreach_element( _inner.begin(), _inner.end(), fn, _num_constants + _num_leaves ); + } + + inline auto index_to_node( uint32_t index ) const + { + if ( index < _num_constants ) + { + return _constants[index]; + } + if ( index < _num_constants + _num_leaves ) + { + return _leaves[index - _num_constants]; + } + return _inner[index - _num_constants - _num_leaves]; + } + + inline auto node_to_index( node const& n ) const { return _node_to_index.at( n ); } + + void update_mffcs() + { + _leaves.clear(); + _inner.clear(); + if ( collect( _root ) ) + { + _empty = false; + compute_sets(); + } + else + { + _empty = true; + } + _num_leaves = static_cast( _leaves.size() ); + + /* restore ref counts */ + for ( auto const& n : _nodes ) + { + this->incr_fanout_size( n ); + } + } + +private: + bool collect( node const& n ) + { + if ( Ntk::is_constant( n ) ) + return true; + + if ( Ntk::is_pi( n ) ) + { + return true; + } + + /* we break from the loop over the fanins, if we find that _nodes contains + too many nodes; the return value is stored in ret_val */ + bool ret_val = true; + this->foreach_fanin( n, [&]( auto const& f ) { + _nodes.push_back( this->get_node( f ) ); + if ( this->decr_fanout_size( this->get_node( f ) ) == 0 && ( _nodes.size() > _limit || !collect( this->get_node( f ) ) ) ) + { + ret_val = false; + return false; + } + return true; + } ); + + return ret_val; + } + + void compute_sets() + { + // std::stable_sort( _nodes.begin(), _nodes.end(), + // [&]( auto const& n1, auto const& n2 ) { return static_cast( this )->node_to_index( n1 ) < static_cast( this )->node_to_index( n2 ); } ); + std::stable_sort( _nodes.begin(), _nodes.end() ); + + for ( auto const& n : _nodes ) + { + if ( Ntk::is_constant( n ) ) + { + continue; + } + + if ( this->fanout_size( n ) > 0 || Ntk::is_pi( n ) ) /* PI candidate */ + { + if ( _leaves.empty() || _leaves.back() != n ) + { + _leaves.push_back( n ); + } + } + else + { + if ( _inner.empty() || _inner.back() != n ) + { + _inner.push_back( n ); + } + } + } + + for ( auto const& n : _leaves ) + { + _node_to_index.emplace( n, _node_to_index.size() ); + } + for ( auto const& n : _inner ) + { + _node_to_index.emplace( n, _node_to_index.size() ); + } + + _inner.push_back( _root ); + _node_to_index.emplace( _root, _node_to_index.size() ); + + /* sort topologically */ + _topo.clear(); + _colors.clear(); + const auto _size = _num_constants + _inner.size() + _leaves.size(); + _colors.resize( _size, 0 ); + std::for_each( _leaves.begin(), _leaves.end(), [&]( auto& l ) { _colors[_node_to_index[l]] = 2u; } ); + for ( auto i = 0u; i < _num_constants; ++i ) + { + _colors[i] = 2u; + } + topo_sort_rec( _root ); + + assert( _inner.size() == _topo.size() ); + _inner = _topo; + } + + void topo_sort_rec( node const& n ) + { + const auto idx = _node_to_index[n]; + + /* is permanently marked? */ + if ( _colors[idx] == 2u ) + return; + + /* mark node temporarily */ + _colors[idx] = 1u; + + /* mark children */ + Ntk::foreach_fanin( n, [&]( auto const& f ) { + topo_sort_rec( Ntk::get_node( f ) ); + } ); + + /* mark node n permanently */ + _colors[idx] = 2u; + + _topo.push_back( n ); + } + +public: + std::vector _nodes, _constants, _leaves, _inner, _topo; + std::vector _colors; + unsigned _num_constants{ 1 }, _num_leaves{ 0 }; + phmap::flat_hash_map _node_to_index; + node _root; + bool _empty{ true }; + uint32_t _limit{ 100 }; +}; + +template +mffc_view( T const&, typename T::node const& ) -> mffc_view; + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/views/names_view.hpp b/include/mockturtle/views/names_view.hpp new file mode 100644 index 0000000..e633b91 --- /dev/null +++ b/include/mockturtle/views/names_view.hpp @@ -0,0 +1,210 @@ +/* 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 names_view.hpp + \brief Implements methods to declare names for network signals + + \author Heinz Riener + \author Marcel Walter + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../traits.hpp" + +#include +#include + +namespace mockturtle +{ + +template +class names_view : public Ntk +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template + names_view( Ntk const& ntk = Ntk(), StrType name = "" ) + : Ntk( ntk ), _network_name{ name } + { + } + + names_view( names_view const& named_ntk ) + : Ntk( named_ntk ), _network_name( named_ntk._network_name ), _signal_names( named_ntk._signal_names ), _output_names( named_ntk._output_names ) + { + } + + names_view& operator=( names_view const& named_ntk ) + { + if ( this != &named_ntk ) // Check for self-assignment + { + Ntk::operator=( named_ntk ); + _signal_names = named_ntk._signal_names; + _network_name = named_ntk._network_name; + _output_names = named_ntk._output_names; + } + return *this; + } + + /*! \brief Creates a primary input and set its name. + * + * \param name Name of the created primary input + */ + signal create_pi( std::string const& name = {} ) + { + const auto s = Ntk::create_pi(); + if ( !name.empty() ) + { + set_name( s, name ); + } + return s; + } + + /*! \brief Creates a primary output and set its name. + * + * \param s Signal that drives the created primary output + * \param name Name of the created primary output + */ + void create_po( signal const& s, std::string const& name = {} ) + { + const auto index = Ntk::num_pos(); + Ntk::create_po( s ); + if ( !name.empty() ) + { + set_output_name( index, name ); + } + } + + /*! \brief Sets network name. + * + * \param name Name of the network + */ + template + void set_network_name( StrType name ) noexcept + { + _network_name = name; + } + + /*! \brief Gets network name. + * + * \return Network name + */ + std::string get_network_name() const noexcept + { + return _network_name; + } + + /*! \brief Checks if a signal has a name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be checked + * \return Whether the signal has a name in record + */ + bool has_name( signal const& s ) const + { + return ( _signal_names.find( s ) != _signal_names.end() ); + } + + /*! \brief Sets the name for a signal. + * + * Note that names are set separately for complemented signals. + * + * \param s Signal to be set a name + * \param name Name of the signal + */ + void set_name( signal const& s, std::string const& name ) + { + _signal_names[s] = name; + } + + /*! \brief Gets signal name. + * + * Note that complemented signals may have different names. + * + * \param s Signal to be queried + * \return Name of the signal + */ + std::string get_name( signal const& s ) const + { + return _signal_names.at( s ); + } + + /*! \brief Checks if a primary output has a name. + * + * \param index Index of the primary output to be checked + * \return Whether the primary output has a name in record + */ + bool has_output_name( uint32_t index ) const + { + return ( _output_names.find( index ) != _output_names.end() ); + } + + /*! \brief Sets the name for a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to set a name + * \param name Name of the primary output + */ + void set_output_name( uint32_t index, std::string const& name ) + { + _output_names[index] = name; + } + + /*! \brief Gets the name of a primary output. + * + * Note that even if two primary outputs are driven by + * the same signal, they may have different names. + * + * \param index Index of the primary output to be queried + * \return Name of the primary output + */ + std::string get_output_name( uint32_t index ) const + { + return _output_names.at( index ); + } + +private: + std::string _network_name; + std::map _signal_names; + std::map _output_names; +}; /* names_view */ + +template +names_view( T const& ) -> names_view; + +template +names_view( T const&, typename T::signal const& ) -> names_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/rank_view.hpp b/include/mockturtle/views/rank_view.hpp new file mode 100644 index 0000000..ecfef63 --- /dev/null +++ b/include/mockturtle/views/rank_view.hpp @@ -0,0 +1,384 @@ +/* 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 rank_view.hpp + \brief Implements rank orders for a network + + \author Marcel Walter +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/node_map.hpp" +#include "depth_view.hpp" + +#include +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements rank orders for a network. +* +* This view assigns manipulable relative orders to the nodes of each level. +* A sequence of nodes in the same level is called a rank. The width +* of a rank is thereby defined as the number of nodes in the rank. The width of +* the network is equal to the width of the widest rank. This view implements +* functions to retrieve the assigned node position within a rank (`rank_position`) +* as well as to fetch the node in a certain rank at a certain position (`at_rank_position`). +* The ranks are assigned at construction and can be manipulated by calling the `swap` function. +* +* This view also automatically inserts new nodes into their respective rank (at the end), +* however, it does not update the information, when modifying or deleting nodes. +* +* **Required network functions:** +* - `foreach_node` +* - `get_node` +* - `num_pis` +* - `is_ci` +* - `is_constant` +* +* Example +* + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow + aig_network aig = ...; + + // create a rank view on the network + rank_view aig_rank{aig_depth}; + + // print width + std::cout << "Width: " << aig_rank.width() << "\n"; + \endverbatim +*/ +template&& has_at_rank_position_v&& has_swap_v&& has_width_v&& has_foreach_node_in_rank_v&& has_foreach_gate_in_rank_v> +class rank_view +{ +}; + +template +class rank_view : public depth_view +{ +public: + rank_view( Ntk const& ntk ) : depth_view( ntk ) + { + } +}; + +template +class rank_view : public depth_view +{ +public: + static constexpr bool is_topologically_sorted = true; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + explicit rank_view() + : depth_view(), rank_pos{ *this }, ranks{}, max_rank_width{ 0 } + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Standard constructor. + * + * \param ntk Base network + */ + explicit rank_view( Ntk const& ntk ) + : depth_view{ ntk }, rank_pos{ ntk }, ranks{ this->depth() + 1 }, max_rank_width{ 0 } + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + + init_ranks(); + + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + /*! \brief Copy constructor. */ + rank_view( rank_view const& other ) + : depth_view( other ), rank_pos{ other.rank_pos }, ranks{ other.ranks }, max_rank_width{ other.max_rank_width } + { + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + } + + rank_view& operator=( rank_view const& other ) + { + /* delete the event of this network */ + Ntk::events().release_add_event( add_event ); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + rank_pos = other.rank_pos; + ranks = other.ranks; + max_rank_width = other.max_rank_width; + + /* register new event in the other network */ + add_event = Ntk::events().register_add_event( [this]( auto const& n ) { on_add( n ); } ); + + return *this; + } + + ~rank_view() + { + Ntk::events().release_add_event( add_event ); + } + /** + * \brief Returns the rank position of a node. + * + * @param n Node to get the rank position of. + * @return Rank position of node `n`. + */ + uint32_t rank_position( node const& n ) const noexcept + { + assert( !this->is_constant( n ) && "node must not be constant" ); + + return rank_pos[n]; + } + /** + * \brief Returns the node at a certain rank position. + * + * @param level Level in the network, i.e., rank to get the node from. + * @param pos Position in the rank to get the node from. + * @return Node at position `pos` in rank `level`. + */ + node at_rank_position( uint32_t const level, uint32_t const pos ) const noexcept + { + assert( level < ranks.size() && "level must be less than the number of ranks" ); + assert( pos < ranks[level].size() && "pos must be less than the number of nodes in rank" ); + + return ranks[level][pos]; + } + /** + * \brief Returns the width of the widest rank in the network. + * + * @return Width of the widest rank in the network. + */ + uint32_t width() const noexcept + { + return max_rank_width; + } + /** + * \brief Swaps the positions of two nodes in the same rank. + * + * @param n1 First node to swap. + * @param n2 Second node to swap. + */ + void swap( node const& n1, node const& n2 ) noexcept + { + assert( this->level( n1 ) == this->level( n2 ) && "nodes must be in the same rank" ); + + auto& pos1 = rank_pos[n1]; + auto& pos2 = rank_pos[n2]; + + std::swap( ranks[this->level( n1 )][pos1], ranks[this->level( n2 )][pos2] ); + std::swap( pos1, pos2 ); + } + /** + * \brief Sorts the given rank according to a comparator. + * + * @tparam Cmp Functor type that compares two nodes. It needs to fulfill the requirements of `Compare` (named C++ requirement). + * @param level The level of the rank to sort. + * @param cmp The comparator to use. + */ + template + void sort_rank( uint32_t const level, Cmp const& cmp ) + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto& rank = ranks[level]; + + std::stable_sort( rank.begin(), rank.end(), cmp ); + std::for_each( rank.cbegin(), rank.cend(), [this, i = 0u]( auto const& n ) mutable { rank_pos[n] = i++; } ); + } + } + /** + * \brief Applies a given function to each node in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_node_in_rank( uint32_t const level, Fn&& fn ) const + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto const& rank = ranks[level]; + + detail::foreach_element( rank.cbegin(), rank.cend(), std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each node in rank order. + * + * This function overrides the `foreach_node` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_node( Fn&& fn ) const + { + for ( auto l = 0; l < ranks.size(); ++l ) + { + foreach_node_in_rank( l, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each gate in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_gate_in_rank( uint32_t const level, Fn&& fn ) const + { + // level must be less than the number of ranks + if ( level < ranks.size() ) + { + auto const& rank = ranks[level]; + + detail::foreach_element_if( + rank.cbegin(), rank.cend(), [this]( auto const& n ) { return !this->is_ci( n ); }, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each gate in rank order. + * + * This function overrides the `foreach_gate` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_gate( Fn&& fn ) const + { + for ( auto l = 0; l < ranks.size(); ++l ) + { + foreach_gate_in_rank( l, std::forward( fn ) ); + } + } + /** + * \brief Applies a given function to each PI in rank order. + * + * This function overrides the `foreach_pi` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_pi( Fn&& fn ) const + { + std::vector pis{}; + pis.reserve( this->num_pis() ); + + depth_view::foreach_pi( [&pis]( auto const& pi ) { pis.push_back( pi ); } ); + std::stable_sort( pis.begin(), pis.end(), [this]( auto const& n1, auto const& n2 ) { return rank_pos[n1] < rank_pos[n2]; } ); + detail::foreach_element( pis.cbegin(), pis.cend(), std::forward( fn ) ); + } + /** + * Overrides the base class method to also call the add_event on create_pi(). + * + * @note This can (and in fact will) lead to issues if Ntk already calls add_event functions on create_pi()! + * + * @return Newly created PI signal. + */ + signal create_pi() + { + auto const n = depth_view::create_pi(); + this->resize_levels(); + on_add( this->get_node( n ) ); + return n; + } + +private: + node_map rank_pos; + std::vector> ranks; + uint32_t max_rank_width; + + std::shared_ptr::add_event_type> add_event; + + void insert_in_rank( node const& n ) noexcept + { + auto& rank = ranks[this->level( n )]; + rank_pos[n] = rank.size(); + rank.push_back( n ); + max_rank_width = std::max( max_rank_width, static_cast( rank.size() ) ); + } + + void on_add( node const& n ) noexcept + { + if ( this->level( n ) >= ranks.size() ) + { + // add sufficient ranks to store the new node + ranks.insert( ranks.end(), this->level( n ) - ranks.size() + 1, {} ); + } + rank_pos.resize(); + + insert_in_rank( n ); + } + + void init_ranks() noexcept + { + depth_view::foreach_node( [this]( auto const& n ) { + if (!this->is_constant(n)) + { + insert_in_rank(n); + } } ); + } +}; + +template +rank_view( T const& ) -> rank_view; + +} // namespace mockturtle diff --git a/include/mockturtle/views/topo_view.hpp b/include/mockturtle/views/topo_view.hpp new file mode 100644 index 0000000..227e9c3 --- /dev/null +++ b/include/mockturtle/views/topo_view.hpp @@ -0,0 +1,324 @@ +/* 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 topo_view.hpp + \brief Reimplements foreach_node to guarantee topological order + + \author Alessandro Tempia Calvino + \author Heinz Riener + \author Mathias Soeken + \author Max Austin +*/ + +#pragma once + +#include +#include +#include + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "immutable_view.hpp" + +namespace mockturtle +{ + +/*! \brief Ensures topological order for of all nodes reachable from the outputs. + * + * Overrides the interface methods `foreach_node`, `foreach_gate`, + * `size`, `num_gates`. + * + * This class computes *on construction* a topological order of the nodes which + * are reachable from the outputs. Constant nodes and primary inputs will also + * be considered even if they are not reachable from the outputs. Further, + * constant nodes and primary inputs will be visited first before any gate node + * is visited. Constant nodes precede primary inputs, and primary inputs are + * visited in the same order in which they were created. + * + * Since the topological order is computed only once when creating an instance, + * this view disables changes to the network interface. Also, since only + * reachable nodes are traversed, not all network nodes may be called in + * `foreach_node` and `foreach_gate`. + * + * **Required network functions:** + * - `get_constant` + * - `foreach_pi` + * - `foreach_po` + * - `foreach_fanin` + * - `incr_trav_id` + * - `set_visited` + * - `trav_id` + * - `visited` + * + * Example + * + \verbatim embed:rst + + .. code-block:: c++ + + // create network somehow; aig may not be in topological order + aig_network aig = ...; + + // create a topological view on the network + topo_view aig_topo{aig}; + + // call algorithm that requires topological order + cut_enumeration( aig_topo ); + \endverbatim + */ +template> +class topo_view +{ +}; + +template +class topo_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + static constexpr bool is_topologically_sorted = true; + + /*! \brief Default constructor. + * + * Constructs topological view on another network. + */ + topo_view( Ntk const& ntk ) : immutable_view( ntk ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Default constructor. + * + * Constructs topological view, but only for the transitive fan-in starting + * from a given start signal. + */ + topo_view( Ntk const& ntk, typename Ntk::signal const& start_signal ) + : immutable_view( ntk ), + start_signal( start_signal ) + { + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_incr_trav_id_v, "Ntk does not implement the incr_trav_id method" ); + static_assert( has_set_visited_v, "Ntk does not implement the set_visited method" ); + static_assert( has_trav_id_v, "Ntk does not implement the trav_id method" ); + static_assert( has_visited_v, "Ntk does not implement the visited method" ); + + update_topo(); + } + + /*! \brief Reimplementation of `size`. */ + auto size() const + { + return static_cast( topo_order.size() ); + } + + /*! \brief Reimplementation of `num_gates`. */ + auto num_gates() const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + return static_cast( topo_order.size() - offset ); + } + + /*! \brief Reimplementation of `node_to_index`. */ + uint32_t node_to_index( node const& n ) const + { + return std::distance( std::begin( topo_order ), std::find( std::begin( topo_order ), std::end( topo_order ), n ) ); + } + + /*! \brief Reimplementation of `index_to_node`. */ + node index_to_node( uint32_t index ) const + { + return topo_order.at( index ); + } + + /*! \brief Reimplementation of `foreach_node`. */ + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( topo_order.begin(), + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_node` in reverse topological order. */ + template + void foreach_node_reverse( Fn&& fn ) const + { + detail::foreach_element( topo_order.rbegin(), + topo_order.rend(), + fn ); + } + + /*! \brief Reimplementation of `foreach_gate`. */ + template + void foreach_gate( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.begin() + offset, + topo_order.end(), + fn ); + } + + /*! \brief Implementation of `foreach_gate` in reverse topological order. */ + template + void foreach_gate_reverse( Fn&& fn ) const + { + uint32_t const offset = 1u + this->num_pis() + ( this->get_node( this->get_constant( true ) ) != this->get_node( this->get_constant( false ) ) ); + detail::foreach_element( topo_order.rbegin(), + topo_order.rend() - offset, + fn ); + } + + /*! \brief Reimplementation of `foreach_po`. + * + * If `start_signal` is provided in constructor, only this is returned as + * primary output, otherwise reverts to original `foreach_po` implementation. + */ + template + void foreach_po( Fn&& fn ) const + { + if ( start_signal ) + { + std::vector signals( 1, *start_signal ); + detail::foreach_element( signals.begin(), signals.end(), fn ); + } + else + { + Ntk::foreach_po( fn ); + } + } + + uint32_t num_pos() const + { + return start_signal ? 1 : Ntk::num_pos(); + } + + void update_topo() + { + this->incr_trav_id(); + this->incr_trav_id(); + topo_order.reserve( this->size() ); + + /* constants and PIs */ + const auto c0 = this->get_node( this->get_constant( false ) ); + topo_order.push_back( c0 ); + this->set_visited( c0, this->trav_id() ); + + if ( const auto c1 = this->get_node( this->get_constant( true ) ); this->visited( c1 ) != this->trav_id() ) + { + topo_order.push_back( c1 ); + this->set_visited( c1, this->trav_id() ); + } + + this->foreach_ci( [this]( auto n ) { + if ( this->visited( n ) != this->trav_id() ) + { + topo_order.push_back( n ); + this->set_visited( n, this->trav_id() ); + } + } ); + + if ( start_signal ) + { + if ( this->visited( this->get_node( *start_signal ) ) == this->trav_id() ) + return; + create_topo_rec( this->get_node( *start_signal ) ); + } + else + { + Ntk::foreach_co( [this]( auto f ) { + /* node was already visited */ + if ( this->visited( this->get_node( f ) ) == this->trav_id() ) + return; + + create_topo_rec( this->get_node( f ) ); + } ); + } + } + +private: + void create_topo_rec( node const& n ) + { + /* is permanently marked? */ + if ( this->visited( n ) == this->trav_id() ) + return; + + /* ensure that the node is not temporarily marked */ + assert( this->visited( n ) != this->trav_id() - 1 ); + + /* mark node temporarily */ + this->set_visited( n, this->trav_id() - 1 ); + + /* mark children */ + this->foreach_fanin( n, [this]( signal const& f ) { + create_topo_rec( this->get_node( f ) ); + } ); + + /* mark node n permanently */ + this->set_visited( n, this->trav_id() ); + + /* visit node */ + topo_order.push_back( n ); + } + +private: + std::vector topo_order; + std::optional start_signal; +}; + +template +class topo_view : public Ntk +{ +public: + topo_view( Ntk const& ntk ) : Ntk( ntk ) + { + } +}; + +template +topo_view( T const& ) -> topo_view; + +template +topo_view( T const&, typename T::signal const& ) -> topo_view; + +} // namespace mockturtle \ No newline at end of file diff --git a/include/mockturtle/views/window_view.hpp b/include/mockturtle/views/window_view.hpp new file mode 100644 index 0000000..4fafb45 --- /dev/null +++ b/include/mockturtle/views/window_view.hpp @@ -0,0 +1,308 @@ +/* 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 window_view.hpp + \brief Implements an isolated view on a window in a network + + \author Heinz Riener + \author Mathias Soeken + \author Siang-Yun (Sonia) Lee +*/ + +#pragma once + +#include "../networks/detail/foreach.hpp" +#include "../traits.hpp" +#include "../utils/window_utils.hpp" +#include "immutable_view.hpp" + +#include +#include +#include +#include +#include + +namespace mockturtle +{ + +/*! \brief Implements an isolated view on a window in a network. + * + * This view creates a network from a window in a large network. The + * window is specified by three parameters: + * 1.) `inputs` are the common support of all window nodes, they do + * not overlap with `gates` (i.e., the intersection of `inputs` and + * `gates` is the empty set). + * 2.) `gates` are the nodes in the window, supported by the + * `inputs` (i.e., `gates` are in the transitive fanout of the + * `inputs`, + * 3.) `outputs` are signals (regular or complemented nodes) + * pointing to nodes in `gates` or `inputs`. Not all fanouts + * of an output node are already part of the window. + * + * The second parameter `gates` has to be passed and is not + * automatically computed (for example in contrast to `cut_view`), + * because there are different strategies to construct a window from a + * support set. The outputs could be automatically computed. + * + * The window_view implements three new API methods: + * 1.) `belongs_to`: takes a node (or a signal) and returns true if and + * only if the corresponding node belongs to the window + * 2.) `foreach_internal_fanout`: takes a node and invokes a predicate + on all fanout nodes of the node that belong to the window + * 3.) `foreach_external_fanout`: takes a node and invokes a predicate + on all fanouts of the node that do not belong to the window + */ +template +class window_view : public immutable_view +{ +public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + template>> + explicit window_view( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates ) + : immutable_view( ntk ), _inputs( inputs ), _outputs( outputs ) + { + construct( inputs, gates ); + } + + explicit window_view( Ntk const& ntk, std::vector const& inputs, std::vector const& outputs, std::vector const& gates ) + : immutable_view( ntk ), _inputs( inputs ) + { + construct( inputs, gates ); + + /* convert output nodes to signals */ + std::transform( std::begin( outputs ), std::end( outputs ), std::back_inserter( _outputs ), + [this]( node const& n ) { + return this->make_signal( n ); + } ); + } + +#pragma region Window + template>> + inline bool belongs_to( signal const& s ) const + { + return std::find( std::begin( _nodes ), std::end( _nodes ), get_node( s ) ) != std::end( _nodes ); + } + + inline bool belongs_to( node const& n ) const + { + return std::find( std::begin( _nodes ), std::end( _nodes ), n ) != std::end( _nodes ); + } +#pragma endregion + +#pragma region Structural properties + inline uint32_t size() const + { + return static_cast( _nodes.size() ); + } + + inline uint32_t num_cis() const + { + return num_pis(); + } + + inline uint32_t num_cos() const + { + return num_pos(); + } + + inline uint32_t num_pis() const + { + return static_cast( _inputs.size() ); + } + + inline uint32_t num_pos() const + { + return static_cast( _outputs.size() ); + } + + inline uint32_t num_registers() const + { + return 0u; + } + + inline uint32_t num_gates() const + { + return static_cast( _nodes.size() - _inputs.size() - 1u ); + } + + inline uint32_t fanout_size( node const& n ) const = delete; + + inline uint32_t node_to_index( node const& n ) const + { + return _node_to_index.at( n ); + } + + inline node index_to_node( uint32_t index ) const + { + return _nodes[index]; + } + + inline bool is_pi( node const& n ) const + { + return std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ); + } + + inline bool is_ci( node const& n ) const + { + return is_pi( n ); + } + + signal po_at( uint32_t index ) const + { + assert( index < _outputs.size() ); + return *( std::begin( _outputs ) + index ); + } + + signal co_at( uint32_t index ) const + { + return po_at( index ); + } +#pragma endregion + +#pragma region Node and signal iterators + template + void foreach_pi( Fn&& fn ) const + { + detail::foreach_element( std::begin( _inputs ), std::end( _inputs ), fn ); + } + + template + void foreach_po( Fn&& fn ) const + { + detail::foreach_element( std::begin( _outputs ), std::end( _outputs ), fn ); + } + + template + void foreach_ci( Fn&& fn ) const + { + foreach_pi( fn ); + } + + template + void foreach_co( Fn&& fn ) const + { + foreach_po( fn ); + } + + template + void foreach_ro( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_ri( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_register( Fn&& fn ) const + { + (void)fn; + } + + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ), std::end( _nodes ), fn ); + } + + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ) + 1u + _inputs.size(), std::end( _nodes ), fn ); + } + + template + void foreach_fanin( node const& n, Fn&& fn ) const + { + /* constants and inputs do not have fanins */ + if ( this->is_constant( n ) || + std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ) ) + { + return; + } + + /* if it's not a window input, the node has to be a window node */ + assert( std::find( std::begin( _nodes ) + 1 + _inputs.size(), std::end( _nodes ), n ) != std::end( _nodes ) ); + immutable_view::foreach_fanin( n, fn ); + } + + template + void foreach_internal_fanout( node const& n, Fn&& fn ) const + { + this->foreach_fanout( n, [&]( node const& fo ) { + if ( tbelongs_to( fo ) ) + { + fn( fo ); + } + } ); + } + + template + void foreach_external_fanout( node const& n, Fn&& fn ) const + { + this->foreach_fanout( n, [&]( node const& fo ) { + if ( !belongs_to( fo ) ) + { + fn( fo ); + } + } ); + } +#pragma endregion + +protected: + void construct( std::vector const& inputs, std::vector const& gates ) + { + /* copy constant to nodes */ + _nodes.emplace_back( this->get_node( this->get_constant( false ) ) ); + + /* copy inputs to nodes */ + std::copy( std::begin( inputs ), std::end( inputs ), std::back_inserter( _nodes ) ); + + /* copy gates to nodes */ + std::copy( std::begin( gates ), std::end( gates ), std::back_inserter( _nodes ) ); + + /* create a mapping from node id (index in the original network) to window index */ + for ( uint32_t index = 0; index < _nodes.size(); ++index ) + { + _node_to_index[_nodes.at( index )] = index; + } + } + +protected: + std::vector _inputs; + std::vector _outputs; + std::vector _nodes; + std::unordered_map _node_to_index; +}; /* window_view */ + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/util/cover_to_bbdd.hpp b/include/util/cover_to_bbdd.hpp new file mode 100644 index 0000000..9cffedb --- /dev/null +++ b/include/util/cover_to_bbdd.hpp @@ -0,0 +1,149 @@ +#pragma once + +#include "../bbdd/include/bbdd.hpp" +#include "../bbdd/include/bbdd_node.hpp" +#include "mockturtle/traits.hpp" +#include "../bbdd/include/unique_table.hpp" +#include "util.hpp" +#include +#include + +#define LEFT_CHILD(node) node.children[0].index +#define RIGHT_CHILD(node) node.children[1].index + +#define CUBE(node) ntk._storage->data.covers[node.data[1].h1] + +int nodes_visited; + +using namespace mockturtle; + +template +bbdd_node_t *create_bbdd(Unique_table *table, Ntk &ntk, const auto &node_i, + int output_completed) { + static_assert(has_is_ci_v, "Ntk does not implement the is_ci method"); + const auto &node = ntk._storage->nodes[node_i]; + bbdd_node_t *f, *g, *result; + bbdd_op_t op = Util::cube_to_bbdd_op(CUBE(node)); + if (ntk.visited(node_i)) { + return table->get_node_p(ntk.visited(node_i)); + } +#ifdef DEBUG_COVER + printf("[INFO] CREATE: node %d with op %s\n", node_i, bbdd_op_s[op].c_str()); +#endif + if (node.children.size() != 2 && node.children.size() != 1) { + if (node_i == 1 || node_i == 0) { + return table->get_node_p(node_i); + } +#ifdef DEBUG_COVER + std::cout << node_i << ": " << node.children.size() << " with " + << bbdd_op_s[op].c_str() << "\n"; +#endif + assert(ntk.is_ci(node_i)); + assert(node_i < pow(2, 31)); + assert(node_i - 2 < ntk._storage->inputs.size()); + return table->insert_node({{(node_index_t)node_i, INT_MAX}, 0, 1}); + } + assert(node.children.size() == 2 || node.children.size() == 1); + // all the logic gates except buf and inv + if (node.children.size() == 2) { + node_index_t f_i = LEFT_CHILD(node), g_i = RIGHT_CHILD(node); + if (ntk.is_ci(LEFT_CHILD(node)) && ntk.is_ci(RIGHT_CHILD(node))) { + result = base_case_two_inputs(table, f_i, g_i, op); +#ifdef DEBUG_COVER + printf("[INFO] CREATE: two input case %d: %d %s %d\n", result->index, f_i, + bbdd_op_s[op].c_str(), g_i); +#endif + return result; + } + f = create_bbdd(table, ntk, LEFT_CHILD(node), output_completed); + g = create_bbdd(table, ntk, RIGHT_CHILD(node), output_completed); + result = merge_bbdds(table, f, g, op); +#ifdef DEBUG_COVER + std::cout << "[INFO] CREATE: merging " << result->index << ": " << f->index + << " " << bbdd_op_s[op] << " " << g->index << "\n"; + dump_node(g); +#endif + ntk.set_visited(node_i, result->index); + Util::show_progress_bar( + {{"Output", {output_completed, ntk._storage->outputs.size()}}, + {"Nodes", {++nodes_visited, ntk._storage->nodes.size()}}}, + 50); + return result; + } else { + // buf and inverter nodes + f = create_bbdd(table, ntk, LEFT_CHILD(node), output_completed); + if (op == bbdd_inv) { + result = negate_recursive(table, f); + ntk.set_visited(node_i, result->index); + Util::show_progress_bar( + {{"Output", {output_completed, ntk._storage->outputs.size()}}, + {"Nodes", {++nodes_visited, ntk._storage->nodes.size()}}}, + 50); + return result; + } + ntk.set_visited(node_i, f->index); + Util::show_progress_bar( + {{"Output", {output_completed, ntk._storage->outputs.size()}}, + {"Nodes", {++nodes_visited, ntk._storage->nodes.size()}}}, + 50); + return f; + } +} + +template +void cover_to_bbdd(Unique_table *table, Ntk &ntk, bool use_height, + int sifting_repetitions, std::ofstream &order_file) { + + static_assert(has_foreach_co_v, + "Ntk does not implement the foreach_pi method"); + static_assert(has_get_node_v, + "Ntk does not implement the get_node method"); + static_assert(has_set_visited_v, + "Ntk does not implement the set_visited method"); + static_assert(has_visited_v, + "Ntk does not implement the set_visited method"); + int index = ntk._storage->inputs.size() + 2; + int output_completed = 0; + nodes_visited = 0; + Util::show_progress_bar( + {{"Output", {output_completed, ntk._storage->outputs.size()}}, + {"Nodes", {++nodes_visited, ntk._storage->nodes.size()}}}, + 50); + ntk.foreach_co([ntk, &table, &index, &output_completed, sifting_repetitions](const auto &node_i) { + const auto &node = ntk.get_node(node_i); + auto &n = ntk._storage->nodes[node_i]; +#ifdef DEBUG_COVER + std::cout << "output with node_i: " << node_i << " and n->index: " << " : " + << n.children.size() << "\n"; +#endif + bbdd_node_t *output = + add_ref(create_bbdd(table, ntk, node, output_completed)); + Util::show_progress_bar( + {{"Output", {++output_completed, ntk._storage->outputs.size()}}, + {"Nodes", {++nodes_visited, ntk._storage->nodes.size()}}}, + 50); + table->add_output(ntk.get_signal_name(index), output); + index++; + /*if (table->get_total_height() > 20 && table->get_total_height() < 30) { + sift(table, &Unique_table::get_total_number_nodes, table); + ntk.clear_visited(); + }*/ + }); + for (int i = 0; i < sifting_repetitions; i++) { + /*printf("total height before sifting: %d and total number of nodes before: " + "%d\n", + table->get_max_height(), table->get_total_number_nodes()); + if (order_file.is_open()) { + dump_ordering(table->cvo, order_file); + order_file << ";" << table->get_max_height() << ";" + << table->get_total_number_nodes() << "\n"; + }*/ + if (table->get_total_height() < 30) { + if (use_height) { + sift(table, &Unique_table::get_total_height, table); + } else { + sift(table, &Unique_table::get_total_number_nodes, table); + } + } + } +} diff --git a/include/util/cover_to_bdd.hpp b/include/util/cover_to_bdd.hpp new file mode 100644 index 0000000..dc444e1 --- /dev/null +++ b/include/util/cover_to_bdd.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include "../bbdd/include/bbdd_node.hpp" +#include "bdd.h" +#include "util.hpp" + +#define LEFT_CHILD(node) node.children[0].index +#define RIGHT_CHILD(node) node.children[1].index +#define CUBE(node) ntk._storage->data.covers[node.data[1].h1] + +using namespace mockturtle; + +template bdd create_bdd(Ntk &ntk, const auto &node_i) { + static_assert(has_is_ci_v, "Ntk does not implement the is_ci method"); + const auto &node = ntk._storage->nodes[node_i]; + bdd f, g, result; + bbdd_op_t op = Util::cube_to_bbdd_op(CUBE(node)); + if (node.children.size() != 2 && node.children.size() != 1) { + if (node_i == 1) { +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: constant 1 node\n"); +#endif + return bdd_true(); + } + if (node_i == 0) { +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: constant 0 node\n"); +#endif + return bdd_false(); + } + assert(ntk.is_ci(node_i)); + assert(node_i < pow(2, 31)); + assert(node_i - 2 < ntk._storage->inputs.size()); +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: base case input: %d\n", node_i); +#endif + return bdd_ithvar(node_i - 2); + } + assert(node.children.size() == 2 || node.children.size() == 1); + // all the logic gates except buf and inv + if (node.children.size() == 2) { + node_index_t f_i = LEFT_CHILD(node), g_i = RIGHT_CHILD(node); + f = create_bdd(ntk, LEFT_CHILD(node)); +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: return from f\n"); +#endif + g = create_bdd(ntk, RIGHT_CHILD(node)); +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: merge %d %s %d\n", f, + bbdd_op_s[op].c_str(), g); +#endif + switch (op) { + case bbdd_and: + result = bdd_apply(f, g, bddop_and); + break; + case bbdd_xor: + result = bdd_apply(f, g, bddop_xor); + break; + case bbdd_xnor: + result = bdd_not(bdd_apply(f, g, bddop_xor)); + break; + case bbdd_or: + result = bdd_apply(f, g, bddop_or); + break; + case bbdd_nand: + result = bdd_apply(f, g, bddop_nand); + break; + case bbdd_nor: + result = bdd_apply(f, g, bddop_nor); + break; + default: + printf("%s\n", bbdd_op_s[op].c_str()); + assert(false && "not implemented yet"); + break; + } + return result; + } else { + // buf and inverter nodes + f = create_bdd(ntk, LEFT_CHILD(node)); + // TODO how to invert + if (op == bbdd_inv) { +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: negate\n"); +#endif + result = bdd_not(f); + return result; + } +#ifdef DEBUG_BDD + printf("[INFO] cover to bdd: buffer\n"); +#endif + return f; + } +} + +template void cover_to_bdd(Ntk &ntk, std::vector> &output_map) { + + static_assert(has_foreach_co_v, + "Ntk does not implement the foreach_pi method"); + static_assert(has_get_node_v, + "Ntk does not implement the get_node method"); + static_assert(has_set_visited_v, + "Ntk does not implement the set_visited method"); + static_assert(has_visited_v, + "Ntk does not implement the set_visited method"); + int index = ntk._storage->inputs.size() + 2; + ntk.foreach_co([ntk, &index, &output_map](const auto &node_i) { + const auto &node = ntk.get_node(node_i); + auto &n = ntk._storage->nodes[node_i]; + bdd output = create_bdd(ntk, node); + output_map.emplace_back(output, ntk.get_signal_name(index)); + bdd_printtable(output); + index++; + }); + } diff --git a/include/util/util.hpp b/include/util/util.hpp new file mode 100644 index 0000000..d2aec2b --- /dev/null +++ b/include/util/util.hpp @@ -0,0 +1,97 @@ +#include "../bbdd/include/bbdd_node.hpp" +#include "kitty/cube.hpp" +#include "mockturtle/networks/cover.hpp" +#include +#include +#include +#include + +class Util { + +public: + static bbdd_op_t + cube_to_bbdd_op(std::pair, bool> const &cover) { + bbdd_op_t type = bbdd_none; + if (cover.second) { // on-set + if (cover.first.size() == 1) { + if (cover.first[0] == kitty::cube("11")) { + type = bbdd_and; + } else if (cover.first[0] == kitty::cube("0")) { + type = bbdd_inv; + } else if (cover.first[0] == kitty::cube("00")) { + type = bbdd_and; + } else if (cover.first[0] == kitty::cube("01")) { + type = bbdd_notand; + } else if (cover.first[0] == kitty::cube("10")) { + type = bbdd_andnot; + } else if (cover.first[0] == kitty::cube("1")) { + type = bbdd_buf; + } else if (cover.first[0] == kitty::cube("")) { + type = bbdd_one; + } + } else if (cover.first.size() == 2) { + if (cover.first[0] == kitty::cube("1-") && + cover.first[1] == kitty::cube("-1")) { + type = bbdd_or; + } else if (cover.first[0] == kitty::cube("10") && + cover.first[1] == kitty::cube("01")) { + type = bbdd_xor; + } else if (cover.first[0] == kitty::cube("11") && + cover.first[1] == kitty::cube("00")) { + type = bbdd_xnor; + } + } else if (cover.first.size() == 3) { + if (cover.first[0] == kitty::cube("10") && + cover.first[1] == kitty::cube("01") && + cover.first[0] == kitty::cube("11")) { + type = bbdd_or; + } + } + } else if (!cover.second) { // off-set + if (cover.first.size() == 1) { + if (cover.first[0] == kitty::cube("")) { + type = bbdd_zero; + } + if (cover.first[0] == kitty::cube("00")) { + type = bbdd_or; + } else if (cover.first[0] == kitty::cube("01")) { + type = bbdd_ornot; + } else if (cover.first[0] == kitty::cube("10")) { + type = bbdd_notor; + } else if (cover.first[0] == kitty::cube("11")) { + type = bbdd_nor; + } + } + } + if (type == bbdd_none) { + kitty::print_cubes(cover.first); + } + assert(type != bbdd_none && "[ERROR] cube function not implemented\n"); + return type; + } + + // format list(progress name, (share, base)) + void static show_progress_bar(std::vector>> progress_v, + int bar_width) { + for (std::pair> progress : progress_v) { + std::string name = progress.first; + int share = progress.second.first; + int base = progress.second.second; + int current_width = bar_width / progress_v.size(); + float percent = (float)share / base; + int pos = percent * current_width; + std::cout << name << ": ["; + for (int i = 0; i < current_width; i++) { + if (i < pos) + std::cout << "="; + else if (i == pos) + std::cout << ">"; + else + std::cout << " "; + } + std::cout << "] " << share << "/" << base << " " << (int)(percent * 100) << "% "; + } + std::cout << " \r"; + std::cout.flush(); + } +}; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..b6594fa --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,91 @@ +if (NOT TARGET parallel_hashmap) + add_library(parallel_hashmap INTERFACE) # 2020.11 + target_include_directories(parallel_hashmap SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/parallel_hashmap) +endif() + +if (NOT TARGET fmt) + add_library(fmt INTERFACE) # v6.3.0 + target_include_directories(fmt SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/fmt) + target_compile_definitions(fmt INTERFACE FMT_HEADER_ONLY) +endif() + +if (NOT TARGET kitty) + add_library(kitty INTERFACE) # v0.4 + target_include_directories(kitty SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/kitty) +endif() + +if (NOT TARGET range) + add_library(rang INTERFACE) + target_include_directories(rang SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rang) +endif() + +if (NOT TARGET lorina) + add_library(lorina INTERFACE) # v0.1 + target_include_directories(lorina SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lorina) + target_link_libraries(lorina INTERFACE rang fmt) +endif() + +if (NOT TARGET json) + add_library(json INTERFACE) # v3.5.0 + target_include_directories(json SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/json) +endif() + +if (NOT TARGET percy) + add_library(percy INTERFACE) # >v0.1.2 + target_include_directories(percy SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/percy) + + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + target_link_libraries(percy INTERFACE Threads::Threads) + + set(ABC_USE_NAMESPACE "pabc") + set(STATIC_LIBABC true) + add_subdirectory(abcsat) + if (UNIX) + target_compile_definitions(libabcsat PUBLIC "LIN64" ABC_NAMESPACE=pabc ABC_NO_USE_READLINE) + elseif(WIN32) + target_compile_definitions(libabcsat PUBLIC ABC_NAMESPACE=pabc ABC_USE_NO_READLINE NOMINMAX WIN32_NO_DLL _CRT_SECURE_NO_WARNINGS) + endif() + target_link_libraries(percy INTERFACE libabcsat) + + if (ENABLE_NAUTY) + add_subdirectory(nauty) + target_link_libraries(percy INTERFACE nauty) + else() + target_compile_definitions(percy INTERFACE DISABLE_NAUTY) + endif() +endif() + +if (NOT TARGET bill) + add_library(bill INTERFACE) + target_include_directories(bill SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/bill) + + if(BILL_Z3) + target_compile_definitions(bill INTERFACE BILL_HAS_Z3) + set(BILL_Z3_INCLUDE_PATH "" CACHE PATH "Path to Z3 includes, e.g., z3++.h") + set(BILL_Z3_LIBRARY_PATH "" CACHE PATH "Path to Z3 library, e.g., libz3.a") + if(NOT "${BILL_Z3_INCLUDE_PATH}" STREQUAL "") + target_include_directories(bill SYSTEM INTERFACE ${BILL_Z3_INCLUDE_PATH}) + endif() + if(NOT "${BILL_Z3_LIBRARY_PATH}" STREQUAL "") + target_link_directories(bill INTERFACE ${BILL_Z3_LIBRARY_PATH}) + endif() + if (WIN32) + target_link_libraries(bill INTERFACE libz3) + else() + target_link_libraries(bill INTERFACE z3) + endif() + endif() +endif() + +if (NOT TARGET libabcesop) + set(STATIC_LIBABC true) + add_subdirectory(abcesop) +endif() + +if (ENABLE_MATPLOTLIB AND NOT TARGET matplot) + find_package(Python3 COMPONENTS Development NumPy) + add_library(matplot INTERFACE) + target_include_directories(matplot SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/matplot/ ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS}) + target_link_libraries(matplot INTERFACE Python3::Python Python3::NumPy) +endif() diff --git a/lib/abcesop/CMakeLists.txt b/lib/abcesop/CMakeLists.txt new file mode 100644 index 0000000..c2a056b --- /dev/null +++ b/lib/abcesop/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required (VERSION 3.6) + +project(libabcesop LANGUAGES CXX) + +include_directories(${PROJECT_SOURCE_DIR}) +file(GLOB ABC_SRC *.cpp) + +# Surpress warnings in external library +if (UNIX) + add_compile_options("-w") +elseif (MSVC) + string(REPLACE "/W3" "/w" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +endif() + +if (STATIC_LIBABC) + add_library(libabcesop STATIC EXCLUDE_FROM_ALL ${ABC_SRC}) + set_property(TARGET libabcesop PROPERTY OUTPUT_NAME libabcesop) +else() + add_library(libabcesop SHARED EXCLUDE_FROM_ALL ${ABC_SRC}) + set_property(TARGET libabcesop PROPERTY OUTPUT_NAME libabcesop) + set_property(TARGET libabcesop PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(libabcesop INTERFACE ${PROJECT_SOURCE_DIR}) + +if (UNIX) + target_compile_definitions(libabcesop PUBLIC "LIN64" ABC_NO_USE_READLINE) +elseif(WIN32) + target_compile_definitions(libabcesop PUBLIC ABC_USE_NO_READLINE NOMINMAX WIN32_NO_DLL _CRT_SECURE_NO_WARNINGS) +endif() diff --git a/lib/abcesop/eabc/abc_global.h b/lib/abcesop/eabc/abc_global.h new file mode 100644 index 0000000..98a8fc0 --- /dev/null +++ b/lib/abcesop/eabc/abc_global.h @@ -0,0 +1,157 @@ +/**CFile**************************************************************** + + FileName [abc_global.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Global declarations.] + + Synopsis [Global declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Jan 30, 2009.] + + Revision [$Id: abc_global.h,v 1.00 2009/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#pragma once + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#ifndef __MINGW32__ +//#define inline __inline // compatible with MS VS 6.0 +#pragma warning(disable : 4152) // warning C4152: nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4244) // warning C4244: '+=' : conversion from 'int ' to 'unsigned short ', possible loss of data +#pragma warning(disable : 4514) // warning C4514: 'Vec_StrPop' : unreferenced inline function has been removed +#pragma warning(disable : 4710) // warning C4710: function 'Vec_PtrGrow' not inlined +//#pragma warning( disable : 4273 ) +#endif +#endif + +#ifdef WIN32 + #ifdef WIN32_NO_DLL + #define ABC_DLLEXPORT + #define ABC_DLLIMPORT + #else + #define ABC_DLLEXPORT __declspec(dllexport) + #define ABC_DLLIMPORT __declspec(dllimport) + #endif +#else /* defined(WIN32) */ +#define ABC_DLLIMPORT +#endif /* defined(WIN32) */ + +#ifndef ABC_DLL +#define ABC_DLL ABC_DLLIMPORT +#endif + +#if !defined(___unused) +#if defined(__GNUC__) +#define ___unused __attribute__ ((__unused__)) +#else +#define ___unused +#endif +#endif + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +/** + * Signed integral type that can contain a pointer. + * This is a signed integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRINT_T; +#elif defined(ABC_USE_STDINT_H) +typedef intptr_t ABC_PTRINT_T; +#elif defined(LIN64) +typedef long ABC_PTRINT_T; +#elif defined(NT64) +typedef long long ABC_PTRINT_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef int ABC_PTRINT_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * 64-bit signed integral type. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_INT64_T; +#elif defined(ABC_USE_STDINT_H) +typedef int64_t ABC_INT64_T; +#elif defined(LIN64) +typedef long ABC_INT64_T; +#elif defined(NT64) || defined(LIN) +typedef long long ABC_INT64_T; +#elif defined(WIN32) || defined(NT) +typedef signed __int64 ABC_INT64_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_SWAP(Type, a, b) { Type t = a; a = b; b = t; } + +#define ABC_ALLOC(type, num) ((type *) malloc(sizeof(type) * (size_t)(num))) +#define ABC_CALLOC(type, num) ((type *) calloc((size_t)(num), sizeof(type))) +#define ABC_FALLOC(type, num) ((type *) memset(malloc(sizeof(type) * (size_t)(num)), 0xff, sizeof(type) * (size_t)(num))) +#define ABC_FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define ABC_REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (size_t)(num))) : \ + ((type *) malloc(sizeof(type) * (size_t)(num)))) + +static inline int Abc_MaxInt( int a, int b ) { return a > b ? a : b; } +static inline int Abc_Base2Log( unsigned n ) { int r; if ( n < 2 ) return (int)n; for ( r = 0, n--; n; n >>= 1, r++ ) {}; return r; } +static inline char * Abc_UtilStrsav( char * s ) { return s ? strcpy(ABC_ALLOC(char, strlen(s)+1), s) : NULL; } +static inline int Abc_Lit2Var( int Lit ) { assert(Lit >= 0); return Lit >> 1; } +static inline int Abc_LitIsCompl( int Lit ) { assert(Lit >= 0); return Lit & 1; } + +// time counting +typedef ABC_INT64_T abctime; +static inline abctime Abc_Clock() +{ +#if (defined(LIN) || defined(LIN64)) && !(__APPLE__ & __MACH__) && !defined(__MINGW32__) + struct timespec ts; + if ( clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) < 0 ) + return (abctime)-1; + abctime res = ((abctime) ts.tv_sec) * CLOCKS_PER_SEC; + res += (((abctime) ts.tv_nsec) * CLOCKS_PER_SEC) / 1000000000; + return res; +#else + return (abctime) clock(); +#endif +} + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/lib/abcesop/eabc/exor.h b/lib/abcesop/eabc/exor.h new file mode 100644 index 0000000..6a25ccf --- /dev/null +++ b/lib/abcesop/eabc/exor.h @@ -0,0 +1,184 @@ +/**CFile**************************************************************** + + FileName [exor.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Internal declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exor.h,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Interface of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Main Module /// +/// /// +/// Ver. 1.0. Started - July 15, 2000. Last update - July 20, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 10, 2000 /// +/// Ver. 1.7. Started - Sep 20, 2000. Last update - Sep 23, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include "eabc/abc_global.h" +#include "eabc/vecInt.h" +#include "eabc/vecPtr.h" +#include "eabc/vecWec.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +enum { + // the number of bits per integer + BPI = 32, + BPIMASK = 31, + LOGBPI = 5, + + // the maximum number of input variables + MAXVARS = 1000, + + // the number of cubes that are allocated additionally + ADDITIONAL_CUBES = 33, + + // the factor showing how many cube pairs will be allocated + CUBE_PAIR_FACTOR = 20, + // the following number of cube pairs are allocated: + // nCubesAlloc/CUBE_PAIR_FACTOR + + DIFFERENT = 0x55555555, +}; + +extern unsigned char BitCount[]; + +static inline int BIT_COUNT(int w) { return BitCount[(w)&0xffff] + BitCount[(w)>>16]; } + +static inline int VarWord(int element) { return element>>LOGBPI; } +static inline int VarBit(int element) { return element&BPIMASK; } + +static inline float TICKS_TO_SECONDS(abctime time) { return (float)time/(float)CLOCKS_PER_SEC; } + +//////////////////////////////////////////////////////////////////////// +/// CUBE COVER and CUBE typedefs /// +//////////////////////////////////////////////////////////////////////// + +typedef enum { MULTI_OUTPUT = 1, SINGLE_NODE, MULTI_NODE } type; + +// infomation about the cover +typedef struct cinfo_tag +{ + int nVarsIn; // number of input binary variables in the cubes + int nVarsOut; // number of output binary variables in the cubes + int nWordsIn; // number of input words used to represent the cover + int nWordsOut; // number of output words used to represent the cover + int nCubesAlloc; // the number of allocated cubes + int nCubesBefore; // number of cubes before simplification + int nCubesInUse; // number of cubes after simplification + int nCubesFree; // number of free cubes + int nLiteralsBefore;// number of literals before + int nLiteralsAfter; // number of literals after + int QCostBefore; // q-cost before + int QCostAfter; // q-cost after + int cIDs; // the counter of cube IDs + + int Verbosity; // verbosity level + int Quality; // quality + int nCubesMax; // maximum number of cubes in starting cover + int fUseQCost; // use q-cost instead of literal count + + abctime TimeRead; // reading time + abctime TimeStart; // starting cover computation time + abctime TimeMin; // pure minimization time +} cinfo; + +// representation of one cube (24 bytes + bit info) +typedef unsigned int drow; +typedef unsigned char byte; +typedef struct cube +{ + byte fMark; // the flag which is TRUE if the cubes is enabled + byte ID; // (almost) unique ID of the cube + short a; // the number of literals + short z; // the number of 1's in the output part + short q; // user cost + drow* pCubeDataIn; // a pointer to the bit string representing literals + drow* pCubeDataOut; // a pointer to the bit string representing literals + struct cube* Prev; // pointers to the previous/next cubes in the list/ring + struct cube* Next; +} Cube; + + +// preparation +extern void PrepareBitSetModule(); +extern int WriteResultIntoFile( char * pFileName ); + +// iterative ExorLink +extern int IterativelyApplyExorLink2( char fDistEnable ); +extern int IterativelyApplyExorLink3( char fDistEnable ); +extern int IterativelyApplyExorLink4( char fDistEnable ); + +// cube storage allocation/delocation +extern int AllocateCubeSets( int nVarsIn, int nVarsOut ); +extern void DelocateCubeSets(); + +// adjacency queque allocation/delocation procedures +extern int AllocateQueques( int nPlaces ); +extern void DelocateQueques(); + +extern int AllocateCover( int nCubes, int nWordsIn, int nWordsOut ); +extern void DelocateCover(); + +extern void AddToFreeCubes( Cube* pC ); +extern Cube* GetFreeCube(); +// getting and returning free cubes to the heap + +extern void InsertVarsWithoutClearing( Cube * pC, int * pVars, int nVarsIn, int * pVarValues, int Output ); +extern int CheckForCloseCubes( Cube* p, int fAddCube ); + +extern int FindDiffVars( int *pDiffVars, Cube* pC1, Cube* pC2 ); +// determines the variables that are different in cubes pC1 and pC2 +// returns the number of variables + +extern int ComputeQCost( Vec_Int_t * vCube ); +extern int ComputeQCostBits( Cube * p ); + +extern int CountLiterals(); +extern int CountQCost(); + +//////////////////////////////////////////////////////////////////////// +/// VARVALUE and CUBEDIST enum typedefs /// +//////////////////////////////////////////////////////////////////////// + +// literal values +typedef enum { VAR_NEG = 1, VAR_POS, VAR_ABS } varvalue; + +// the flag in some function calls can take one of the follwing values +typedef enum { DIST2, DIST3, DIST4 } cubedist; + +} diff --git a/lib/abcesop/eabc/vecInt.h b/lib/abcesop/eabc/vecInt.h new file mode 100644 index 0000000..1a13e6c --- /dev/null +++ b/lib/abcesop/eabc/vecInt.h @@ -0,0 +1,2070 @@ +/**CFile**************************************************************** + + FileName [vecInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of integers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#pragma once + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +namespace abc::exorcism { + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Int_t_ Vec_Int_t; +struct Vec_Int_t_ +{ + int nCap; + int nSize; + int * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_IntForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStart( vVec, Entry, i, Start ) \ + for ( i = Start; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStop( vVec, Entry, i, Stop ) \ + for ( i = 0; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStartStop( vVec, Entry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_IntEntry(vVec, i)), 1); i-- ) +#define Vec_IntForEachEntryTwo( vVec1, vVec2, Entry1, Entry2, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec1)) && (((Entry1) = Vec_IntEntry(vVec1, i)), 1) && (((Entry2) = Vec_IntEntry(vVec2, i)), 1); i++ ) +#define Vec_IntForEachEntryDouble( vVec, Entry1, Entry2, i ) \ + for ( i = 0; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryDoubleStart( vVec, Entry1, Entry2, i, Start ) \ + for ( i = Start; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryTriple( vVec, Entry1, Entry2, Entry3, i ) \ + for ( i = 0; (i+2 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1) && (((Entry3) = Vec_IntEntry(vVec, i+2)), 1); i += 3 ) +#define Vec_IntForEachEntryThisNext( vVec, This, Next, i ) \ + for ( i = 0, (This) = (Next) = (Vec_IntSize(vVec) ? Vec_IntEntry(vVec, 0) : -1); (i+1 < Vec_IntSize(vVec)) && (((Next) = Vec_IntEntry(vVec, i+1)), 1); i += 2, (This) = (Next) ) +#define Vec_IntForEachEntryInVec( vVec2, vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec2, Vec_IntEntry(vVec, i))), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAlloc( int nCap ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} +static inline Vec_Int_t * Vec_IntAllocExact( int nCap ) +{ + Vec_Int_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStart( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + if ( p->pArray ) memset( p->pArray, 0, sizeof(int) * (size_t)nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartFull( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + if ( p->pArray ) memset( p->pArray, 0xff, sizeof(int) * (size_t)nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartRange( int First, int Range ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( Range ); + p->nSize = Range; + for ( i = 0; i < Range; i++ ) + p->pArray[i] = First + i; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStartNatural( int nSize ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArray( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArrayCopy( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ABC_ALLOC( int, nSize ); + memcpy( p->pArray, pArray, sizeof(int) * (size_t)nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDup( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nSize; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(int) * (size_t)pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDupArray( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntZero( Vec_Int_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntErase( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntFree( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFreeP( Vec_Int_t ** p ) +{ + if ( *p == NULL ) + return; + ABC_FREE( (*p)->pArray ); + ABC_FREE( (*p) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntReleaseArray( Vec_Int_t * p ) +{ + int * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} +static inline int * Vec_IntReleaseNewArray( Vec_Int_t * p ) +{ + int * pArray = ABC_ALLOC( int, p->nSize+1 ); + pArray[0] = p->nSize+1; + memcpy( pArray+1, p->pArray, sizeof(int)*(size_t)p->nSize ); + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntArray( Vec_Int_t * p ) +{ + return p->pArray; +} +static inline int ** Vec_IntArrayP( Vec_Int_t * p ) +{ + return &p->pArray; +} +static inline int * Vec_IntLimit( Vec_Int_t * p ) +{ + return p->pArray + p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSize( Vec_Int_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCap( Vec_Int_t * p ) +{ + return p->nCap; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_IntMemory( Vec_Int_t * p ) +{ + return !p ? 0.0 : 1.0 * sizeof(int) * (size_t)p->nCap + sizeof(Vec_Int_t) ; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntry( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntEntryP( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntWriteEntry( Vec_Int_t * p, int i, int Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntAddToEntry( Vec_Int_t * p, int i, int Addition ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i] += Addition; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntUpdateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) < Value ) + Vec_IntWriteEntry( p, i, Value ); +} +static inline void Vec_IntDowndateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) > Value ) + Vec_IntWriteEntry( p, i, Value ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntryLast( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrow( Vec_Int_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrowResize( Vec_Int_t * p, int nCapMin ) +{ + p->nSize = nCapMin; + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFill( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} +static inline void Vec_IntFillTwo( Vec_Int_t * p, int nSize, int FillEven, int FillOdd ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = (i & 1) ? FillOdd : FillEven; + p->nSize = nSize; +} +static inline void Vec_IntFillNatural( Vec_Int_t * p, int nSize ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFillExtra( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + if ( nSize <= p->nSize ) + return; + if ( nSize > 2 * p->nCap ) + Vec_IntGrow( p, nSize ); + else if ( nSize > p->nCap ) + Vec_IntGrow( p, 2 * p->nCap ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntGetEntry( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntry( p, i ); +} +static inline int Vec_IntGetEntryFull( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + return Vec_IntEntry( p, i ); +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntGetEntryP( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntryP( p, i ); +} + +/**Function************************************************************* + + Synopsis [Inserts the entry even if the place does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSetEntry( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + Vec_IntWriteEntry( p, i, Entry ); +} +static inline void Vec_IntSetEntryFull( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + Vec_IntWriteEntry( p, i, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntShrink( Vec_Int_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntClear( Vec_Int_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPush( Vec_Int_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} +static inline void Vec_IntPushTwo( Vec_Int_t * p, int Entry1, int Entry2 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); +} +static inline void Vec_IntPushThree( Vec_Int_t * p, int Entry1, int Entry2, int Entry3 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); +} +static inline void Vec_IntPushFour( Vec_Int_t * p, int Entry1, int Entry2, int Entry3, int Entry4 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); + Vec_IntPush( p, Entry4 ); +} +static inline void Vec_IntPushArray( Vec_Int_t * p, int * pEntries, int nEntries ) +{ + int i; + for ( i = 0; i < nEntries; i++ ) + Vec_IntPush( p, pEntries[i] ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushFirst( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize - 1; i >= 1; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[0] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrder( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] > Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} +static inline void Vec_IntPushOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( Vec_IntEntry(vCost, p->pArray[i]) > Vec_IntEntry(vCost, Entry) ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrderReverse( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] < Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUniqueOrder( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrder( p, Entry ); + return 0; +} +static inline int Vec_IntPushUniqueOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrderCost( p, Entry, vCost ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUnique( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the next nWords entries in the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned * Vec_IntFetch( Vec_Int_t * p, int nWords ) +{ + if ( nWords == 0 ) + return NULL; + assert( nWords > 0 ); + p->nSize += nWords; + if ( p->nSize > p->nCap ) + { +// Vec_IntGrow( p, 2 * p->nSize ); + return NULL; + } + return ((unsigned *)p->pArray) + p->nSize - nWords; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPop( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFind( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntRemove( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i == p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} +static inline int Vec_IntRemove1( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 1; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i >= p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntDrop( Vec_Int_t * p, int i ) +{ + int k; + assert( i >= 0 && i < Vec_IntSize(p) ); + p->nSize--; + for ( k = i; k < p->nSize; k++ ) + p->pArray[k] = p->pArray[k+1]; +} + +/**Function************************************************************* + + Synopsis [Interts entry at the index iHere. Shifts other entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntInsert( Vec_Int_t * p, int iHere, int Entry ) +{ + int i; + assert( iHere >= 0 && iHere <= p->nSize ); + Vec_IntPush( p, 0 ); + for ( i = p->nSize - 1; i > iHere; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMax( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best < p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMin( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best > p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Reverses the order of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntReverseOrder( Vec_Int_t * p ) +{ + int i, Temp; + for ( i = 0; i < p->nSize/2; i++ ) + { + Temp = p->pArray[i]; + p->pArray[i] = p->pArray[p->nSize-1-i]; + p->pArray[p->nSize-1-i] = Temp; + } +} + +/**Function************************************************************* + + Synopsis [Removes odd entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemoveOdd( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i]; +} +static inline void Vec_IntRemoveEven( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i+1]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntInvert( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( 0 ); + if ( Vec_IntSize(p) == 0 ) + return vRes; + Vec_IntFill( vRes, Vec_IntFindMax(p) + 1, Fill ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntWriteEntry( vRes, Entry, i ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntCondense( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( Vec_IntSize(p) ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntPush( vRes, Entry ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSum( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += p->pArray[i]; + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountEntry( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == Entry); + return Counter; +} +static inline int Vec_IntCountLarger( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > Entry); + return Counter; +} +static inline int Vec_IntCountSmaller( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] < Entry); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountPositive( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > 0); + return Counter; +} +static inline int Vec_IntCountZero( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Checks if two vectors are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEqual( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + int i; + if ( p1->nSize != p2->nSize ) + return 0; + for ( i = 0; i < p1->nSize; i++ ) + if ( p1->pArray[i] != p2->pArray[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Counts the number of common entries.] + + Description [Assumes that the entries are non-negative integers that + are not very large, so inversion of the array can be performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountCommon( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + Vec_Int_t * vTemp; + int Entry, i, Counter = 0; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + vTemp = p1, p1 = p2, p2 = vTemp; + assert( Vec_IntSize(p1) >= Vec_IntSize(p2) ); + vTemp = Vec_IntInvert( p2, -1 ); + Vec_IntFillExtra( vTemp, Vec_IntFindMax(p1) + 1, -1 ); + Vec_IntForEachEntry( p1, Entry, i ) + if ( Vec_IntEntry(vTemp, Entry) >= 0 ) + Counter++; + Vec_IntFree( vTemp ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare1( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare2( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSort( Vec_Int_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} +static inline void Vec_IntSortMulti( Vec_Int_t * p, int nMulti, int fReverse ) +{ + assert( Vec_IntSize(p) % nMulti == 0 ); + if ( fReverse ) + qsort( (void *)p->pArray, (size_t)(p->nSize/nMulti), nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, (size_t)(p->nSize/nMulti), nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} +static inline int Vec_IntIsSorted( Vec_Int_t * p, int fReverse ) +{ + int i; + for ( i = 1; i < p->nSize; i++ ) + if ( fReverse ? (p->pArray[i-1] < p->pArray[i]) : (p->pArray[i-1] > p->pArray[i]) ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Leaves only unique entries.] + + Description [Returns the number of duplicated entried found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqify( Vec_Int_t * p ) +{ + int i, k, RetValue; + if ( p->nSize < 2 ) + return 0; + Vec_IntSort( p, 0 ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( p->pArray[i] != p->pArray[i-1] ) + p->pArray[k++] = p->pArray[i]; + RetValue = p->nSize - k; + p->nSize = k; + return RetValue; +} +static inline int Vec_IntCountDuplicates( Vec_Int_t * p ) +{ + int RetValue; + Vec_Int_t * pDup = Vec_IntDup( p ); + Vec_IntUniqify( pDup ); + RetValue = Vec_IntSize(p) - Vec_IntSize(pDup); + Vec_IntFree( pDup ); + return RetValue; +} +static inline int Vec_IntCheckUniqueSmall( Vec_Int_t * p ) +{ + int i, k; + for ( i = 0; i < p->nSize; i++ ) + for ( k = i+1; k < p->nSize; k++ ) + if ( p->pArray[i] == p->pArray[k] ) + return 0; + return 1; +} +static inline int Vec_IntCountUnique( Vec_Int_t * p ) +{ + int i, Count = 0, Max = Vec_IntFindMax(p); + unsigned char * pPres = ABC_CALLOC( unsigned char, Max+1 ); + for ( i = 0; i < p->nSize; i++ ) + if ( pPres[p->pArray[i]] == 0 ) + pPres[p->pArray[i]] = 1, Count++; + ABC_FREE( pPres ); + return Count; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqifyPairs( Vec_Int_t * p ) +{ + int i, k, RetValue; + assert( p->nSize % 2 == 0 ); + if ( p->nSize < 4 ) + return 0; + Vec_IntSortMulti( p, 2, 0 ); + for ( i = k = 1; i < p->nSize/2; i++ ) + if ( p->pArray[2*i] != p->pArray[2*(i-1)] || p->pArray[2*i+1] != p->pArray[2*(i-1)+1] ) + { + p->pArray[2*k] = p->pArray[2*i]; + p->pArray[2*k+1] = p->pArray[2*i+1]; + k++; + } + RetValue = p->nSize/2 - k; + p->nSize = 2*k; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Vec_IntUniqueHashKeyDebug( unsigned char * pStr, int nChars, int TableMask ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + { + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + printf( "%d : ", c ); + printf( "%3d ", pStr[c] ); + printf( "%12u ", Key ); + printf( "%12u ", Key&TableMask ); + printf( "\n" ); + } + return Key; +} + +static inline unsigned Vec_IntUniqueHashKey2( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + return Key; +} + +static inline unsigned Vec_IntUniqueHashKey( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[16] = + { + 0x984b6ad9,0x18a6eed3,0x950353e2,0x6222f6eb,0xdfbedd47,0xef0f9023,0xac932a26,0x590eaf55, + 0x97d0a034,0xdc36cd2e,0x22736b37,0xdc9066b0,0x2eb2f98b,0x5d9c7baf,0x85747c9e,0x8aca1055 + }; + static unsigned s_BigPrimes2[16] = + { + 0x8d8a5ebe,0x1e6a15dc,0x197d49db,0x5bab9c89,0x4b55dea7,0x55dede49,0x9a6a8080,0xe5e51035, + 0xe148d658,0x8a17eb3b,0xe22e4b38,0xe5be2a9a,0xbe938cbb,0x3b981069,0x7f9c0c8e,0xf756df10 + }; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += s_BigPrimes2[(2*c)&15] * s_BigPrimes[(unsigned)pStr[c] & 15] + + s_BigPrimes2[(2*c+1)&15] * s_BigPrimes[(unsigned)pStr[c] >> 4]; + return Key; +} +static inline int * Vec_IntUniqueLookup( Vec_Int_t * vData, int i, int nIntSize, int * pNexts, int * pStart ) +{ + int * pData = Vec_IntEntryP( vData, i*nIntSize ); + for ( ; *pStart != -1; pStart = pNexts + *pStart ) + if ( !memcmp( pData, Vec_IntEntryP(vData, *pStart*nIntSize), sizeof(int) * (size_t)nIntSize ) ) + return pStart; + return pStart; +} +static inline int Vec_IntUniqueCount( Vec_Int_t * vData, int nIntSize, Vec_Int_t ** pvMap ) +{ + int nEntries = Vec_IntSize(vData) / nIntSize; + int TableMask = (1 << Abc_Base2Log(nEntries)) - 1; + int * pTable = ABC_FALLOC( int, TableMask+1 ); + int * pNexts = ABC_FALLOC( int, TableMask+1 ); + int * pClass = ABC_ALLOC( int, nEntries ); + int i, Key, * pEnt, nUnique = 0; + assert( nEntries * nIntSize == Vec_IntSize(vData) ); + for ( i = 0; i < nEntries; i++ ) + { + pEnt = Vec_IntEntryP( vData, i*nIntSize ); + Key = TableMask & Vec_IntUniqueHashKey( (unsigned char *)pEnt, 4*nIntSize ); + pEnt = Vec_IntUniqueLookup( vData, i, nIntSize, pNexts, pTable+Key ); + if ( *pEnt == -1 ) + *pEnt = i, nUnique++; + pClass[i] = *pEnt; + } +// Vec_IntUniqueProfile( vData, pTable, pNexts, TableMask, nIntSize ); + ABC_FREE( pTable ); + ABC_FREE( pNexts ); + if ( pvMap ) + *pvMap = Vec_IntAllocArray( pClass, nEntries ); + else + ABC_FREE( pClass ); + return nUnique; +} +static inline Vec_Int_t * Vec_IntUniqifyHash( Vec_Int_t * vData, int nIntSize ) +{ + Vec_Int_t * vMap, * vUnique; + int i, Ent, nUnique = Vec_IntUniqueCount( vData, nIntSize, &vMap ); + vUnique = Vec_IntAlloc( nUnique * nIntSize ); + Vec_IntForEachEntry( vMap, Ent, i ) + { + if ( Ent < i ) continue; + assert( Ent == i ); + Vec_IntPushArray( vUnique, Vec_IntEntryP(vData, i*nIntSize), nIntSize ); + } + assert( Vec_IntSize(vUnique) == nUnique * nIntSize ); + Vec_IntFree( vMap ); + return vUnique; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompareUnsigned( unsigned * pp1, unsigned * pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSortUnsigned( Vec_Int_t * p ) +{ + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompareUnsigned ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoCountCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int Counter = 0; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++, Counter++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Collects common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoFindCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} +static inline int Vec_IntTwoFindCommonReverse( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 > *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Collects and removes common entries] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemoveCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + int * pBeg2New = vArr2->pArray; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + *pBeg2New++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg2New++ = *pBeg2++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + Vec_IntShrink( vArr2, pBeg2New - vArr2->pArray ); + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Removes entries of the second one from the first one.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemove( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + return Vec_IntSize(vArr1); +} + +/**Function************************************************************* + + Synopsis [Returns the result of merging the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoMerge2Int( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg = vArr->pArray; + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + *pBeg++ = *pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg++ = *pBeg1++; + else + *pBeg++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg++ = *pBeg2++; + vArr->nSize = pBeg - vArr->pArray; + assert( vArr->nSize <= vArr->nCap ); + assert( vArr->nSize >= vArr1->nSize ); + assert( vArr->nSize >= vArr2->nSize ); +} +static inline Vec_Int_t * Vec_IntTwoMerge( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + Vec_Int_t * vArr = Vec_IntAlloc( vArr1->nSize + vArr2->nSize ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); + return vArr; +} +static inline void Vec_IntTwoMerge2( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + Vec_IntGrow( vArr, Vec_IntSize(vArr1) + Vec_IntSize(vArr2) ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); +} + +/**Function************************************************************* + + Synopsis [Returns the result of splitting of the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoSplit( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr, Vec_Int_t * vArr1n, Vec_Int_t * vArr2n ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1++ ), pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + else + Vec_IntPush( vArr2n, *pBeg2++ ); + } + while ( pBeg1 < pEnd1 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + while ( pBeg2 < pEnd2 ) + Vec_IntPush( vArr2n, *pBeg2++ ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSort( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] < pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} +static inline void Vec_IntSelectSortReverse( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] > pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSortCost( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) < Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} +static inline void Vec_IntSelectSortCostReverse( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) > Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} + +static inline void Vec_IntSelectSortCost2( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] < pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} +static inline void Vec_IntSelectSortCost2Reverse( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] > pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPrint( Vec_Int_t * vVec ) +{ + int i, Entry; + printf( "Vector has %d entries: {", Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, Entry, i ) + printf( " %d", Entry ); + printf( " }\n" ); +} +static inline void Vec_IntPrintBinary( Vec_Int_t * vVec ) +{ + int i, Entry; + Vec_IntForEachEntry( vVec, Entry, i ) + printf( "%d", (int)(Entry != 0) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCompareVec( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( p1 == NULL || p2 == NULL ) + return (p1 != NULL) - (p2 != NULL); + if ( Vec_IntSize(p1) != Vec_IntSize(p2) ) + return Vec_IntSize(p1) - Vec_IntSize(p2); + return memcmp( Vec_IntArray(p1), Vec_IntArray(p2), sizeof(int)*(size_t)Vec_IntSize(p1) ); +} + +/**Function************************************************************* + + Synopsis [Appends the contents of the second vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntAppend( Vec_Int_t * vVec1, Vec_Int_t * vVec2 ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendSkip( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int iVar ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + if ( i != iVar ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendMinus( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int fMinus ) +{ + int Entry, i; + Vec_IntClear( vVec1 ); + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, fMinus ? -Entry : Entry ); +} + +/**Function************************************************************* + + Synopsis [Remapping attributes after objects were duplicated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemapArray( Vec_Int_t * vOld2New, Vec_Int_t * vOld, Vec_Int_t * vNew, int nNew ) +{ + int iOld, iNew; + if ( Vec_IntSize(vOld) == 0 ) + return; + Vec_IntFill( vNew, nNew, 0 ); + Vec_IntForEachEntry( vOld2New, iNew, iOld ) + if ( iNew > 0 && iNew < nNew && iOld < Vec_IntSize(vOld) && Vec_IntEntry(vOld, iOld) != 0 ) + Vec_IntWriteEntry( vNew, iNew, Vec_IntEntry(vOld, iOld) ); +} + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcesop/eabc/vecPtr.h b/lib/abcesop/eabc/vecPtr.h new file mode 100644 index 0000000..9093901 --- /dev/null +++ b/lib/abcesop/eabc/vecPtr.h @@ -0,0 +1,1170 @@ +/**CFile**************************************************************** + + FileName [vecPtr.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of generic pointers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecPtr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#pragma once + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +namespace abc::exorcism { + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Ptr_t_ Vec_Ptr_t; +struct Vec_Ptr_t_ +{ + int nCap; + int nSize; + void ** pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through entries +#define Vec_PtrForEachEntry( Type, vVec, pEntry, i ) \ + for ( i = 0; (i < Vec_PtrSize(vVec)) && (((pEntry) = (Type)Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStart( Type, vVec, pEntry, i, Start ) \ + for ( i = Start; (i < Vec_PtrSize(vVec)) && (((pEntry) = (Type)Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStop( Type, vVec, pEntry, i, Stop ) \ + for ( i = 0; (i < Stop) && (((pEntry) = (Type)Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryStartStop( Type, vVec, pEntry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((pEntry) = (Type)Vec_PtrEntry(vVec, i)), 1); i++ ) +#define Vec_PtrForEachEntryReverse( Type, vVec, pEntry, i ) \ + for ( i = Vec_PtrSize(vVec) - 1; (i >= 0) && (((pEntry) = (Type)Vec_PtrEntry(vVec, i)), 1); i-- ) +#define Vec_PtrForEachEntryTwo( Type1, vVec1, Type2, vVec2, pEntry1, pEntry2, i ) \ + for ( i = 0; (i < Vec_PtrSize(vVec1)) && (((pEntry1) = (Type1)Vec_PtrEntry(vVec1, i)), 1) && (((pEntry2) = (Type2)Vec_PtrEntry(vVec2, i)), 1); i++ ) +#define Vec_PtrForEachEntryDouble( Type1, Type2, vVec, Entry1, Entry2, i ) \ + for ( i = 0; (i+1 < Vec_PtrSize(vVec)) && (((Entry1) = (Type1)Vec_PtrEntry(vVec, i)), 1) && (((Entry2) = (Type2)Vec_PtrEntry(vVec, i+1)), 1); i += 2 ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAlloc( int nCap ) +{ + Vec_Ptr_t * p; + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( void *, p->nCap ) : NULL; + return p; +} +static inline Vec_Ptr_t * Vec_PtrAllocExact( int nCap ) +{ + Vec_Ptr_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( void *, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrStart( int nSize ) +{ + Vec_Ptr_t * p; + p = Vec_PtrAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(void *) * (size_t)nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocArray( void ** pArray, int nSize ) +{ + Vec_Ptr_t * p; + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocArrayCopy( void ** pArray, int nSize ) +{ + Vec_Ptr_t * p; + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ABC_ALLOC( void *, nSize ); + memcpy( p->pArray, pArray, sizeof(void *) * (size_t)nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrDup( Vec_Ptr_t * pVec ) +{ + Vec_Ptr_t * p; + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = p->nCap? ABC_ALLOC( void *, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(void *) * (size_t)pVec->nSize ); + return p; +} +static inline Vec_Ptr_t * Vec_PtrDupStr( Vec_Ptr_t * pVec ) +{ + int i; + Vec_Ptr_t * p = Vec_PtrDup( pVec ); + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = Abc_UtilStrsav( (char *)p->pArray[i] ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrDupArray( Vec_Ptr_t * pVec ) +{ + Vec_Ptr_t * p; + p = ABC_ALLOC( Vec_Ptr_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrZero( Vec_Ptr_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_PtrErase( Vec_Ptr_t * p ) +{ + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_PtrFree( Vec_Ptr_t * p ) +{ + ABC_FREE( p->pArray ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFreeP( Vec_Ptr_t ** p ) +{ + if ( *p == NULL ) + return; + ABC_FREE( (*p)->pArray ); + ABC_FREE( (*p) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrReleaseArray( Vec_Ptr_t * p ) +{ + void ** pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrArray( Vec_Ptr_t * p ) +{ + return p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrSize( Vec_Ptr_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrCap( Vec_Ptr_t * p ) +{ + return p->nCap; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_PtrMemory( Vec_Ptr_t * p ) +{ + return !p ? 0.0 : 1.0 * sizeof(void *) * (size_t)p->nCap + sizeof(Vec_Ptr_t); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrCountZero( Vec_Ptr_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == NULL); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrEntry( Vec_Ptr_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void ** Vec_PtrEntryP( Vec_Ptr_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrWriteEntry( Vec_Ptr_t * p, int i, void * Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrEntryLast( Vec_Ptr_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrGrow( Vec_Ptr_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( void *, p->pArray, nCapMin ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFill( Vec_Ptr_t * p, int nSize, void * Entry ) +{ + int i; + Vec_PtrGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Entry; + p->nSize = nSize; +} +static inline void Vec_PtrFillTwo( Vec_Ptr_t * p, int nSize, void * EntryEven, void * EntryOdd ) +{ + int i; + Vec_PtrGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = (i & 1) ? EntryOdd : EntryEven; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFillExtra( Vec_Ptr_t * p, int nSize, void * Fill ) +{ + int i; + if ( nSize <= p->nSize ) + return; + if ( nSize > 2 * p->nCap ) + Vec_PtrGrow( p, nSize ); + else if ( nSize > p->nCap ) + Vec_PtrGrow( p, 2 * p->nCap ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrGetEntry( Vec_Ptr_t * p, int i ) +{ + Vec_PtrFillExtra( p, i + 1, NULL ); + return Vec_PtrEntry( p, i ); +} + +/**Function************************************************************* + + Synopsis [Inserts the entry even if the place does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrSetEntry( Vec_Ptr_t * p, int i, void * Entry ) +{ + Vec_PtrFillExtra( p, i + 1, NULL ); + Vec_PtrWriteEntry( p, i, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrShrink( Vec_Ptr_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrClear( Vec_Ptr_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [Deallocates array of memory pointers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFreeData( Vec_Ptr_t * p ) +{ + void * pTemp; int i; + if ( p == NULL ) return; + Vec_PtrForEachEntry( void *, p, pTemp, i ) + if ( pTemp != (void *)(ABC_PTRINT_T)1 && pTemp != (void *)(ABC_PTRINT_T)2 ) + ABC_FREE( pTemp ); +} +static inline void Vec_PtrFreeFree( Vec_Ptr_t * p ) +{ + if ( p == NULL ) return; + Vec_PtrFreeData( p ); + Vec_PtrFree( p ); +} + +/**Function************************************************************* + + Synopsis [Copies the interger array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrCopy( Vec_Ptr_t * pDest, Vec_Ptr_t * pSour ) +{ + pDest->nSize = 0; + Vec_PtrGrow( pDest, pSour->nSize ); + memcpy( pDest->pArray, pSour->pArray, sizeof(void *) * (size_t)pSour->nSize ); + pDest->nSize = pSour->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrPush( Vec_Ptr_t * p, void * Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_PtrGrow( p, 16 ); + else + Vec_PtrGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} +static inline void Vec_PtrPushTwo( Vec_Ptr_t * p, void * Entry1, void * Entry2 ) +{ + Vec_PtrPush( p, Entry1 ); + Vec_PtrPush( p, Entry2 ); +} +static inline void Vec_PtrAppend( Vec_Ptr_t * vVec1, Vec_Ptr_t * vVec2 ) +{ + void * Entry; int i; + Vec_PtrForEachEntry( void *, vVec2, Entry, i ) + Vec_PtrPush( vVec1, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrPushFirst( Vec_Ptr_t * p, void * Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_PtrGrow( p, 16 ); + else + Vec_PtrGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize - 1; i >= 1; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[0] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrPushUnique( Vec_Ptr_t * p, void * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_PtrPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void * Vec_PtrPop( Vec_Ptr_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrFind( Vec_Ptr_t * p, void * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} +static inline int Vec_PtrFindStr( Vec_Ptr_t * p, char * Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] && !strcmp((char *)p->pArray[i], Entry) ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrRemove( Vec_Ptr_t * p, void * Entry ) +{ + int i; + // delete assuming that it is closer to the end + for ( i = p->nSize - 1; i >= 0; i-- ) + if ( p->pArray[i] == Entry ) + break; + assert( i >= 0 ); +/* + // delete assuming that it is closer to the beginning + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + assert( i < p->nSize ); +*/ + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrDrop( Vec_Ptr_t * p, int i ) +{ + int k; + assert( i >= 0 && i < Vec_PtrSize(p) ); + p->nSize--; + for ( k = i; k < p->nSize; k++ ) + p->pArray[k] = p->pArray[k+1]; +} + +/**Function************************************************************* + + Synopsis [Interts entry at the index iHere. Shifts other entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrInsert( Vec_Ptr_t * p, int iHere, void * Entry ) +{ + int i; + assert( iHere >= 0 && iHere < p->nSize ); + Vec_PtrPush( p, 0 ); + for ( i = p->nSize - 1; i > iHere; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [Moves the first nItems to the end.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrReorder( Vec_Ptr_t * p, int nItems ) +{ + assert( nItems < p->nSize ); + Vec_PtrGrow( p, nItems + p->nSize ); + memmove( (char **)p->pArray + p->nSize, p->pArray, (size_t)nItems * sizeof(void*) ); + memmove( p->pArray, (char **)p->pArray + nItems, (size_t)p->nSize * sizeof(void*) ); +} + +/**Function************************************************************* + + Synopsis [Reverses the order of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrReverseOrder( Vec_Ptr_t * p ) +{ + void * Temp; + int i; + for ( i = 0; i < p->nSize/2; i++ ) + { + Temp = p->pArray[i]; + p->pArray[i] = p->pArray[p->nSize-1-i]; + p->pArray[p->nSize-1-i] = Temp; + } +} + +/**Function************************************************************* + + Synopsis [Checks if two vectors are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrEqual( Vec_Ptr_t * p1, Vec_Ptr_t * p2 ) +{ + int i; + if ( p1->nSize != p2->nSize ) + return 0; + for ( i = 0; i < p1->nSize; i++ ) + if ( p1->pArray[i] != p2->pArray[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_PtrSortComparePtr( void ** pp1, void ** pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Vec_PtrSort( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) ___unused; +static void Vec_PtrSort( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) +{ + if ( p->nSize < 2 ) + return; + if ( Vec_PtrSortCompare == NULL ) + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_PtrSortComparePtr ); + else + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(void *), + (int (*)(const void *, const void *)) Vec_PtrSortCompare ); +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void Vec_PtrUniqify( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) ___unused; +static void Vec_PtrUniqify( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)() ) +{ + int i, k; + if ( p->nSize < 2 ) + return; + Vec_PtrSort( p, Vec_PtrSortCompare ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( p->pArray[i] != p->pArray[i-1] ) + p->pArray[k++] = p->pArray[i]; + p->nSize = k; +} +static void Vec_PtrUniqify2( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)(void**, void**), void (*Vec_PtrObjFree)(void*), Vec_Int_t * vCounts ) ___unused; +static void Vec_PtrUniqify2( Vec_Ptr_t * p, int (*Vec_PtrSortCompare)(void**, void**), void (*Vec_PtrObjFree)(void*), Vec_Int_t * vCounts ) +{ + int i, k; + if ( vCounts ) + Vec_IntFill( vCounts, 1, 1 ); + if ( p->nSize < 2 ) + return; + Vec_PtrSort( p, (int (*)())Vec_PtrSortCompare ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( Vec_PtrSortCompare(p->pArray+i, p->pArray+k-1) != 0 ) + { + p->pArray[k++] = p->pArray[i]; + if ( vCounts ) + Vec_IntPush( vCounts, 1 ); + } + else + { + if ( Vec_PtrObjFree ) + Vec_PtrObjFree( p->pArray[i] ); + if ( vCounts ) + Vec_IntAddToEntry( vCounts, Vec_IntSize(vCounts)-1, 1 ); + } + p->nSize = k; + assert( vCounts == NULL || Vec_IntSize(vCounts) == Vec_PtrSize(p) ); +} + + + +/**Function************************************************************* + + Synopsis [Allocates the array of simulation info.] + + Description [Allocates the array containing given number of entries, + each of which contains given number of unsigned words of simulation data. + The resulting array can be freed using regular procedure Vec_PtrFree(). + It is the responsibility of the user to ensure this array is never grown.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocSimInfo( int nEntries, int nWords ) +{ + void ** pMemory; + unsigned * pInfo; + int i; + pMemory = (void **)ABC_ALLOC( char, (sizeof(void *) + sizeof(unsigned) * (size_t)nWords) * nEntries ); + pInfo = (unsigned *)(pMemory + nEntries); + for ( i = 0; i < nEntries; i++ ) + pMemory[i] = pInfo + i * nWords; + return Vec_PtrAllocArray( pMemory, nEntries ); +} + +/**Function************************************************************* + + Synopsis [Cleans simulation info of each entry beginning with iWord.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_PtrReadWordsSimInfo( Vec_Ptr_t * p ) +{ + return (unsigned *)Vec_PtrEntry(p,1) - (unsigned *)Vec_PtrEntry(p,0); +} + +/**Function************************************************************* + + Synopsis [Cleans simulation info of each entry beginning with iWord.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrCleanSimInfo( Vec_Ptr_t * vInfo, int iWord, int nWords ) +{ + int i; + for ( i = 0; i < vInfo->nSize; i++ ) + memset( (char*)Vec_PtrEntry(vInfo,i) + 4*iWord, 0, (size_t)(4*(nWords-iWord)) ); +} + +/**Function************************************************************* + + Synopsis [Cleans simulation info of each entry beginning with iWord.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrFillSimInfo( Vec_Ptr_t * vInfo, int iWord, int nWords ) +{ + int i; + for ( i = 0; i < vInfo->nSize; i++ ) + memset( (char*)Vec_PtrEntry(vInfo,i) + 4*iWord, 0xFF, (size_t)(4*(nWords-iWord)) ); +} + +/**Function************************************************************* + + Synopsis [Resizes the array of simulation info.] + + Description [Doubles the number of objects for which siminfo is allocated.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrDoubleSimInfo( Vec_Ptr_t * vInfo ) +{ + Vec_Ptr_t * vInfoNew; + int nWords; + assert( Vec_PtrSize(vInfo) > 1 ); + // get the new array + nWords = (unsigned *)Vec_PtrEntry(vInfo,1) - (unsigned *)Vec_PtrEntry(vInfo,0); + vInfoNew = Vec_PtrAllocSimInfo( 2*Vec_PtrSize(vInfo), nWords ); + // copy the simulation info + memcpy( Vec_PtrEntry(vInfoNew,0), Vec_PtrEntry(vInfo,0), (size_t)(Vec_PtrSize(vInfo) * nWords * 4) ); + // replace the array + ABC_FREE( vInfo->pArray ); + vInfo->pArray = vInfoNew->pArray; + vInfo->nSize *= 2; + vInfo->nCap *= 2; + // free the old array + vInfoNew->pArray = NULL; + ABC_FREE( vInfoNew ); +} + +/**Function************************************************************* + + Synopsis [Resizes the array of simulation info.] + + Description [Doubles the number of simulation patterns stored for each object.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_PtrReallocSimInfo( Vec_Ptr_t * vInfo ) +{ + Vec_Ptr_t * vInfoNew; + int nWords, i; + assert( Vec_PtrSize(vInfo) > 1 ); + // get the new array + nWords = (unsigned *)Vec_PtrEntry(vInfo,1) - (unsigned *)Vec_PtrEntry(vInfo,0); + vInfoNew = Vec_PtrAllocSimInfo( Vec_PtrSize(vInfo), 2*nWords ); + // copy the simulation info + for ( i = 0; i < vInfo->nSize; i++ ) + memcpy( Vec_PtrEntry(vInfoNew,i), Vec_PtrEntry(vInfo,i), (size_t)(nWords * 4) ); + // replace the array + ABC_FREE( vInfo->pArray ); + vInfo->pArray = vInfoNew->pArray; + // free the old array + vInfoNew->pArray = NULL; + ABC_FREE( vInfoNew ); +} + +/**Function************************************************************* + + Synopsis [Allocates the array of truth tables for the given number of vars.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_PtrAllocTruthTables( int nVars ) +{ + Vec_Ptr_t * p; + unsigned Masks[5] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned * pTruth; + int i, k, nWords; + nWords = (nVars <= 5 ? 1 : (1 << (nVars - 5))); + p = Vec_PtrAllocSimInfo( nVars, nWords ); + for ( i = 0; i < nVars; i++ ) + { + pTruth = (unsigned *)p->pArray[i]; + if ( i < 5 ) + { + for ( k = 0; k < nWords; k++ ) + pTruth[k] = Masks[i]; + } + else + { + for ( k = 0; k < nWords; k++ ) + if ( k & (1 << (i-5)) ) + pTruth[k] = ~(unsigned)0; + else + pTruth[k] = 0; + } + } + return p; +} + + + +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcesop/eabc/vecWec.h b/lib/abcesop/eabc/vecWec.h new file mode 100644 index 0000000..e630477 --- /dev/null +++ b/lib/abcesop/eabc/vecWec.h @@ -0,0 +1,739 @@ +/**CFile**************************************************************** + + FileName [vecWec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable vector of resizable vectors.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecWec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#pragma once + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +namespace abc::exorcism { + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Wec_t_ Vec_Wec_t; +struct Vec_Wec_t_ +{ + int nCap; + int nSize; + Vec_Int_t * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through levels +#define Vec_WecForEachLevel( vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelVec( vLevels, vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_IntSize(vLevels)) && (((vVec) = Vec_WecEntry(vGlob, Vec_IntEntry(vLevels, i))), 1); i++ ) +#define Vec_WecForEachLevelStart( vGlob, vVec, i, LevelStart ) \ + for ( i = LevelStart; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStop( vGlob, vVec, i, LevelStop ) \ + for ( i = 0; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelReverse( vGlob, vVec, i ) \ + for ( i = Vec_WecSize(vGlob)-1; (i >= 0) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelReverseStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart-1; (i >= LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelTwo( vGlob1, vGlob2, vVec1, vVec2, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob1)) && (((vVec1) = Vec_WecEntry(vGlob1, i)), 1) && (((vVec2) = Vec_WecEntry(vGlob2, i)), 1); i++ ) +#define Vec_WecForEachLevelDouble( vGlob, vVec1, vVec2, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob)) && (((vVec1) = Vec_WecEntry(vGlob, i)), 1) && (((vVec2) = Vec_WecEntry(vGlob, i+1)), 1); i += 2 ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecAlloc( int nCap ) +{ + Vec_Wec_t * p; + p = ABC_ALLOC( Vec_Wec_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecAllocExact( int nCap ) +{ + Vec_Wec_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Wec_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecStart( int nSize ) +{ + Vec_Wec_t * p; + p = Vec_WecAlloc( nSize ); + p->nSize = nSize; + return p; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecGrow( Vec_Wec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( Vec_Int_t, p->pArray, nCapMin ); + memset( p->pArray + p->nCap, 0, sizeof(Vec_Int_t) * (size_t)(nCapMin - p->nCap) ); + p->nCap = nCapMin; +} +static inline void Vec_WecInit( Vec_Wec_t * p, int nSize ) +{ + Vec_WecGrow( p, nSize ); + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecEntry( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} +static inline Vec_Int_t * Vec_WecEntryLast( Vec_Wec_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray + p->nSize - 1; +} +static inline int Vec_WecEntryEntry( Vec_Wec_t * p, int i, int k ) +{ + return Vec_IntEntry( Vec_WecEntry(p, i), k ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecArray( Vec_Wec_t * p ) +{ + return p->pArray; +} +static inline int Vec_WecLevelId( Vec_Wec_t * p, Vec_Int_t * vLevel ) +{ + assert( p->pArray <= vLevel && vLevel < p->pArray + p->nSize ); + return vLevel - p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCap( Vec_Wec_t * p ) +{ + return p->nCap; +} +static inline int Vec_WecSize( Vec_Wec_t * p ) +{ + return p->nSize; +} +static inline int Vec_WecLevelSize( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return Vec_IntSize( p->pArray + i ); +} +static inline int Vec_WecSizeSize( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += Vec_IntSize(vVec); + return Counter; +} +static inline int Vec_WecSizeUsed( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} +static inline int Vec_WecSizeUsedLimits( Vec_Wec_t * p, int iStart, int iStop ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevelStartStop( p, vVec, i, iStart, iStop ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecShrink( Vec_Wec_t * p, int nSizeNew ) +{ + Vec_Int_t * vVec; int i; + Vec_WecForEachLevelStart( p, vVec, i, nSizeNew ) + Vec_IntShrink( vVec, 0 ); + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecClear( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i; + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntClear( vVec ); + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPush( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + { + Vec_WecGrow( p, Abc_MaxInt(2*p->nSize, Level + 1) ); + p->nSize = Level + 1; + } + Vec_IntPush( Vec_WecEntry(p, Level), Entry ); +} +static inline Vec_Int_t * Vec_WecPushLevel( Vec_Wec_t * p ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + return Vec_WecEntryLast( p ); +} +static inline Vec_Int_t * Vec_WecInsertLevel( Vec_Wec_t * p, int i ) +{ + Vec_Int_t * pTemp; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + assert( i >= 0 && i < p->nSize ); + for ( pTemp = p->pArray + p->nSize - 2; pTemp >= p->pArray + i; pTemp-- ) + pTemp[1] = pTemp[0]; + Vec_IntZero( p->pArray + i ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_WecMemory( Vec_Wec_t * p ) +{ + int i; + double Mem; + if ( p == NULL ) return 0.0; + Mem = sizeof(Vec_Int_t) * Vec_WecCap(p); + for ( i = 0; i < p->nSize; i++ ) + Mem += sizeof(int) * (size_t)Vec_IntCap( Vec_WecEntry(p, i) ); + return Mem; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecZero( Vec_Wec_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecErase( Vec_Wec_t * p ) +{ + int i; + for ( i = 0; i < p->nCap; i++ ) + ABC_FREE( p->pArray[i].pArray ); + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecFree( Vec_Wec_t * p ) +{ + Vec_WecErase( p ); + ABC_FREE( p ); +} +static inline void Vec_WecFreeP( Vec_Wec_t ** p ) +{ + if ( *p == NULL ) + return; + Vec_WecFree( *p ); + *p = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPushUnique( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + Vec_WecPush( p, Level, Entry ); + else + Vec_IntPushUnique( Vec_WecEntry(p, Level), Entry ); +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecDup( Vec_Wec_t * p ) +{ + Vec_Wec_t * vNew; + Vec_Int_t * vVec; + int i, k, Entry; + vNew = Vec_WecAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntForEachEntry( vVec, Entry, k ) + Vec_WecPush( vNew, i, Entry ); + return vNew; +} + +/**Function************************************************************* + + Synopsis [Sorting by array size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare1( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare2( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSort( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare2 ); + else + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare1 ); +} + + +/**Function************************************************************* + + Synopsis [Sorting by the first entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare3( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static int Vec_WecSortCompare4( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static inline void Vec_WecSortByFirstInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare4 ); + else + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare3 ); +} + +/**Function************************************************************* + + Synopsis [Sorting by the last entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare5( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare6( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSortByLastInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare6 ); + else + qsort( (void *)p->pArray, (size_t)p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare5 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPrint( Vec_Wec_t * p, int fSkipSingles ) +{ + Vec_Int_t * vVec; + int i, k, Entry; + Vec_WecForEachLevel( p, vVec, i ) + { + if ( fSkipSingles && Vec_IntSize(vVec) == 1 ) + continue; + printf( " %4d : {", i ); + Vec_IntForEachEntry( vVec, Entry, k ) + printf( " %d", Entry ); + printf( " }\n" ); + } +} +static inline void Vec_WecPrintLits( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, k, iLit; + Vec_WecForEachLevel( p, vVec, i ) + { + printf( " %4d : %2d {", i, Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, iLit, k ) + printf( " %c%d", Abc_LitIsCompl(iLit) ? '-' : '+', Abc_Lit2Var(iLit) ); + printf( " }\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Derives the set of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecCreateClasses( Vec_Int_t * vMap ) +{ + Vec_Wec_t * vClasses; + int i, Entry; + vClasses = Vec_WecStart( Vec_IntFindMax(vMap) + 1 ); + Vec_IntForEachEntry( vMap, Entry, i ) + Vec_WecPush( vClasses, Entry, i ); + return vClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCountNonTrivial( Vec_Wec_t * p, int * pnUsed ) +{ + Vec_Int_t * vClass; + int i, nClasses = 0; + *pnUsed = 0; + Vec_WecForEachLevel( p, vClass, i ) + { + if ( Vec_IntSize(vClass) < 2 ) + continue; + nClasses++; + (*pnUsed) += Vec_IntSize(vClass); + } + return nClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecCollectFirsts( Vec_Wec_t * p ) +{ + Vec_Int_t * vFirsts, * vLevel; + int i; + vFirsts = Vec_IntAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vLevel, i ) + if ( Vec_IntSize(vLevel) > 0 ) + Vec_IntPush( vFirsts, Vec_IntEntry(vLevel, 0) ); + return vFirsts; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Ptr_t * Vec_WecConvertToVecPtr( Vec_Wec_t * p ) +{ + Vec_Ptr_t * vCopy; + Vec_Int_t * vLevel; + int i; + vCopy = Vec_PtrAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vLevel, i ) + Vec_PtrPush( vCopy, Vec_IntDup(vLevel) ); + return vCopy; +} + + +/**Function************************************************************* + + Synopsis [Temporary vector marking.] + + Description [The vector should be static when the marking is used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecIntHasMark( Vec_Int_t * vVec ) { return (vVec->nCap >> 30) & 1; } +static inline void Vec_WecIntSetMark( Vec_Int_t * vVec ) { vVec->nCap |= (1<<30); } +static inline void Vec_WecIntXorMark( Vec_Int_t * vVec ) { vVec->nCap ^= (1<<30); } +static inline void Vec_WecMarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( !Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} +static inline void Vec_WecUnmarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} + +/**Function************************************************************* + + Synopsis [Removes 0-size vectors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecRemoveEmpty( Vec_Wec_t * vCubes ) +{ + Vec_Int_t * vCube; + int i, k = 0; + Vec_WecForEachLevel( vCubes, vCube, i ) + if ( Vec_IntSize(vCube) > 0 ) + vCubes->pArray[k++] = *vCube; + else + ABC_FREE( vCube->pArray ); + for ( i = k; i < Vec_WecSize(vCubes); i++ ) + Vec_IntZero( Vec_WecEntry(vCubes, i) ); + Vec_WecShrink( vCubes, k ); +// Vec_WecSortByFirstInt( vCubes, 0 ); +} + + +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcesop/exor.cpp b/lib/abcesop/exor.cpp new file mode 100644 index 0000000..5c63fd5 --- /dev/null +++ b/lib/abcesop/exor.cpp @@ -0,0 +1,990 @@ +/**CFile**************************************************************** + + FileName [exor.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Main procedure.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exor.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Main Module /// +/// ESOP Minimization Task Coordinator /// +/// /// +/// 1) interprets command line /// +/// 2) calls the approapriate reading procedure /// +/// 3) calls the minimization module /// +/// /// +/// Ver. 1.0. Started - July 18, 2000. Last update - July 20, 2000 /// +/// Ver. 1.1. Started - July 24, 2000. Last update - July 29, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 26, 2000 /// +/// Ver. 1.6. Started - Sep 11, 2000. Last update - Sep 15, 2000 /// +/// Ver. 1.7. Started - Sep 20, 2000. Last update - Sep 23, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +#include +#include +#include + +namespace abc::exorcism { + +/////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// information about the cube cover +cinfo g_CoverInfo; + +extern int s_fDecreaseLiterals; + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION main() /// +//////////////////////////////////////////////////////////////////////// + + +/**Function************************************************************* + + Synopsis [Number of negative literals.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +/* +static int QCost[16][16] = +{ + { 1}, // 0 + { 1, 2}, // 1 + { 5, 5, 6}, // 2 + { 14, 14, 16, 18}, // 3 + { 20, 20, 20, 22, 24}, // 4 + { 32, 32, 32, 34, 36, 38}, // 5 + { 44, 44, 44, 44, 46, 48, 50}, // 6 + { 56, 56, 56, 56, 58, 60, 62, 64}, // 7 + { 0 } +}; +*/ +int GetQCost( int nVars, int nNegs ) +{ + int Extra; + assert( nVars >= nNegs ); + if ( nVars == 0 ) + return 1; + if ( nVars == 1 ) + { + if ( nNegs == 0 ) return 1; + if ( nNegs == 1 ) return 2; + } + if ( nVars == 2 ) + { + if ( nNegs <= 1 ) return 5; + if ( nNegs == 2 ) return 6; + } + if ( nVars == 3 ) + { + if ( nNegs <= 1 ) return 14; + if ( nNegs == 2 ) return 16; + if ( nNegs == 3 ) return 18; + } + Extra = nNegs - nVars/2; + return 20 + 12 * (nVars - 4) + (Extra > 0 ? 2 * Extra : 0); + +} +void GetQCostTest() +{ + int i, k, Limit = 10; + for ( i = 0; i < Limit; i++ ) + { + for ( k = 0; k <= i; k++ ) + printf( "%4d ", GetQCost(i, k) ); + printf( "\n" ); + } +} +int ComputeQCost( Vec_Int_t * vCube ) +{ + int i, Entry, nLitsN = 0; + Vec_IntForEachEntry( vCube, Entry, i ) + nLitsN += Abc_LitIsCompl(Entry); + return GetQCost( Vec_IntSize(vCube), nLitsN ); +} +int ComputeQCostBits( Cube * p ) +{ + extern varvalue GetVar( Cube* pC, int Var ); + int v, nLits = 0, nLitsN = 0; + for ( v = 0; v < g_CoverInfo.nVarsIn; v++ ) + { + int Value = GetVar( p, v ); + if ( Value == VAR_NEG ) + nLitsN++; + else if ( Value == VAR_POS ) + nLits++; + } + nLits += nLitsN; + return GetQCost( nLits, nLitsN ); +} +int ToffoliGateCount( int controls, int lines ) +{ + switch ( controls ) + { + case 0u: + case 1u: + return 0; + break; + case 2u: + return 1; + break; + case 3u: + return 4; + break; + case 4u: + return ( ( ( lines + 1 ) / 2 ) >= controls ) ? 8 : 10; + break; + default: + return ( ( ( lines + 1 ) / 2 ) >= controls ) ? 4 * ( controls - 2 ) : 8 * ( controls - 3 ); + } +} +int ComputeQCostTcount( Vec_Int_t * vCube ) +{ + return 7 * ToffoliGateCount( Vec_IntSize( vCube ), g_CoverInfo.nVarsIn + 1 ); +} +int ComputeQCostTcountBits( Cube * p ) +{ + extern varvalue GetVar( Cube* pC, int Var ); + int v, nLits = 0; + for ( v = 0; v < g_CoverInfo.nVarsIn; v++ ) + if ( GetVar( p, v ) != VAR_ABS ) + nLits++; + return 7 * ToffoliGateCount( nLits, g_CoverInfo.nVarsIn + 1 ); + + /* maybe just: 7 * ToffoliGateCount( p->a, g_CoverInfo.nVarsIn + 1 ); */ +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int ReduceEsopCover() +{ + /////////////////////////////////////////////////////////////// + // SIMPLIFICATION + //////////////////////////////////////////////////////////////////// + + int nIterWithoutImprovement = 0; + int nIterCount = 0; + int GainTotal; + int z; + + do + { +//START: + if ( g_CoverInfo.Verbosity == 2 ) + printf( "\nITERATION #%d\n\n", ++nIterCount ); + else if ( g_CoverInfo.Verbosity == 1 ) + printf( "." ); + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + if ( nIterWithoutImprovement > (int)(g_CoverInfo.Quality>0) ) + { + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|0 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + +// if ( g_CoverInfo.Quality >= 2 && nIterWithoutImprovement == 2 ) +// s_fDecreaseLiterals = 1; + } + while ( nIterWithoutImprovement < 1 + g_CoverInfo.Quality ); + + + // improve the literal count + s_fDecreaseLiterals = 1; + for ( z = 0; z < 1; z++ ) + { + if ( g_CoverInfo.Verbosity == 2 ) + printf( "\nITERATION #%d\n\n", ++nIterCount ); + else if ( g_CoverInfo.Verbosity == 1 ) + printf( "." ); + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + +// if ( GainTotal ) +// { +// nIterWithoutImprovement = 0; +// goto START; +// } + } + + +/* //////////////////////////////////////////////////////////////////// + // Print statistics + printf( "\nShallow simplification time is "; + cout << (float)(clk2 - clk1)/(float)(CLOCKS_PER_SEC) << " sec\n" ); + printf( "Deep simplification time is "; + cout << (float)(Abc_Clock() - clk2)/(float)(CLOCKS_PER_SEC) << " sec\n" ); + printf( "Cover after iterative simplification = " << s_nCubesInUse << endl; + printf( "Reduced by initial cube writing = " << g_CoverInfo.nCubesBefore-nCubesAfterWriting << endl; + printf( "Reduced by shallow simplification = " << nCubesAfterWriting-nCubesAfterShallow << endl; + printf( "Reduced by deep simplification = " << nCubesAfterWriting-s_nCubesInUse << endl; + +// printf( "\nThe total number of cubes created = " << g_CoverInfo.cIDs << endl; +// printf( "Total number of places in a queque = " << s_nPosAlloc << endl; +// printf( "Minimum free places in queque-2 = " << s_nPosMax[0] << endl; +// printf( "Minimum free places in queque-3 = " << s_nPosMax[1] << endl; +// printf( "Minimum free places in queque-4 = " << s_nPosMax[2] << endl; +*/ //////////////////////////////////////////////////////////////////// + + // write the number of cubes into cover information + assert ( g_CoverInfo.nCubesInUse + g_CoverInfo.nCubesFree == g_CoverInfo.nCubesAlloc ); + +// printf( "\nThe output cover is\n" ); +// PrintCoverDebug( cout ); + + return 0; +} + +////////////////////////////////////////////////////////////////// +// quite a good script +////////////////////////////////////////////////////////////////// +/* + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + do + { + PrintQuequeStats(); + GainTotal = 0; + GainTotal += IterativelyApplyExorLink( DIST2, 0|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 0|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + if ( nIterWithoutImprovement > 2 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + } + + if ( nIterWithoutImprovement > 6 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 12 ); + + nCubesAfterShallow = s_nCubesInUse; + +*/ + +/* + // alu4 - 439 + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + do + { + PrintQuequeStats(); + GainTotal = 0; + GainTotal += IterativelyApplyExorLink( DIST2, 1|0|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + if ( nIterWithoutImprovement > 2 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + } + + if ( nIterWithoutImprovement > 6 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 0|0|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 12 ); +*/ + +/* +// alu4 - 412 cubes, 700 sec + + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + int nIterCount = 0; + do + { + printf( "\nITERATION #" << ++nIterCount << endl << endl; + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + if ( nIterWithoutImprovement > 3 ) + { + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + } + + if ( nIterWithoutImprovement > 7 ) + { + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 12 ); +*/ + +/* +// pretty good script +// alu4 = 424 in 250 sec + + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + int nIterCount = 0; + do + { + printf( "\nITERATION #" << ++nIterCount << " |"; + for ( int k = 0; k < nIterWithoutImprovement; k++ ) + printf( "*"; + for ( ; k < 11; k++ ) + printf( "_"; + printf( "|" << endl << endl; + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + if ( nIterWithoutImprovement > 2 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + } + + if ( nIterWithoutImprovement > 4 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 7 ); +*/ + +/* +alu4 = 435 70 secs + + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + int nIterCount = 0; + + do + { + printf( "\nITERATION #" << ++nIterCount << endl << endl; + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 4 ); +*/ + +/* + // the best previous + + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + int nIterCount = 0; + int GainTotal; + do + { + if ( g_CoverInfo.Verbosity == 2 ) + printf( "\nITERATION #" << ++nIterCount << endl << endl; + else if ( g_CoverInfo.Verbosity == 1 ) + cout << '.'; + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + + if ( nIterWithoutImprovement > 1 ) + { + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|0 ); + GainTotal += IterativelyApplyExorLink( DIST3, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST2, 1|2|4 ); + GainTotal += IterativelyApplyExorLink( DIST4, 1|2|0 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } +// while ( nIterWithoutImprovement < 20 ); +// while ( nIterWithoutImprovement < 7 ); + while ( nIterWithoutImprovement < 1 + g_CoverInfo.Quality ); + +*/ + +/* +// the last tried + + long clk1 = Abc_Clock(); + int nIterWithoutImprovement = 0; + int nIterCount = 0; + int GainTotal; + do + { + if ( g_CoverInfo.Verbosity == 2 ) + printf( "\nITERATION #" << ++nIterCount << endl << endl; + else if ( g_CoverInfo.Verbosity == 1 ) + cout << '.'; + + GainTotal = 0; + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + + if ( nIterWithoutImprovement > (int)(g_CoverInfo.Quality>0) ) + { + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|0 ); + + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|0 ); + GainTotal += IterativelyApplyExorLink2( 1|2|0 ); + GainTotal += IterativelyApplyExorLink3( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|4 ); + GainTotal += IterativelyApplyExorLink2( 1|2|4 ); + GainTotal += IterativelyApplyExorLink4( 1|2|0 ); + } + + if ( GainTotal ) + nIterWithoutImprovement = 0; + else + nIterWithoutImprovement++; + } + while ( nIterWithoutImprovement < 1 + g_CoverInfo.Quality ); +*/ + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void AddCubesToStartingCover( Vec_Wec_t * vEsop ) +{ + Vec_Int_t * vCube; + Cube * pNew; + int * s_Level2Var; + int * s_LevelValues; + int c, i, k, Lit, Out; + + s_Level2Var = ABC_ALLOC( int, g_CoverInfo.nVarsIn ); + s_LevelValues = ABC_ALLOC( int, g_CoverInfo.nVarsIn ); + + for ( i = 0; i < g_CoverInfo.nVarsIn; i++ ) + s_Level2Var[i] = i; + + g_CoverInfo.nLiteralsBefore = 0; + g_CoverInfo.QCostBefore = 0; + Vec_WecForEachLevel( vEsop, vCube, c ) + { + // get the output of this cube + Out = -Vec_IntPop(vCube) - 1; + + // fill in the cube with blanks + for ( i = 0; i < g_CoverInfo.nVarsIn; i++ ) + s_LevelValues[i] = VAR_ABS; + Vec_IntForEachEntry( vCube, Lit, k ) + { + if ( Abc_LitIsCompl(Lit) ) + s_LevelValues[Abc_Lit2Var(Lit)] = VAR_NEG; + else + s_LevelValues[Abc_Lit2Var(Lit)] = VAR_POS; + } + + // get the new cube + pNew = GetFreeCube(); + // consider the need to clear the cube + if ( pNew->pCubeDataIn[0] ) // this is a recycled cube + { + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + pNew->pCubeDataIn[i] = 0; + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + pNew->pCubeDataOut[i] = 0; + } + + InsertVarsWithoutClearing( pNew, s_Level2Var, g_CoverInfo.nVarsIn, s_LevelValues, Out ); + // set literal counts + pNew->a = Vec_IntSize(vCube); + pNew->z = 1; + pNew->q = ComputeQCost(vCube); + // set the ID + pNew->ID = g_CoverInfo.cIDs++; + // skip through zero-ID + if ( g_CoverInfo.cIDs == 256 ) + g_CoverInfo.cIDs = 1; + + // add this cube to storage + CheckForCloseCubes( pNew, 1 ); + + g_CoverInfo.nLiteralsBefore += Vec_IntSize(vCube); + g_CoverInfo.QCostBefore += ComputeQCost(vCube); + } + ABC_FREE( s_Level2Var ); + ABC_FREE( s_LevelValues ); + + assert ( g_CoverInfo.nCubesInUse + g_CoverInfo.nCubesFree == g_CoverInfo.nCubesAlloc ); +} + +/**Function************************************************************* + + Synopsis [Performs heuristic minimization of ESOPs.] + + Description [Returns 1 on success, 0 on failure.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Exorcism( Vec_Wec_t * vEsop, int nIns, int nOuts, std::function const& onCube ) +{ + abctime clk1; + int RemainderBits; + int TotalWords; + int MemTemp, MemTotal; + + /////////////////////////////////////////////////////////////////////// + // STEPS of HEURISTIC ESOP MINIMIZATION + /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // STEP 1: determine the size of the starting cover + /////////////////////////////////////////////////////////////////////// + assert( nIns > 0 ); + // inputs + RemainderBits = (nIns*2)%(sizeof(unsigned)*8); + TotalWords = (nIns*2)/(sizeof(unsigned)*8) + (RemainderBits > 0); + g_CoverInfo.nVarsIn = nIns; + g_CoverInfo.nWordsIn = TotalWords; + // outputs + RemainderBits = (nOuts)%(sizeof(unsigned)*8); + TotalWords = (nOuts)/(sizeof(unsigned)*8) + (RemainderBits > 0); + g_CoverInfo.nVarsOut = nOuts; + g_CoverInfo.nWordsOut = TotalWords; + g_CoverInfo.cIDs = 1; + + // cubes + clk1 = Abc_Clock(); +// g_CoverInfo.nCubesBefore = CountTermsInPseudoKroneckerCover( g_Func.dd, nOuts ); + g_CoverInfo.nCubesBefore = Vec_WecSize(vEsop); + g_CoverInfo.TimeStart = Abc_Clock() - clk1; + + if ( g_CoverInfo.Verbosity ) + { + printf( "Starting cover generation time is %.2f sec\n", TICKS_TO_SECONDS(g_CoverInfo.TimeStart) ); + printf( "The number of cubes in the starting cover is %d\n", g_CoverInfo.nCubesBefore ); + } + + if ( g_CoverInfo.nCubesBefore > g_CoverInfo.nCubesMax ) + { + printf( "\nThe size of the starting cover is more than %d cubes. Quitting...\n", g_CoverInfo.nCubesMax ); + return 0; + } + + /////////////////////////////////////////////////////////////////////// + // STEP 2: prepare internal data structures + /////////////////////////////////////////////////////////////////////// + g_CoverInfo.nCubesAlloc = g_CoverInfo.nCubesBefore + ADDITIONAL_CUBES; + + // allocate cube cover + MemTotal = 0; + MemTemp = AllocateCover( g_CoverInfo.nCubesAlloc, g_CoverInfo.nWordsIn, g_CoverInfo.nWordsOut ); + if ( MemTemp == 0 ) + { + printf( "Unexpected memory allocation problem. Quitting...\n" ); + return 0; + } + else + MemTotal += MemTemp; + + // allocate cube sets + MemTemp = AllocateCubeSets( g_CoverInfo.nVarsIn, g_CoverInfo.nVarsOut ); + if ( MemTemp == 0 ) + { + printf( "Unexpected memory allocation problem. Quitting...\n" ); + return 0; + } + else + MemTotal += MemTemp; + + // allocate adjacency queques + MemTemp = AllocateQueques( g_CoverInfo.nCubesAlloc*g_CoverInfo.nCubesAlloc/CUBE_PAIR_FACTOR ); + if ( MemTemp == 0 ) + { + printf( "Unexpected memory allocation problem. Quitting...\n" ); + return 0; + } + else + MemTotal += MemTemp; + + if ( g_CoverInfo.Verbosity ) + printf( "Dynamically allocated memory is %dK\n", MemTotal/1000 ); + + /////////////////////////////////////////////////////////////////////// + // STEP 3: write the cube cover into the allocated storage + /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + clk1 = Abc_Clock(); + if ( g_CoverInfo.Verbosity ) + printf( "Generating the starting cover...\n" ); + AddCubesToStartingCover( vEsop ); + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + // STEP 4: iteratively improve the cover + /////////////////////////////////////////////////////////////////////// + if ( g_CoverInfo.Verbosity ) + printf( "Performing minimization...\n" ); + clk1 = Abc_Clock(); + ReduceEsopCover(); + g_CoverInfo.TimeMin = Abc_Clock() - clk1; +// g_Func.TimeMin = (float)(Abc_Clock() - clk1)/(float)(CLOCKS_PER_SEC); + if ( g_CoverInfo.Verbosity ) + { + printf( "\nMinimization time is %.2f sec\n", TICKS_TO_SECONDS(g_CoverInfo.TimeMin) ); + printf( "\nThe number of cubes after minimization is %d\n", g_CoverInfo.nCubesInUse ); + } + + /////////////////////////////////////////////////////////////////////// + // STEP 5: save the cover into file + /////////////////////////////////////////////////////////////////////// + // if option is MULTI_OUTPUT, the output is written into the output file; + // if option is SINGLE_NODE, the output is added to the input file + // and written into the output file; in this case, the minimized nodes is + // also stored in the temporary file "temp.blif" for verification + + // create the file name and write the output + extern Cube* IterCubeSetStart(); + extern Cube* IterCubeSetNext(); + extern varvalue GetVar( Cube* pC, int Var ); + + { + int v; + Cube* p; + + for ( p = IterCubeSetStart(); p; p = IterCubeSetNext() ) + { + assert( p->fMark == 0 ); + + // write the input variables + uint32_t mask{}, bits{}; + for ( v = 0; v < g_CoverInfo.nVarsIn; v++ ) + { + int Value = GetVar( p, v ); + if ( Value == VAR_NEG ) + { + mask |= 1 << v; + } + else if ( Value == VAR_POS ) + { + mask |= 1 << v; + bits |= 1 << v; + } + else if ( Value == VAR_ABS ) + { + // do nothing + } + else + assert( 0 ); + } + + onCube( bits, mask ); + } + } + + /////////////////////////////////////////////////////////////////////// + // STEP 6: delocate memory + /////////////////////////////////////////////////////////////////////// + DelocateCubeSets(); + DelocateCover(); + DelocateQueques(); + + // return success + return 1; +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_ExorcismMain( Vec_Wec_t * vEsop, int nIns, int nOuts, std::function const& onCube, int Quality, int Verbosity, int nCubesMax, int fUseQCost ) +{ + memset( &g_CoverInfo, 0, sizeof(cinfo) ); + g_CoverInfo.Quality = Quality; + g_CoverInfo.Verbosity = Verbosity; + g_CoverInfo.nCubesMax = nCubesMax; + g_CoverInfo.fUseQCost = fUseQCost; + if ( fUseQCost ) + s_fDecreaseLiterals = 1; + if ( g_CoverInfo.Verbosity ) + { + printf( "\nEXORCISM, Ver.4.7: Exclusive Sum-of-Product Minimizer\n" ); + printf( "by Alan Mishchenko, Portland State University, July-September 2000\n\n" ); + printf( "Incoming ESOP has %d inputs, %d outputs, and %d cubes.\n", nIns, nOuts, Vec_WecSize(vEsop) ); + } + PrepareBitSetModule(); + if ( Exorcism( vEsop, nIns, nOuts, onCube ) == 0 ) + { + printf( "Something went wrong when minimizing the cover\n" ); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} + diff --git a/lib/abcesop/exorBits.cpp b/lib/abcesop/exorBits.cpp new file mode 100644 index 0000000..c362381 --- /dev/null +++ b/lib/abcesop/exorBits.cpp @@ -0,0 +1,425 @@ +/**CFile**************************************************************** + + FileName [exorBits.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Bit-level procedures.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exorBits.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// EXOR-Oriented Bit String Manipulation /// +/// /// +/// Ver. 1.0. Started - July 18, 2000. Last update - July 20, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 10, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// information about the cube cover +// the number of cubes is constantly updated when the cube cover is processed +// in this module, only the number of variables (nVarsIn) and integers (nWordsIn) +// is used, which do not change +extern cinfo g_CoverInfo; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTIONS OF THIS MODULE /// +//////////////////////////////////////////////////////////////////////// + +int GetDistance( Cube * pC1, Cube * pC2 ); +// return the distance between two cubes +int GetDistancePlus( Cube * pC1, Cube * pC2 ); + +int FindDiffVars( int * pDiffVars, Cube * pC1, Cube * pC2 ); +// determine different variables in cubes from pCubes[] and writes them into pDiffVars +// returns the number of different variables + +void InsertVars( Cube * pC, int * pVars, int nVarsIn, int * pVarValues ); + +//inline int VarWord( int element ); +//inline int VarBit( int element ); +varvalue GetVar( Cube * pC, int Var ); + +void ExorVar( Cube * pC, int Var, varvalue Val ); + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// the bit count for the first 256 integer numbers +static unsigned char BitCount8[256] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +static int SparseNumbers[163] = { + 0,1,4,5,16,17,20,21,64,65,68,69,80,81,84,85,256,257,260, + 261,272,273,276,277,320,321,324,325,336,337,340,1024,1025, + 1028,1029,1040,1041,1044,1045,1088,1089,1092,1093,1104,1105, + 1108,1280,1281,1284,1285,1296,1297,1300,1344,1345,1348,1360, + 4096,4097,4100,4101,4112,4113,4116,4117,4160,4161,4164,4165, + 4176,4177,4180,4352,4353,4356,4357,4368,4369,4372,4416,4417, + 4420,4432,5120,5121,5124,5125,5136,5137,5140,5184,5185,5188, + 5200,5376,5377,5380,5392,5440,16384,16385,16388,16389,16400, + 16401,16404,16405,16448,16449,16452,16453,16464,16465,16468, + 16640,16641,16644,16645,16656,16657,16660,16704,16705,16708, + 16720,17408,17409,17412,17413,17424,17425,17428,17472,17473, + 17476,17488,17664,17665,17668,17680,17728,20480,20481,20484, + 20485,20496,20497,20500,20544,20545,20548,20560,20736,20737, + 20740,20752,20800,21504,21505,21508,21520,21568,21760 +}; + +static unsigned char GroupLiterals[163][4] = { + {0}, {0}, {1}, {0,1}, {2}, {0,2}, {1,2}, {0,1,2}, {3}, {0,3}, + {1,3}, {0,1,3}, {2,3}, {0,2,3}, {1,2,3}, {0,1,2,3}, {4}, {0,4}, + {1,4}, {0,1,4}, {2,4}, {0,2,4}, {1,2,4}, {0,1,2,4}, {3,4}, + {0,3,4}, {1,3,4}, {0,1,3,4}, {2,3,4}, {0,2,3,4}, {1,2,3,4}, {5}, + {0,5}, {1,5}, {0,1,5}, {2,5}, {0,2,5}, {1,2,5}, {0,1,2,5}, {3,5}, + {0,3,5}, {1,3,5}, {0,1,3,5}, {2,3,5}, {0,2,3,5}, {1,2,3,5}, + {4,5}, {0,4,5}, {1,4,5}, {0,1,4,5}, {2,4,5}, {0,2,4,5}, + {1,2,4,5}, {3,4,5}, {0,3,4,5}, {1,3,4,5}, {2,3,4,5}, {6}, {0,6}, + {1,6}, {0,1,6}, {2,6}, {0,2,6}, {1,2,6}, {0,1,2,6}, {3,6}, + {0,3,6}, {1,3,6}, {0,1,3,6}, {2,3,6}, {0,2,3,6}, {1,2,3,6}, + {4,6}, {0,4,6}, {1,4,6}, {0,1,4,6}, {2,4,6}, {0,2,4,6}, + {1,2,4,6}, {3,4,6}, {0,3,4,6}, {1,3,4,6}, {2,3,4,6}, {5,6}, + {0,5,6}, {1,5,6}, {0,1,5,6}, {2,5,6}, {0,2,5,6}, {1,2,5,6}, + {3,5,6}, {0,3,5,6}, {1,3,5,6}, {2,3,5,6}, {4,5,6}, {0,4,5,6}, + {1,4,5,6}, {2,4,5,6}, {3,4,5,6}, {7}, {0,7}, {1,7}, {0,1,7}, + {2,7}, {0,2,7}, {1,2,7}, {0,1,2,7}, {3,7}, {0,3,7}, {1,3,7}, + {0,1,3,7}, {2,3,7}, {0,2,3,7}, {1,2,3,7}, {4,7}, {0,4,7}, + {1,4,7}, {0,1,4,7}, {2,4,7}, {0,2,4,7}, {1,2,4,7}, {3,4,7}, + {0,3,4,7}, {1,3,4,7}, {2,3,4,7}, {5,7}, {0,5,7}, {1,5,7}, + {0,1,5,7}, {2,5,7}, {0,2,5,7}, {1,2,5,7}, {3,5,7}, {0,3,5,7}, + {1,3,5,7}, {2,3,5,7}, {4,5,7}, {0,4,5,7}, {1,4,5,7}, {2,4,5,7}, + {3,4,5,7}, {6,7}, {0,6,7}, {1,6,7}, {0,1,6,7}, {2,6,7}, + {0,2,6,7}, {1,2,6,7}, {3,6,7}, {0,3,6,7}, {1,3,6,7}, {2,3,6,7}, + {4,6,7}, {0,4,6,7}, {1,4,6,7}, {2,4,6,7}, {3,4,6,7}, {5,6,7}, + {0,5,6,7}, {1,5,6,7}, {2,5,6,7}, {3,5,6,7}, {4,5,6,7} +}; + +// the bit count to 16-bit numbers +#define FULL16BITS 0x10000 +#define MARKNUMBER 200 + +static unsigned char BitGroupNumbers[FULL16BITS]; +unsigned char BitCount[FULL16BITS]; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +void PrepareBitSetModule() +// this function should be called before anything is done with the cube cover +{ + // prepare bit count + int i, k; + int nLimit; + + nLimit = FULL16BITS; + for ( i = 0; i < nLimit; i++ ) + { + BitCount[i] = BitCount8[ i & 0xff ] + BitCount8[ i>>8 ]; + BitGroupNumbers[i] = MARKNUMBER; + } + // prepare bit groups + for ( k = 0; k < 163; k++ ) + BitGroupNumbers[ SparseNumbers[k] ] = k; +/* + // verify bit groups + int n = 4368; + char Buff[100]; + cout << "The number is " << n << endl; + cout << "The binary is " << itoa(n,Buff,2) << endl; + cout << "BitGroupNumbers[n] is " << (int)BitGroupNumbers[n] << endl; + cout << "The group literals are "; + for ( int g = 0; g < 4; g++ ) + cout << " " << (int)GroupLiterals[BitGroupNumbers[n]][g]; +*/ +} + +//////////////////////////////////////////////////////////////////////// +/// INLINE FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// +/* +int VarWord( int element ) +{ + return ( element >> LOGBPI ); +} + +int VarBit( int element ) +{ + return ( element & BPIMASK ); +} +*/ + +varvalue GetVar( Cube * pC, int Var ) +// returns VAR_NEG if var is neg, VAR_POS if var is pos, VAR_ABS if var is absent +{ + int Bit = (Var<<1); + int Value = ((pC->pCubeDataIn[VarWord(Bit)] >> VarBit(Bit)) & 3); + assert( Value == VAR_NEG || Value == VAR_POS || Value == VAR_ABS ); + return (varvalue)Value; +} + +void ExorVar( Cube * pC, int Var, varvalue Val ) +// EXORs the value Val with the value of variable Var in the given cube +// ((cube[VAR_WORD((v)<<1)]) ^ ( (pol)<pCubeDataIn[VarWord(Bit)] ^= ( Val << VarBit(Bit) ); +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static int DiffVarCounter, cVars; +static drow Temp1, Temp2, Temp; +static drow LastNonZeroWord; +static int LastNonZeroWordNum; + +int GetDistance( Cube * pC1, Cube * pC2 ) +// finds and returns the distance between two cubes pC1 and pC2 +{ + int i; + DiffVarCounter = 0; + + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + { + Temp1 = pC1->pCubeDataIn[i] ^ pC2->pCubeDataIn[i]; + Temp2 = (Temp1|(Temp1>>1)) & DIFFERENT; + + // count how many bits are one in this var difference + DiffVarCounter += BIT_COUNT(Temp2); + if ( DiffVarCounter > 4 ) + return 5; + } + // check whether the output parts are different + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + if ( pC1->pCubeDataOut[i] ^ pC2->pCubeDataOut[i] ) + { + DiffVarCounter++; + break; + } + return DiffVarCounter; +} + +// place to put the number of the different variable and its value in the second cube +extern int s_DiffVarNum; +extern int s_DiffVarValueP_old; +extern int s_DiffVarValueP_new; +extern int s_DiffVarValueQ; + +int GetDistancePlus( Cube * pC1, Cube * pC2 ) +// finds and returns the distance between two cubes pC1 and pC2 +// if the distance is 1, returns the number of diff variable in VarNum +{ + int i; + + DiffVarCounter = 0; + LastNonZeroWordNum = -1; + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + { + Temp1 = pC1->pCubeDataIn[i] ^ pC2->pCubeDataIn[i]; + Temp2 = (Temp1|(Temp1>>1)) & DIFFERENT; + + // save the value of var difference, in case + // the distance is one and we need to return the var number + if ( Temp2 ) + { + LastNonZeroWordNum = i; + LastNonZeroWord = Temp2; + } + + // count how many bits are one in this var difference + DiffVarCounter += BIT_COUNT(Temp2); + if ( DiffVarCounter > 4 ) + return 5; + } + // check whether the output parts are different + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + if ( pC1->pCubeDataOut[i] ^ pC2->pCubeDataOut[i] ) + { + DiffVarCounter++; + break; + } + + if ( DiffVarCounter == 1 ) + { + if ( LastNonZeroWordNum == -1 ) // the output is the only different variable + s_DiffVarNum = -1; + else + { + Temp = (LastNonZeroWord>>2); + for ( i = 0; Temp; Temp>>=2, i++ ); + s_DiffVarNum = LastNonZeroWordNum*BPI/2 + i; + + // save the old var value + s_DiffVarValueP_old = GetVar( pC1, s_DiffVarNum ); + s_DiffVarValueQ = GetVar( pC2, s_DiffVarNum ); + + // EXOR this value with the corresponding value in p cube + ExorVar( pC1, s_DiffVarNum, (varvalue)s_DiffVarValueQ ); + + s_DiffVarValueP_new = GetVar( pC1, s_DiffVarNum ); + } + } + + return DiffVarCounter; +} + +int FindDiffVars( int * pDiffVars, Cube * pC1, Cube * pC2 ) +// determine different variables in two cubes and +// writes them into pDiffVars[] +// -1 is written into pDiffVars[0] if the cubes have different outputs +// returns the number of different variables (including the output) +{ + int i, v; + DiffVarCounter = 0; + // check whether the output parts of the cubes are different + + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + if ( pC1->pCubeDataOut[i] != pC2->pCubeDataOut[i] ) + { // they are different + pDiffVars[0] = -1; + DiffVarCounter = 1; + break; + } + + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + { + + Temp1 = pC1->pCubeDataIn[i] ^ pC2->pCubeDataIn[i]; + Temp2 = (Temp1|(Temp1>>1)) & DIFFERENT; + + // check the first part of this word + Temp = Temp2 & 0xffff; + cVars = BitCount[ Temp ]; + if ( cVars ) + { + if ( cVars < 5 ) + for ( v = 0; v < cVars; v++ ) + { + assert( BitGroupNumbers[Temp] != MARKNUMBER ); + pDiffVars[ DiffVarCounter++ ] = i*16 + GroupLiterals[ BitGroupNumbers[Temp] ][v]; + } + else + return 5; + } + if ( DiffVarCounter > 4 ) + return 5; + + // check the second part of this word + Temp = Temp2 >> 16; + cVars = BitCount[ Temp ]; + if ( cVars ) + { + if ( cVars < 5 ) + for ( v = 0; v < cVars; v++ ) + { + assert( BitGroupNumbers[Temp] != MARKNUMBER ); + pDiffVars[ DiffVarCounter++ ] = i*16 + 8 + GroupLiterals[ BitGroupNumbers[Temp] ][v]; + } + else + return 5; + } + if ( DiffVarCounter > 4 ) + return 5; + } + return DiffVarCounter; +} + +void InsertVars( Cube * pC, int * pVars, int nVarsIn, int * pVarValues ) +// corrects the given number of variables (nVarsIn) in pC->pCubeDataIn[] +// variable numbers are given in pVarNumbers[], their values are in pVarValues[] +// arrays pVarNumbers[] and pVarValues[] are provided by the user +{ + int GlobalBit; + int LocalWord; + int LocalBit; + int i; + assert( nVarsIn > 0 && nVarsIn <= g_CoverInfo.nVarsIn ); + for ( i = 0; i < nVarsIn; i++ ) + { + assert( pVars[i] >= 0 && pVars[i] < g_CoverInfo.nVarsIn ); + assert( pVarValues[i] == VAR_NEG || pVarValues[i] == VAR_POS || pVarValues[i] == VAR_ABS ); + GlobalBit = (pVars[i]<<1); + LocalWord = VarWord(GlobalBit); + LocalBit = VarBit(GlobalBit); + + // correct this variables + pC->pCubeDataIn[LocalWord] = ((pC->pCubeDataIn[LocalWord]&(~(3<pCubeDataIn[] +// variable numbers are given in pVarNumbers[], their values are in pVarValues[] +// arrays pVarNumbers[] and pVarValues[] are provided by the user +{ + int GlobalBit; + int LocalWord; + int LocalBit; + int i; + assert( nVarsIn > 0 && nVarsIn <= g_CoverInfo.nVarsIn ); + for ( i = 0; i < nVarsIn; i++ ) + { + assert( pVars[i] >= 0 && pVars[i] < g_CoverInfo.nVarsIn ); + assert( pVarValues[i] == VAR_NEG || pVarValues[i] == VAR_POS || pVarValues[i] == VAR_ABS ); + GlobalBit = (pVars[i]<<1); + LocalWord = VarWord(GlobalBit); + LocalBit = VarBit(GlobalBit); + + // correct this variables + pC->pCubeDataIn[LocalWord] |= ( pVarValues[i] << LocalBit ); + } + // insert the output bit + pC->pCubeDataOut[VarWord(Output)] |= ( 1 << VarBit(Output) ); +} + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} diff --git a/lib/abcesop/exorCubes.cpp b/lib/abcesop/exorCubes.cpp new file mode 100644 index 0000000..35a1d2a --- /dev/null +++ b/lib/abcesop/exorCubes.cpp @@ -0,0 +1,190 @@ +/**CFile**************************************************************** + + FileName [exorCubes.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Cube manipulation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exorCubes.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Cube Allocation and Free Cube Management /// +/// /// +/// Ver. 1.0. Started - July 18, 2000. Last update - July 20, 2000 /// +/// Ver. 1.1. Started - July 24, 2000. Last update - July 29, 2000 /// +/// Ver. 1.2. Started - July 30, 2000. Last update - July 30, 2000 /// +/// Ver. 1.5. Started - Aug 19, 2000. Last update - Aug 19, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// information about the cube cover before and after simplification +extern cinfo g_CoverInfo; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTIONS OF THIS MODULE /// +//////////////////////////////////////////////////////////////////////// + +// cube cover memory allocation/delocation procedures +// (called from the ExorMain module) +int AllocateCover( int nCubes, int nWordsIn, int nWordsOut ); +void DelocateCover(); + +// manipulation of the free cube list +// (called from Pseudo-Kronecker, ExorList, and ExorLink modules) +void AddToFreeCubes( Cube * pC ); +Cube * GetFreeCube(); + +//////////////////////////////////////////////////////////////////////// +/// EXPORTED VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// the pointer to the allocated memory +Cube ** s_pCoverMemory; + +// the list of free cubes +Cube * s_CubesFree; + +/////////////////////////////////////////////////////////////////// +/// CUBE COVER MEMORY MANAGEMENT // +/////////////////////////////////////////////////////////////////// + +int AllocateCover( int nCubes, int nWordsIn, int nWordsOut ) +// uses the cover parameters nCubes and nWords +// to allocate-and-clean the cover in one large piece +{ + int OneCubeSize; + int OneInputSetSize; + Cube ** pp; + int TotalSize; + int i, k; + + // determine the size of one cube WITH storage for bits + OneCubeSize = sizeof(Cube) + (nWordsIn+nWordsOut)*sizeof(unsigned); + // determine what is the amount of storage for the input part of the cube + OneInputSetSize = nWordsIn*sizeof(unsigned); + + // allocate memory for the array of pointers + pp = (Cube **)ABC_ALLOC( Cube *, nCubes ); + if ( pp == NULL ) + return 0; + + // determine the size of the total cube cover + TotalSize = nCubes*OneCubeSize; + // allocate and clear memory for the cover in one large piece + pp[0] = (Cube *)ABC_ALLOC( char, TotalSize ); + if ( pp[0] == NULL ) + return 0; + memset( pp[0], 0, (size_t)TotalSize ); + + // assign pointers to cubes and bit strings inside this piece + pp[0]->pCubeDataIn = (unsigned*)(pp[0] + 1); + pp[0]->pCubeDataOut = (unsigned*)((char*)pp[0]->pCubeDataIn + OneInputSetSize); + for ( i = 1; i < nCubes; i++ ) + { + pp[i] = (Cube *)((char*)pp[i-1] + OneCubeSize); + pp[i]->pCubeDataIn = (unsigned*)(pp[i] + 1); + pp[i]->pCubeDataOut = (unsigned*)((char*)pp[i]->pCubeDataIn + OneInputSetSize); + } + + // connect the cubes into the list using Next pointers + for ( k = 0; k < nCubes-1; k++ ) + pp[k]->Next = pp[k+1]; + // the last pointer is already set to NULL + + // assign the head of the free list + s_CubesFree = pp[0]; + // set the counters of the used and free cubes + g_CoverInfo.nCubesInUse = 0; + g_CoverInfo.nCubesFree = nCubes; + + // save the pointer to the allocated memory + s_pCoverMemory = pp; + + assert ( g_CoverInfo.nCubesInUse + g_CoverInfo.nCubesFree == g_CoverInfo.nCubesAlloc ); + + return nCubes*sizeof(Cube *) + TotalSize; +} + +void DelocateCover() +{ + ABC_FREE( s_pCoverMemory[0] ); + ABC_FREE( s_pCoverMemory ); +} + +/////////////////////////////////////////////////////////////////// +/// FREE CUBE LIST MANIPULATION FUNCTIONS /// +/////////////////////////////////////////////////////////////////// + +void AddToFreeCubes( Cube * p ) +{ + assert( p ); + assert( p->Prev == NULL ); // the cube should not be in use + assert( p->Next == NULL ); + assert( p->ID ); + + p->Next = s_CubesFree; + s_CubesFree = p; + + // set the ID of the cube to 0, + // so that cube pair garbage collection could recognize it as different + p->ID = 0; + + g_CoverInfo.nCubesFree++; +} + +Cube * GetFreeCube() +{ + Cube * p; + assert( s_CubesFree ); + p = s_CubesFree; + s_CubesFree = s_CubesFree->Next; + p->Next = NULL; + g_CoverInfo.nCubesFree--; + return p; +} + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} diff --git a/lib/abcesop/exorLink.cpp b/lib/abcesop/exorLink.cpp new file mode 100644 index 0000000..634ec77 --- /dev/null +++ b/lib/abcesop/exorLink.cpp @@ -0,0 +1,749 @@ +/**CFile**************************************************************** + + FileName [exorLink.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Cube iterators.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exorLink.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Generation of ExorLinked Cubes /// +/// /// +/// Ver. 1.0. Started - July 26, 2000. Last update - July 29, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 12, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define LARGE_NUM 1000000 + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTIONS OF THIS MODULE /// +//////////////////////////////////////////////////////////////////////// + +int ExorLinkCubeIteratorStart( Cube** pGroup, Cube* pC1, Cube* pC2, cubedist Dist ); +// this function starts the Exor-Link iterator, which iterates +// through the cube groups starting from the group with min literals +// returns 1 on success, returns 0 if the cubes have wrong distance + +int ExorLinkCubeIteratorNext( Cube** pGroup ); +// give the next group in the decreasing order of sum of literals +// returns 1 on success, returns 0 if there are no more groups + +int ExorLinkCubeIteratorPick( Cube** pGroup, int g ); +// gives the group #g in the order in which the groups were given +// during iteration +// returns 1 on success, returns 0 if something g is too large + +void ExorLinkCubeIteratorCleanUp( int fTakeLastGroup ); +// removes the cubes from the store back into the list of free cubes +// if fTakeLastGroup is 0, removes all cubes +// if fTakeLastGroup is 1, does not store the last group + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// information about the cube cover before +extern cinfo g_CoverInfo; +// new IDs are assigned only when it is known that the cubes are useful +// this is done in ExorLinkCubeIteratorCleanUp(); + +// the head of the list of free cubes +extern Cube* g_CubesFree; + +extern byte BitCount[]; + +//////////////////////////////////////////////////////////////////////// +/// EXORLINK INFO /// +//////////////////////////////////////////////////////////////////////// + +const int s_ELMax = 4; + +// ExorLink-2: there are 4 cubes, 2 literals each, combined into 2 groups +// ExorLink-3: there are 12 cubes, 3 literals each, combined into 6 groups +// ExorLink-4: there are 32 cubes, 4 literals each, combined into 24 groups +// ExorLink-5: there are 80 cubes, 5 literals each, combined into 120 groups +// Exorlink-n: there are n*2^(n-1) cubes, n literals each, combined into n! groups +const int s_ELnCubes[4] = { 4, 12, 32, 80 }; +const int s_ELnGroups[4] = { 2, 6, 24, 120 }; + +// value sets of cubes X{a0}Y{b0}Z{c0}U{d0} and X{a1}Y{b1}Z{c1}U{d1} +// used to represent the ExorLink cube generation rules +enum { vs0, vs1, vsX }; +// vs0 = 0, // the value set of the first cube +// vs1 = 1, // the value set of the second cube +// vsX = 2 // EXOR of the value sets of the first and second cubes + +// representation of ExorLinked cubes +static int s_ELCubeRules[3][32][4] = { +{ // ExorLink-2 Cube Generating Rules + // | 0 | 1 | - sections + // |-------| + {vsX,vs0}, // cube 0 | | | + {vsX,vs1}, // cube 1 | | 0 | + {vs0,vsX}, // cube 2 | | | + {vs1,vsX} // cube 3 | 0 | | +}, +{ // ExorLink-3 Cube Generating Rules + // | 0 | 1 | 2 | - sections + // |-----------| + {vsX,vs0,vs0}, // cube 0 | | | | + {vsX,vs0,vs1}, // cube 1 | | | 0 | + {vsX,vs1,vs0}, // cube 2 | | 0 | | + {vsX,vs1,vs1}, // cube 3 | | 1 | 1 | + + {vs0,vsX,vs0}, // cube 4 | | | | + {vs0,vsX,vs1}, // cube 5 | | | 2 | + {vs1,vsX,vs0}, // cube 6 | 0 | | | + {vs1,vsX,vs1}, // cube 7 | 1 | | 3 | + + {vs0,vs0,vsX}, // cube 8 | | | | + {vs0,vs1,vsX}, // cube 9 | | 2 | | + {vs1,vs0,vsX}, // cube 10 | 2 | | | + {vs1,vs1,vsX} // cube 11 | 3 | 3 | | +}, +{ // ExorLink-4 Rules Generating Rules + // | 0 | 1 | 2 | 4 | - sections + // |---------------| + {vsX,vs0,vs0,vs0}, // cube 0 | | | | | + {vsX,vs0,vs0,vs1}, // cube 1 | | | | 0 | + {vsX,vs0,vs1,vs0}, // cube 2 | | | 0 | | + {vsX,vs0,vs1,vs1}, // cube 3 | | | 1 | 1 | + {vsX,vs1,vs0,vs0}, // cube 4 | | 0 | | | + {vsX,vs1,vs0,vs1}, // cube 5 | | 1 | | 2 | + {vsX,vs1,vs1,vs0}, // cube 6 | | 2 | 2 | | + {vsX,vs1,vs1,vs1}, // cube 7 | | 3 | 3 | 3 | + + {vs0,vsX,vs0,vs0}, // cube 8 | | | | | + {vs0,vsX,vs0,vs1}, // cube 9 | | | | 4 | + {vs0,vsX,vs1,vs0}, // cube 10 | | | 4 | | + {vs0,vsX,vs1,vs1}, // cube 11 | | | 5 | 5 | + {vs1,vsX,vs0,vs0}, // cube 12 | 0 | | | | + {vs1,vsX,vs0,vs1}, // cube 13 | 1 | | | 6 | + {vs1,vsX,vs1,vs0}, // cube 14 | 2 | | 6 | | + {vs1,vsX,vs1,vs1}, // cube 15 | 3 | | 7 | 7 | + + {vs0,vs0,vsX,vs0}, // cube 16 | | | | | + {vs0,vs0,vsX,vs1}, // cube 17 | | | | 8 | + {vs0,vs1,vsX,vs0}, // cube 18 | | 4 | | | + {vs0,vs1,vsX,vs1}, // cube 19 | | 5 | | 9 | + {vs1,vs0,vsX,vs0}, // cube 20 | 4 | | | | + {vs1,vs0,vsX,vs1}, // cube 21 | 5 | | | 10| + {vs1,vs1,vsX,vs0}, // cube 22 | 6 | 6 | | | + {vs1,vs1,vsX,vs1}, // cube 23 | 7 | 7 | | 11| + + {vs0,vs0,vs0,vsX}, // cube 24 | | | | | + {vs0,vs0,vs1,vsX}, // cube 25 | | | 8 | | + {vs0,vs1,vs0,vsX}, // cube 26 | | 8 | | | + {vs0,vs1,vs1,vsX}, // cube 27 | | 9 | 9 | | + {vs1,vs0,vs0,vsX}, // cube 28 | 8 | | | | + {vs1,vs0,vs1,vsX}, // cube 29 | 9 | | 10| | + {vs1,vs1,vs0,vsX}, // cube 30 | 10| 10| | | + {vs1,vs1,vs1,vsX} // cube 31 | 11| 11| 11| | +} +}; + +// these cubes are combined into groups +static int s_ELGroupRules[3][24][4] = { +{ // ExorLink-2 Group Forming Rules + {0,3}, // group 0 - section 0 + {2,1} // group 1 - section 1 +}, +{ // ExorLink-3 Group Forming Rules + {0,6,11}, // group 0 - section 0 + {0,7,10}, // group 1 + {4,2,11}, // group 2 - section 1 + {4,3,9}, // group 3 + {8,1,7}, // group 4 - section 2 + {8,3,5} // group 5 +}, +{ // ExorLink-4 Group Forming Rules +// section 0: (0-12)(1-13)(2-14)(3-15)(4-20)(5-21)(6-22)(7-23)(8-28)(9-29)(10-30)(11-31) + {0,12,22,31}, // group 0 // {0,6,11}, // group 0 - section 0 + {0,12,23,30}, // group 1 // {0,7,10}, // group 1 + {0,20,14,31}, // group 2 // {4,2,11}, // group 2 + {0,20,15,29}, // group 3 // {4,3,9}, // group 3 + {0,28,13,23}, // group 4 // {8,1,7}, // group 4 + {0,28,15,21}, // group 5 // {8,3,5} // group 5 +// section 1: (0-4)(1-5)(2-6)(3-7)(4-18)(5-19)(6-22)(7-23)(8-26)(9-27)(10-30)(11-31) + {8,4,22,31}, // group 6 + {8,4,23,30}, // group 7 + {8,18,6,31}, // group 8 + {8,18,7,27}, // group 9 + {8,26,5,23}, // group 10 + {8,26,7,19}, // group 11 +// section 2: (0-2)(1-3)(2-6)(3-7)(4-10)(5-11)(6-14)(7-15)(8-25)(9-27)(10-29)(11-31) + {16,2,14,31}, // group 12 + {16,2,15,29}, // group 13 + {16,10,6,31}, // group 14 + {16,10,7,27}, // group 15 + {16,25,3,15}, // group 16 + {16,25,7,11}, // group 17 +// section 3: (0-1)(1-3)(2-5)(3-7)(4-9)(5-11)(6-13)(7-15)(8-17)(9-19)(10-21)(11-23) + {24,1,13,23}, // group 18 + {24,1,15,21}, // group 19 + {24,9, 5,23}, // group 20 + {24,9, 7,19}, // group 21 + {24,17,3,15}, // group 22 + {24,17,7,11} // group 23 +} +}; + +// it is assumed that if literals in the first cube, second cube +// and their EXOR are 0 or 1 (as opposed to -), they are written +// into a mask, which is used to count the number of literals in +// the cube groups cubes +// +// below is the set of masks selecting literals belonging +// to the given cube of the group + +static drow s_CubeLitMasks[3][32] = { +{ // ExorLink-2 Literal Counting Masks +// v3 v2 v1 v0 +// -xBA -xBA -xBA -xBA +// ------------------- + 0x14, // cube 0 <0000 0000 0001 0100> {vsX,vs0} + 0x24, // cube 1 <0000 0000 0010 0100> {vsX,vs1} + 0x41, // cube 2 <0000 0000 0100 0001> {vs0,vsX} + 0x42, // cube 3 <0000 0000 0100 0010> {vs1,vsX} +}, +{ // ExorLink-3 Literal Counting Masks + 0x114, // cube 0 <0000 0001 0001 0100> {vsX,vs0,vs0} + 0x214, // cube 1 <0000 0010 0001 0100> {vsX,vs0,vs1} + 0x124, // cube 2 <0000 0001 0010 0100> {vsX,vs1,vs0} + 0x224, // cube 3 <0000 0010 0010 0100> {vsX,vs1,vs1} + 0x141, // cube 4 <0000 0001 0100 0001> {vs0,vsX,vs0} + 0x241, // cube 5 <0000 0010 0100 0001> {vs0,vsX,vs1} + 0x142, // cube 6 <0000 0001 0100 0010> {vs1,vsX,vs0} + 0x242, // cube 7 <0000 0010 0100 0010> {vs1,vsX,vs1} + 0x411, // cube 8 <0000 0100 0001 0001> {vs0,vs0,vsX} + 0x421, // cube 9 <0000 0100 0010 0001> {vs0,vs1,vsX} + 0x412, // cube 10 <0000 0100 0001 0010> {vs1,vs0,vsX} + 0x422, // cube 11 <0000 0100 0010 0010> {vs1,vs1,vsX} +}, +{ // ExorLink-4 Literal Counting Masks + 0x1114, // cube 0 <0001 0001 0001 0100> {vsX,vs0,vs0,vs0} + 0x2114, // cube 1 <0010 0001 0001 0100> {vsX,vs0,vs0,vs1} + 0x1214, // cube 2 <0001 0010 0001 0100> {vsX,vs0,vs1,vs0} + 0x2214, // cube 3 <0010 0010 0001 0100> {vsX,vs0,vs1,vs1} + 0x1124, // cube 4 <0001 0001 0010 0100> {vsX,vs1,vs0,vs0} + 0x2124, // cube 5 <0010 0001 0010 0100> {vsX,vs1,vs0,vs1} + 0x1224, // cube 6 <0001 0010 0010 0100> {vsX,vs1,vs1,vs0} + 0x2224, // cube 7 <0010 0010 0010 0100> {vsX,vs1,vs1,vs1} + 0x1141, // cube 8 <0001 0001 0100 0001> {vs0,vsX,vs0,vs0} + 0x2141, // cube 9 <0010 0001 0100 0001> {vs0,vsX,vs0,vs1} + 0x1241, // cube 10 <0001 0010 0100 0001> {vs0,vsX,vs1,vs0} + 0x2241, // cube 11 <0010 0010 0100 0001> {vs0,vsX,vs1,vs1} + 0x1142, // cube 12 <0001 0001 0100 0010> {vs1,vsX,vs0,vs0} + 0x2142, // cube 13 <0010 0001 0100 0010> {vs1,vsX,vs0,vs1} + 0x1242, // cube 14 <0001 0010 0100 0010> {vs1,vsX,vs1,vs0} + 0x2242, // cube 15 <0010 0010 0100 0010> {vs1,vsX,vs1,vs1} + 0x1411, // cube 16 <0001 0100 0001 0001> {vs0,vs0,vsX,vs0} + 0x2411, // cube 17 <0010 0100 0001 0001> {vs0,vs0,vsX,vs1} + 0x1421, // cube 18 <0001 0100 0010 0001> {vs0,vs1,vsX,vs0} + 0x2421, // cube 19 <0010 0100 0010 0001> {vs0,vs1,vsX,vs1} + 0x1412, // cube 20 <0001 0100 0001 0010> {vs1,vs0,vsX,vs0} + 0x2412, // cube 21 <0010 0100 0001 0010> {vs1,vs0,vsX,vs1} + 0x1422, // cube 22 <0001 0100 0010 0010> {vs1,vs1,vsX,vs0} + 0x2422, // cube 23 <0010 0100 0010 0010> {vs1,vs1,vsX,vs1} + 0x4111, // cube 24 <0100 0001 0001 0001> {vs0,vs0,vs0,vsX} + 0x4211, // cube 25 <0100 0010 0001 0001> {vs0,vs0,vs1,vsX} + 0x4121, // cube 26 <0100 0001 0010 0001> {vs0,vs1,vs0,vsX} + 0x4221, // cube 27 <0100 0010 0010 0001> {vs0,vs1,vs1,vsX} + 0x4112, // cube 28 <0100 0001 0001 0010> {vs1,vs0,vs0,vsX} + 0x4212, // cube 29 <0100 0010 0001 0010> {vs1,vs0,vs1,vsX} + 0x4122, // cube 30 <0100 0001 0010 0010> {vs1,vs1,vs0,vsX} + 0x4222, // cube 31 <0100 0010 0010 0010> {vs1,vs1,vs1,vsX} +} +}; + +static drow s_BitMasks[32] = +{ + 0x00000001,0x00000002,0x00000004,0x00000008, + 0x00000010,0x00000020,0x00000040,0x00000080, + 0x00000100,0x00000200,0x00000400,0x00000800, + 0x00001000,0x00002000,0x00004000,0x00008000, + 0x00010000,0x00020000,0x00040000,0x00080000, + 0x00100000,0x00200000,0x00400000,0x00800000, + 0x01000000,0x02000000,0x04000000,0x08000000, + 0x10000000,0x20000000,0x40000000,0x80000000 +}; + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// this flag is TRUE as long as the storage is allocated +static int fWorking; + +// set these flags to have minimum literal groups generated first +static int fMinLitGroupsFirst[4] = { 0 /*dist2*/, 0 /*dist3*/, 0 /*dist4*/}; + +static int nDist; +static int nCubes; +static int nCubesInGroup; +static int nGroups; +static Cube *pCA, *pCB; + +// storage for variable numbers that are different in the cubes +static int DiffVars[5]; +static int* pDiffVars; +static int nDifferentVars; + +// storage for the bits and words of different input variables +static int nDiffVarsIn; +static int DiffVarWords[5]; +static int DiffVarBits[5]; + +// literal mask used to count the number of literals in the cubes +static drow MaskLiterals; +// the base for counting literals +static int StartingLiterals; +// the number of literals in each cube +static int CubeLiterals[32]; +static int BitShift; +static int DiffVarValues[4][3]; +static int Value; + +// the sorted array of groups in the increasing order of costs +static int GroupCosts[32]; +static int GroupCostBest; +static int GroupCostBestNum; + +static int CubeNum; +static int NewZ; +static drow Temp; + +// the cubes currently created +static Cube* ELCubes[32]; + +// the bit string with 1's corresponding to cubes in ELCubes[] +// that constitute the last group +static drow LastGroup; + +static int GroupOrder[24]; +static drow VisitedGroups; +static int nVisitedGroups; + +//int RemainderBits = (nVars*2)%(sizeof(drow)*8); +//int TotalWords = (nVars*2)/(sizeof(drow)*8) + (RemainderBits > 0); +static drow DammyBitData[(MAXVARS*2)/(sizeof(drow)*8)+(MAXVARS*2)%(sizeof(drow)*8)]; + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINTIONS /// +//////////////////////////////////////////////////////////////////////// + +// IDEA! if we already used a cube to count distances and it did not improve +// there is no need to try it again with other group +// (this idea works only for ExorLink-2 and -3) + +int ExorLinkCubeIteratorStart( Cube** pGroup, Cube* pC1, Cube* pC2, cubedist Dist ) +// this function starts the Exor-Link iterator, which iterates +// through the cube groups starting from the group with min literals +// returns 1 on success, returns 0 if the cubes have wrong distance +{ + int i, c; + + // check that everything is okey + assert( pC1 != NULL ); + assert( pC2 != NULL ); + assert( !fWorking ); + + nDist = Dist; + nCubes = Dist + 2; + nCubesInGroup = s_ELnCubes[nDist]; + nGroups = s_ELnGroups[Dist]; + pCA = pC1; + pCB = pC2; + // find what variables are different in these two cubes + // FindDiffVars returns DiffVars[0] < 0, if the output is different + nDifferentVars = FindDiffVars( DiffVars, pCA, pCB ); + if ( nCubes != nDifferentVars ) + { +// cout << "ExorLinkCubeIterator(): Distance mismatch"; +// cout << " nCubes = " << nCubes << " nDiffVars = " << nDifferentVars << endl; + fWorking = 0; + return 0; + } + + // copy the input variable cube data into DammyBitData[] + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + DammyBitData[i] = pCA->pCubeDataIn[i]; + + // find the number of different input variables + nDiffVarsIn = ( DiffVars[0] >= 0 )? nCubes: nCubes-1; + // assign the pointer to the place where the number of diff input vars is stored + pDiffVars = ( DiffVars[0] >= 0 )? DiffVars: DiffVars+1; + // find the bit offsets and remove different variables + for ( i = 0; i < nDiffVarsIn; i++ ) + { + DiffVarWords[i] = ((2*pDiffVars[i]) >> LOGBPI) ; + DiffVarBits[i] = ((2*pDiffVars[i]) & BPIMASK); + // clear this position + DammyBitData[ DiffVarWords[i] ] &= ~( 3 << DiffVarBits[i] ); + } + + // extract the values from the cubes and create the mask of literals + MaskLiterals = 0; + // initialize the base for literal counts + StartingLiterals = pCA->a; + for ( i = 0, BitShift = 0; i < nDiffVarsIn; i++, BitShift++ ) + { + DiffVarValues[i][0] = ( pCA->pCubeDataIn[DiffVarWords[i]] >> DiffVarBits[i] ) & 3; + if ( DiffVarValues[i][0] != VAR_ABS ) + { + MaskLiterals |= ( 1 << BitShift ); + // update the base for literal counts + StartingLiterals--; + } + BitShift++; + + DiffVarValues[i][1] = ( pCB->pCubeDataIn[DiffVarWords[i]] >> DiffVarBits[i] ) & 3; + if ( DiffVarValues[i][1] != VAR_ABS ) + MaskLiterals |= ( 1 << BitShift ); + BitShift++; + + DiffVarValues[i][2] = DiffVarValues[i][0] ^ DiffVarValues[i][1]; + if ( DiffVarValues[i][2] != VAR_ABS ) + MaskLiterals |= ( 1 << BitShift ); + BitShift++; + } + + // count the number of additional literals in each cube of the group + for ( i = 0; i < nCubesInGroup; i++ ) + CubeLiterals[i] = BitCount[ MaskLiterals & s_CubeLitMasks[Dist][i] ]; + + // compute the costs of all groups + for ( i = 0; i < nGroups; i++ ) + // go over all cubes in the group + for ( GroupCosts[i] = 0, c = 0; c < nCubes; c++ ) + GroupCosts[i] += CubeLiterals[ s_ELGroupRules[Dist][i][c] ]; + + // find the best cost group + if ( fMinLitGroupsFirst[Dist] ) + { // find the minimum cost group + GroupCostBest = LARGE_NUM; + for ( i = 0; i < nGroups; i++ ) + if ( GroupCostBest > GroupCosts[i] ) + { + GroupCostBest = GroupCosts[i]; + GroupCostBestNum = i; + } + } + else + { // find the maximum cost group + GroupCostBest = -1; + for ( i = 0; i < nGroups; i++ ) + if ( GroupCostBest < GroupCosts[i] ) + { + GroupCostBest = GroupCosts[i]; + GroupCostBestNum = i; + } + } + + // create the cubes with min number of literals needed for the group + LastGroup = 0; + for ( c = 0; c < nCubes; c++ ) + { + CubeNum = s_ELGroupRules[Dist][GroupCostBestNum][c]; + LastGroup |= s_BitMasks[CubeNum]; + + // bring a cube from the free cube list + ELCubes[CubeNum] = GetFreeCube(); + + // copy the input bit data into the cube + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + ELCubes[CubeNum]->pCubeDataIn[i] = DammyBitData[i]; + + // copy the output bit data into the cube + NewZ = 0; + if ( DiffVars[0] >= 0 ) // the output is not involved in ExorLink + { + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + ELCubes[CubeNum]->pCubeDataOut[i] = pCA->pCubeDataOut[i]; + NewZ = pCA->z; + } + else // the output is involved + { // determine where the output information comes from + Value = s_ELCubeRules[Dist][CubeNum][nDiffVarsIn]; + if ( Value == vs0 ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCA->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + else if ( Value == vs1 ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCB->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + else if ( Value == vsX ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCA->pCubeDataOut[i] ^ pCB->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + } + + // set the variables that should be there + for ( i = 0; i < nDiffVarsIn; i++ ) + { + Value = DiffVarValues[i][ s_ELCubeRules[Dist][CubeNum][i] ]; + ELCubes[CubeNum]->pCubeDataIn[ DiffVarWords[i] ] |= ( Value << DiffVarBits[i] ); + } + + // set the number of literals + ELCubes[CubeNum]->a = StartingLiterals + CubeLiterals[CubeNum]; + ELCubes[CubeNum]->z = NewZ; + ELCubes[CubeNum]->q = ComputeQCostBits( ELCubes[CubeNum] ); + + // assign the ID + ELCubes[CubeNum]->ID = g_CoverInfo.cIDs++; + // skip through zero-ID + if ( g_CoverInfo.cIDs == 256 ) + g_CoverInfo.cIDs = 1; + + // prepare the return array + pGroup[c] = ELCubes[CubeNum]; + } + + // mark this group as visited + VisitedGroups |= s_BitMasks[ GroupCostBestNum ]; + // set the first visited group number + GroupOrder[0] = GroupCostBestNum; + // increment the counter of visited groups + nVisitedGroups = 1; + fWorking = 1; + return 1; +} + +int ExorLinkCubeIteratorNext( Cube** pGroup ) +// give the next group in the decreasing order of sum of literals +// returns 1 on success, returns 0 if there are no more groups +{ + int i, c; + + // check that everything is okey + assert( fWorking ); + + if ( nVisitedGroups == nGroups ) + // we have iterated through all groups + return 0; + + // find the min/max cost group + if ( fMinLitGroupsFirst[nDist] ) +// if ( nCubes == 4 ) + { // find the minimum cost + // go through all groups + GroupCostBest = LARGE_NUM; + for ( i = 0; i < nGroups; i++ ) + if ( !(VisitedGroups & s_BitMasks[i]) && GroupCostBest > GroupCosts[i] ) + { + GroupCostBest = GroupCosts[i]; + GroupCostBestNum = i; + } + assert( GroupCostBest != LARGE_NUM ); + } + else + { // find the maximum cost + // go through all groups + GroupCostBest = -1; + for ( i = 0; i < nGroups; i++ ) + if ( !(VisitedGroups & s_BitMasks[i]) && GroupCostBest < GroupCosts[i] ) + { + GroupCostBest = GroupCosts[i]; + GroupCostBestNum = i; + } + assert( GroupCostBest != -1 ); + } + + // create the cubes needed for the group, if they are not created already + LastGroup = 0; + for ( c = 0; c < nCubes; c++ ) + { + CubeNum = s_ELGroupRules[nDist][GroupCostBestNum][c]; + LastGroup |= s_BitMasks[CubeNum]; + + if ( ELCubes[CubeNum] == NULL ) // this cube does not exist + { + // bring a cube from the free cube list + ELCubes[CubeNum] = GetFreeCube(); + + // copy the input bit data into the cube + for ( i = 0; i < g_CoverInfo.nWordsIn; i++ ) + ELCubes[CubeNum]->pCubeDataIn[i] = DammyBitData[i]; + + // copy the output bit data into the cube + NewZ = 0; + if ( DiffVars[0] >= 0 ) // the output is not involved in ExorLink + { + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + ELCubes[CubeNum]->pCubeDataOut[i] = pCA->pCubeDataOut[i]; + NewZ = pCA->z; + } + else // the output is involved + { // determine where the output information comes from + Value = s_ELCubeRules[nDist][CubeNum][nDiffVarsIn]; + if ( Value == vs0 ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCA->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + else if ( Value == vs1 ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCB->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + else if ( Value == vsX ) + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + Temp = pCA->pCubeDataOut[i] ^ pCB->pCubeDataOut[i]; + ELCubes[CubeNum]->pCubeDataOut[i] = Temp; + NewZ += BIT_COUNT(Temp); + } + } + + // set the variables that should be there + for ( i = 0; i < nDiffVarsIn; i++ ) + { + Value = DiffVarValues[i][ s_ELCubeRules[nDist][CubeNum][i] ]; + ELCubes[CubeNum]->pCubeDataIn[ DiffVarWords[i] ] |= ( Value << DiffVarBits[i] ); + } + + // set the number of literals and output ones + ELCubes[CubeNum]->a = StartingLiterals + CubeLiterals[CubeNum]; + ELCubes[CubeNum]->z = NewZ; + ELCubes[CubeNum]->q = ComputeQCostBits( ELCubes[CubeNum] ); + assert( NewZ != 255 ); + + // assign the ID + ELCubes[CubeNum]->ID = g_CoverInfo.cIDs++; + // skip through zero-ID + if ( g_CoverInfo.cIDs == 256 ) + g_CoverInfo.cIDs = 1; + + } + // prepare the return array + pGroup[c] = ELCubes[CubeNum]; + } + + // mark this group as visited + VisitedGroups |= s_BitMasks[ GroupCostBestNum ]; + // set the next visited group number and + // increment the counter of visited groups + GroupOrder[ nVisitedGroups++ ] = GroupCostBestNum; + return 1; +} + +int ExorLinkCubeIteratorPick( Cube** pGroup, int g ) +// gives the group #g in the order in which the groups were given +// during iteration +// returns 1 on success, returns 0 if something is wrong (g is too large) +{ + int GroupNum, c; + + assert( fWorking ); + assert( g >= 0 && g < nGroups ); + assert( VisitedGroups & s_BitMasks[g] ); + + GroupNum = GroupOrder[g]; + // form the group + LastGroup = 0; + for ( c = 0; c < nCubes; c++ ) + { + CubeNum = s_ELGroupRules[nDist][GroupNum][c]; + + // remember this group as the last one + LastGroup |= s_BitMasks[CubeNum]; + + assert( ELCubes[CubeNum] != NULL ); // this cube should exist + // prepare the return array + pGroup[c] = ELCubes[CubeNum]; + } + return 1; +} + +void ExorLinkCubeIteratorCleanUp( int fTakeLastGroup ) +// removes the cubes from the store back into the list of free cubes +// if fTakeLastGroup is 0, removes all cubes +// if fTakeLastGroup is 1, does not store the last group +{ + int c; + assert( fWorking ); + + // put cubes back + // set the cube pointers to zero + if ( fTakeLastGroup == 0 ) + for ( c = 0; c < nCubesInGroup; c++ ) + { + ELCubes[c]->fMark = 0; + AddToFreeCubes( ELCubes[c] ); + ELCubes[c] = NULL; + } + else + for ( c = 0; c < nCubesInGroup; c++ ) + if ( ELCubes[c] ) + { + ELCubes[c]->fMark = 0; + if ( (LastGroup & s_BitMasks[c]) == 0 ) // does not belong to the last group + AddToFreeCubes( ELCubes[c] ); + ELCubes[c] = NULL; + } + + // set the cube groups to zero + VisitedGroups = 0; + // shut down the iterator + fWorking = 0; +} + + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} diff --git a/lib/abcesop/exorList.cpp b/lib/abcesop/exorList.cpp new file mode 100644 index 0000000..e3e1cbc --- /dev/null +++ b/lib/abcesop/exorList.cpp @@ -0,0 +1,1156 @@ +/**CFile**************************************************************** + + FileName [exorList.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Cube lists.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exorList.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Iterative Cube Set Minimization /// +/// Iterative ExorLink Procedure /// +/// Support of Cube Pair Queques /// +/// /// +/// Ver. 1.0. Started - July 18, 2000. Last update - July 20, 2000 /// +/// Ver. 1.1. Started - July 24, 2000. Last update - July 29, 2000 /// +/// Ver. 1.2. Started - July 30, 2000. Last update - July 31, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 26, 2000 /// +/// Ver. 1.5. Started - Aug 30, 2000. Last update - Aug 30, 2000 /// +/// Ver. 1.6. Started - Sep 11, 2000. Last update - Sep 15, 2000 /// +/// Ver. 1.7. Started - Sep 20, 2000. Last update - Sep 23, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// information about options and the cover +extern cinfo g_CoverInfo; + +// the look-up table for the number of 1's in unsigned short +extern unsigned char BitCount[]; + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +extern int GetDistance( Cube* pC1, Cube* pC2 ); +// distance computation for two cubes +extern int GetDistancePlus( Cube* pC1, Cube* pC2 ); + +extern void ExorVar( Cube* pC, int Var, varvalue Val ); + +extern void AddToFreeCubes( Cube* pC ); +// returns a simplified cube back into the free list + +//extern void PrintCube( ostream& DebugStream, Cube* pC ); +// debug output for cubes + +extern Cube* GetFreeCube(); + +//////////////////////////////////////////////////////////////////////// +/// ExorLink Functions +extern int ExorLinkCubeIteratorStart( Cube** pGroup, Cube* pC1, Cube* pC2, cubedist Dist ); +// this function starts the Exor-Link IteratorCubePair, which iterates +// through the cube groups starting from the group with min literals +// returns 1 on success, returns 0 if the cubes have wrong distance + +extern int ExorLinkCubeIteratorNext( Cube** pGroup ); +// give the next group in the decreasing order of sum of literals +// returns 1 on success, returns 0 if there are no more groups + +extern int ExorLinkCubeIteratorPick( Cube** pGroup, int g ); +// gives the group #g in the order in which the groups were given +// during iteration +// returns 1 on success, returns 0 if something g is too large + +extern void ExorLinkCubeIteratorCleanUp( int fTakeLastGroup ); +// removes the cubes from the store back into the list of free cubes +// if fTakeLastGroup is 0, removes all cubes +// if fTakeLastGroup is 1, does not store the last group + +//////////////////////////////////////////////////////////////////////// +/// FUNCTIONS OF THIS MODULE /// +//////////////////////////////////////////////////////////////////////// + +// iterative ExorLink +int IterativelyApplyExorLink2( char fDistEnable ); +int IterativelyApplyExorLink3( char fDistEnable ); +int IterativelyApplyExorLink4( char fDistEnable ); + +// function which performs distance computation and simplifes on the fly +// it is also called from the Pseudo-Kronecker module when cubes are added +int CheckForCloseCubes( Cube* p, int fAddCube ); +int CheckAndInsert( Cube* p ); + +// this function changes the cube back after it was DIST-1 transformed +void UndoRecentChanges(); + +//////////////////////////////////////////////////////////////////////// +// iterating through adjucency pair queques (with authomatic garbage collection) + +// start an iterator through cubes of dist CubeDist, +// the resulting pointers are written into ppC1 and ppC2 +int IteratorCubePairStart( cubedist Dist, Cube** ppC1, Cube** ppC2 ); +// gives the next VALID cube pair (the previous one is automatically dequequed) +int IteratorCubePairNext(); + +//////////////////////////////////////////////////////////////////////// +// the cube storage + +// cube storage allocation/delocation +int AllocateCubeSets( int nVarsIn, int nVarsOut ); +void DelocateCubeSets(); + +// insert/extract a cube into/from the storage +void CubeInsert( Cube* p ); +Cube* CubeExtract( Cube* p ); + +//////////////////////////////////////////////////////////////////////// +// Cube Set Iterator +Cube* IterCubeSetStart(); +// starts an iterator that traverses all the cubes in the ring +Cube* IterCubeSetNext(); +// returns the next cube in the ring +// to use it again after it has returned NULL, call IterCubeSetStart() first + +//////////////////////////////////////////////////////////////////////// +// cube adjacency queques + +// adjacency queque allocation/delocation procedures +int AllocateQueques( int nPlaces ); +void DelocateQueques(); + +// conditional adding cube pairs to queques +// reset temporarily stored new range of cube pairs +static void NewRangeReset(); +// add temporarily stored new range of cube pairs to the queque +static void NewRangeAdd(); +// insert one cube pair into the new range +static void NewRangeInsertCubePair( cubedist Dist, Cube* p1, Cube* p2 ); + +static void MarkSet(); +static void MarkRewind(); + +void PrintQuequeStats(); +int GetQuequeStats( cubedist Dist ); + +// iterating through the queque (with authomatic garbage collection) +// start an iterator through cubes of dist CubeDist, +// the resulting pointers are written into ppC1 and ppC2 +int IteratorCubePairStart( cubedist Dist, Cube** ppC1, Cube** ppC2 ); +// gives the next VALID cube pair (the previous one is automatically dequequed) +int IteratorCubePairNext(); + +//////////////////////////////////////////////////////////////////////// +/// EXPORTED VARIABLES /// +////////////////////////////////////////////////////////////////////////` + +// the number of allocated places +int s_nPosAlloc; +// the maximum number of occupied places +int s_nPosMax[3]; + +//////////////////////////////////////////////////////////////////////// +/// Minimization Strategy /// +//////////////////////////////////////////////////////////////////////// + +// 1) check that ExorLink for this cube pair can be performed +// (it may happen that the group is outdated due to recent reshaping) +// 2) find out what is the improvement achieved by each cube group +// 3) depending on the distance, do the following: +// a) if ( Dist == 2 ) +// try both cube groups, +// if one of them leads to improvement, take the cube group right away +// if none of them leads to improment +// - take the last one (because it reshapes) +// - take the last one only in case it does not increase literals +// b) if ( Dist == 3 ) +// try groups one by one +// if one of them leads to improvement, take the group right away +// if none of them leads to improvement +// - take the group which reshapes +// - take the reshaping group only in case it does not increase literals +// if none of them leads to reshaping, do not take any of them +// c) if ( Dist == 4 ) +// try groups one by one +// if one of the leads to reshaping, take it right away +// if none of them leads to reshaping, do not take any of them + +//////////////////////////////////////////////////////////////////////// +/// STATIC VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +// Cube set is a list of cubes +static Cube* s_List; + +/////////////////////////////////////////////////////////////////////////// +// undo information +/////////////////////////////////////////////////////////////////////////// +static struct +{ + int fInput; // 1 if the input was changed + Cube* p; // the pointer to the modified cube + int PrevQa; + int PrevPa; + int PrevQq; + int PrevPq; + int PrevPz; + int Var; // the number of variable that was changed + int Value; // the value what was there + int PrevID; // the previous ID of the removed cube +} s_ChangeStore; +/////////////////////////////////////////////////////////////////////////// + +// enable pair accumulation +// from the begginning (while the starting cover is generated) +// only the distance 2 accumulation is enabled +static int s_fDistEnable2 = 1; +static int s_fDistEnable3; +static int s_fDistEnable4; + +// temporary storage for cubes generated by the ExorLink iterator +static Cube* s_CubeGroup[5]; +// the marks telling whether the given cube is inserted +static int s_fInserted[5]; + +// enable selection only those Dist2 and Dist3 that do not increase literals +int s_fDecreaseLiterals = 0; + +// the counters for display +static int s_cEnquequed; +static int s_cAttempts; +static int s_cReshapes; + +// the number of cubes before ExorLink starts +static int s_nCubesBefore; +// the distance code specific for each ExorLink +static cubedist s_Dist; + +// other variables +static int s_Gain; +static int s_GainTotal; +static int s_GroupCounter; +static int s_GroupBest; +static Cube *s_pC1, *s_pC2; + +//////////////////////////////////////////////////////////////////////// +/// Iterative ExorLink Operation /// +//////////////////////////////////////////////////////////////////////// + +int CheckAndInsert( Cube* p ) +{ +// return CheckForCloseCubes( p, 1 ); + CubeInsert( p ); + return 0; +} + +int IterativelyApplyExorLink2( char fDistEnable ) +// MEMO: instead of inserting the cubes that have already been checked +// by running CheckForCloseCubes again, try inserting them without checking +// and observe the difference (it will save 50% of checking time) +{ + int z; + + // this var is specific to ExorLink-2 + s_Dist = (cubedist)0; + + // enable pair accumulation + s_fDistEnable2 = fDistEnable & 1; + s_fDistEnable3 = fDistEnable & 2; + s_fDistEnable4 = fDistEnable & 4; + + // initialize counters + s_cEnquequed = GetQuequeStats( s_Dist ); + s_cAttempts = 0; + s_cReshapes = 0; + + // remember the number of cubes before minimization + s_nCubesBefore = g_CoverInfo.nCubesInUse; + + for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() ) + { + s_cAttempts++; + // start ExorLink of the given Distance + if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) ) + { + // extract old cubes from storage (to prevent EXORing with their derivitives) + CubeExtract( s_pC1 ); + CubeExtract( s_pC2 ); + + // mark the current position in the cube pair queques + MarkSet(); + + // check the first group (generated by ExorLinkCubeIteratorStart()) + if ( CheckForCloseCubes( s_CubeGroup[0], 0 ) ) + { // the first cube leads to improvement - it is already inserted + CheckForCloseCubes( s_CubeGroup[1], 1 ); // insert the second cube + goto SUCCESS; + } + if ( CheckForCloseCubes( s_CubeGroup[1], 0 ) ) + { // the second cube leads to improvement - it is already inserted + CheckForCloseCubes( s_CubeGroup[0], 1 ); // insert the first cube +// CheckAndInsert( s_CubeGroup[0] ); + goto SUCCESS; + } + // the first group does not lead to improvement + + // rewind to the previously marked position in the cube pair queques + MarkRewind(); + + // generate the second group + ExorLinkCubeIteratorNext( s_CubeGroup ); + + // check the second group + if ( CheckForCloseCubes( s_CubeGroup[0], 0 ) ) + { // the first cube leads to improvement - it is already inserted + CheckForCloseCubes( s_CubeGroup[1], 1 ); // insert the second cube + goto SUCCESS; + } + if ( CheckForCloseCubes( s_CubeGroup[1], 0 ) ) + { // the second cube leads to improvement - it is already inserted + CheckForCloseCubes( s_CubeGroup[0], 1 ); // insert the first cube +// CheckAndInsert( s_CubeGroup[0] ); + goto SUCCESS; + } + // the second group does not lead to improvement + + // decide whether to accept the second group, depending on literals + if ( s_fDecreaseLiterals ) + { + if ( g_CoverInfo.fUseQCost ? + s_CubeGroup[0]->q + s_CubeGroup[1]->q >= s_pC1->q + s_pC2->q : + s_CubeGroup[0]->a + s_CubeGroup[1]->a >= s_pC1->a + s_pC2->a ) + // the group increases literals + { // do not take the last group + + // rewind to the previously marked position in the cube pair queques + MarkRewind(); + + // return the old cubes back to storage + CubeInsert( s_pC1 ); + CubeInsert( s_pC2 ); + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 0 ); + continue; + } + } + + // take the last group + // there is no need to test these cubes again, + // because they have been tested and did not yield any improvement + CubeInsert( s_CubeGroup[0] ); + CubeInsert( s_CubeGroup[1] ); +// CheckForCloseCubes( s_CubeGroup[0], 1 ); +// CheckForCloseCubes( s_CubeGroup[1], 1 ); + +SUCCESS: + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 1 ); // take the last group + // free old cubes + AddToFreeCubes( s_pC1 ); + AddToFreeCubes( s_pC2 ); + // increate the counter + s_cReshapes++; + } + } + // print the report + if ( g_CoverInfo.Verbosity == 2 ) + { + printf( "ExLink-%d", 2 ); + printf( ": Que= %5d", s_cEnquequed ); + printf( " Att= %4d", s_cAttempts ); + printf( " Resh= %4d", s_cReshapes ); + printf( " NoResh= %4d", s_cAttempts - s_cReshapes ); + printf( " Cubes= %3d", g_CoverInfo.nCubesInUse ); + printf( " (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse ); + printf( " Lits= %5d", CountLiterals() ); + printf( " QCost = %6d", CountQCost() ); + printf( "\n" ); + } + + // return the number of cubes gained in the process + return s_nCubesBefore - g_CoverInfo.nCubesInUse; +} + +int IterativelyApplyExorLink3( char fDistEnable ) +{ + int z, c, d; + // this var is specific to ExorLink-3 + s_Dist = (cubedist)1; + + // enable pair accumulation + s_fDistEnable2 = fDistEnable & 1; + s_fDistEnable3 = fDistEnable & 2; + s_fDistEnable4 = fDistEnable & 4; + + // initialize counters + s_cEnquequed = GetQuequeStats( s_Dist ); + s_cAttempts = 0; + s_cReshapes = 0; + + // remember the number of cubes before minimization + s_nCubesBefore = g_CoverInfo.nCubesInUse; + + for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() ) + { + s_cAttempts++; + // start ExorLink of the given Distance + if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) ) + { + // extract old cubes from storage (to prevent EXORing with their derivitives) + CubeExtract( s_pC1 ); + CubeExtract( s_pC2 ); + + // mark the current position in the cube pair queques + MarkSet(); + + // check cube groups one by one + s_GroupCounter = 0; + do + { // check the cubes of this group one by one + for ( c = 0; c < 3; c++ ) + if ( !s_CubeGroup[c]->fMark ) // this cube has not yet been checked + { + s_Gain = CheckForCloseCubes( s_CubeGroup[c], 0 ); // do not insert the cube, by default + if ( s_Gain ) + { // this cube leads to improvement or reshaping - it is already inserted + + // decide whether to accept this group based on literal count + if ( s_fDecreaseLiterals && s_Gain == 1 ) + if ( g_CoverInfo.fUseQCost ? + s_CubeGroup[0]->q + s_CubeGroup[1]->q + s_CubeGroup[2]->q > s_pC1->q + s_pC2->q + s_ChangeStore.PrevQq : + s_CubeGroup[0]->a + s_CubeGroup[1]->a + s_CubeGroup[2]->a > s_pC1->a + s_pC2->a + s_ChangeStore.PrevQa + ) // the group increases literals + { // do not take this group + // remember the group + s_GroupBest = s_GroupCounter; + // undo changes to be able to continue checking other groups + UndoRecentChanges(); + break; + } + + // take this group + for ( d = 0; d < 3; d++ ) // insert other cubes + if ( d != c ) + { + CheckForCloseCubes( s_CubeGroup[d], 1 ); +// if ( s_CubeGroup[d]->fMark ) +// CheckAndInsert( s_CubeGroup[d] ); +// CheckOnlyOneCube( s_CubeGroup[d] ); +// CheckForCloseCubes( s_CubeGroup[d], 1 ); +// else +// CheckForCloseCubes( s_CubeGroup[d], 1 ); + } + + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 1 ); // take the last group + // free old cubes + AddToFreeCubes( s_pC1 ); + AddToFreeCubes( s_pC2 ); + // update the counter + s_cReshapes++; + goto END_OF_LOOP; + } + else // mark the cube as checked + s_CubeGroup[c]->fMark = 1; + } + // the group is not taken - find the new group + s_GroupCounter++; + + // rewind to the previously marked position in the cube pair queques + MarkRewind(); + } + while ( ExorLinkCubeIteratorNext( s_CubeGroup ) ); + // none of the groups leads to improvement + + // return the old cubes back to storage + CubeInsert( s_pC1 ); + CubeInsert( s_pC2 ); + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 0 ); + } +END_OF_LOOP: {} + } + + // print the report + if ( g_CoverInfo.Verbosity == 2 ) + { + printf( "ExLink-%d", 3 ); + printf( ": Que= %5d", s_cEnquequed ); + printf( " Att= %4d", s_cAttempts ); + printf( " Resh= %4d", s_cReshapes ); + printf( " NoResh= %4d", s_cAttempts - s_cReshapes ); + printf( " Cubes= %3d", g_CoverInfo.nCubesInUse ); + printf( " (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse ); + printf( " Lits= %5d", CountLiterals() ); + printf( " QCost = %6d", CountQCost() ); + printf( "\n" ); + } + + // return the number of cubes gained in the process + return s_nCubesBefore - g_CoverInfo.nCubesInUse; +} + +int IterativelyApplyExorLink4( char fDistEnable ) +{ + int z, c; + // this var is specific to ExorLink-4 + s_Dist = (cubedist)2; + + // enable pair accumulation + s_fDistEnable2 = fDistEnable & 1; + s_fDistEnable3 = fDistEnable & 2; + s_fDistEnable4 = fDistEnable & 4; + + // initialize counters + s_cEnquequed = GetQuequeStats( s_Dist ); + s_cAttempts = 0; + s_cReshapes = 0; + + // remember the number of cubes before minimization + s_nCubesBefore = g_CoverInfo.nCubesInUse; + + for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() ) + { + s_cAttempts++; + // start ExorLink of the given Distance + if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) ) + { + // extract old cubes from storage (to prevent EXORing with their derivitives) + CubeExtract( s_pC1 ); + CubeExtract( s_pC2 ); + + // mark the current position in the cube pair queques + MarkSet(); + + // check cube groups one by one + do + { // check the cubes of this group one by one + s_GainTotal = 0; + for ( c = 0; c < 4; c++ ) + if ( !s_CubeGroup[c]->fMark ) // this cube has not yet been checked + { + s_Gain = CheckForCloseCubes( s_CubeGroup[c], 0 ); // do not insert the cube, by default + // if the cube leads to gain, it is already inserted + s_fInserted[c] = (int)(s_Gain>0); + // increment the total gain + s_GainTotal += s_Gain; + } + else + s_fInserted[c] = 0; // the cube has already been checked - it is not inserted + + if ( s_GainTotal == 0 ) // the group does not lead to any gain + { // mark the cubes + for ( c = 0; c < 4; c++ ) + s_CubeGroup[c]->fMark = 1; + } + else if ( s_GainTotal == 1 ) // the group does not lead to substantial gain, too + { + // undo changes to be able to continue checking groups + UndoRecentChanges(); + // mark those cubes that were not inserted + for ( c = 0; c < 4; c++ ) + s_CubeGroup[c]->fMark = !s_fInserted[c]; + } + else // if ( s_GainTotal > 1 ) // the group reshapes or improves + { // accept the group + for ( c = 0; c < 4; c++ ) // insert other cubes + if ( !s_fInserted[c] ) + CheckForCloseCubes( s_CubeGroup[c], 1 ); +// CheckAndInsert( s_CubeGroup[c] ); + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 1 ); // take the last group + // free old cubes + AddToFreeCubes( s_pC1 ); + AddToFreeCubes( s_pC2 ); + // update the counter + s_cReshapes++; + goto END_OF_LOOP; + } + + // rewind to the previously marked position in the cube pair queques + MarkRewind(); + } + while ( ExorLinkCubeIteratorNext( s_CubeGroup ) ); + // none of the groups leads to improvement + + // return the old cubes back to storage + CubeInsert( s_pC1 ); + CubeInsert( s_pC2 ); + // clean the results of generating ExorLinked cubes + ExorLinkCubeIteratorCleanUp( 0 ); + } +END_OF_LOOP: {} + } + + // print the report + if ( g_CoverInfo.Verbosity == 2 ) + { + printf( "ExLink-%d", 4 ); + printf( ": Que= %5d", s_cEnquequed ); + printf( " Att= %4d", s_cAttempts ); + printf( " Resh= %4d", s_cReshapes ); + printf( " NoResh= %4d", s_cAttempts - s_cReshapes ); + printf( " Cubes= %3d", g_CoverInfo.nCubesInUse ); + printf( " (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse ); + printf( " Lits= %5d", CountLiterals() ); + printf( " QCost = %6d", CountQCost() ); + printf( "\n" ); + } + + // return the number of cubes gained in the process + return s_nCubesBefore - g_CoverInfo.nCubesInUse; +} + +// local static variables +Cube* s_q; +int s_Distance; +int s_DiffVarNum; +int s_DiffVarValueP_old; +int s_DiffVarValueP_new; +int s_DiffVarValueQ; + +int CheckForCloseCubes( Cube* p, int fAddCube ) +// checks the cube storage for a cube that is dist-0 and dist-1 removed +// from the given one (p) if such a cube is found, extracts it from the data +// structure, EXORs it with the given cube, adds the resultant cube +// to the data structure and performed the same check for the resultant cube; +// returns the number of cubes gained in the process of reduction; +// if an adjacent cube is not found, inserts the cube only if (fAddCube==1)!!! +{ + // start the new range + NewRangeReset(); + + for ( s_q = s_List; s_q; s_q = s_q->Next ) + { + s_Distance = GetDistancePlus( p, s_q ); + if ( s_Distance > 4 ) + { + } + else if ( s_Distance == 4 ) + { + if ( s_fDistEnable4 ) + NewRangeInsertCubePair( DIST4, p, s_q ); + } + else if ( s_Distance == 3 ) + { + if ( s_fDistEnable3 ) + NewRangeInsertCubePair( DIST3, p, s_q ); + } + else if ( s_Distance == 2 ) + { + if ( s_fDistEnable2 ) + NewRangeInsertCubePair( DIST2, p, s_q ); + } + else if ( s_Distance == 1 ) + { // extract the cube from the data structure + + ////////////////////////////////////////////////////////// + // store the changes + s_ChangeStore.fInput = (s_DiffVarNum != -1); + s_ChangeStore.p = p; + s_ChangeStore.PrevQa = s_q->a; + s_ChangeStore.PrevPa = p->a; + s_ChangeStore.PrevQq = s_q->q; + s_ChangeStore.PrevPq = p->q; + s_ChangeStore.PrevPz = p->z; + s_ChangeStore.Var = s_DiffVarNum; + s_ChangeStore.Value = s_DiffVarValueQ; + s_ChangeStore.PrevID = s_q->ID; + ////////////////////////////////////////////////////////// + + CubeExtract( s_q ); + // perform the EXOR of the two cubes and write the result into p + + // it is important that the resultant cube is written into p!!! + + if ( s_DiffVarNum == -1 ) + { + int i; + // exor the output part + p->z = 0; + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + { + p->pCubeDataOut[i] ^= s_q->pCubeDataOut[i]; + p->z += BIT_COUNT(p->pCubeDataOut[i]); + } + } + else + { + // the cube has already been updated by GetDistancePlus() + + // modify the parameters of the number of literals in the new cube +// p->a += s_UpdateLiterals[ s_DiffVarValueP ][ s_DiffVarValueQ ]; + if ( s_DiffVarValueP_old == VAR_NEG || s_DiffVarValueP_old == VAR_POS ) + p->a--; + if ( s_DiffVarValueP_new == VAR_NEG || s_DiffVarValueP_new == VAR_POS ) + p->a++; + p->q = ComputeQCostBits(p); + } + + // move q to the free cube list + AddToFreeCubes( s_q ); + + // make sure that nobody with use the pairs created so far +// NewRangeReset(); + // call the function again for the new cube + return 1 + CheckForCloseCubes( p, 1 ); + } + else // if ( Distance == 0 ) + { // extract the second cube from the data structure and add them both to the free list + AddToFreeCubes( p ); + AddToFreeCubes( CubeExtract( s_q ) ); + + // make sure that nobody with use the pairs created so far + NewRangeReset(); + return 2; + } + } + + // add the cube to the data structure if needed + if ( fAddCube ) + CubeInsert( p ); + + // add temporarily stored new range of cube pairs to the queque + NewRangeAdd(); + + return 0; +} + +void UndoRecentChanges() +{ + Cube * p, * q; + // get back cube q that was deleted + q = GetFreeCube(); + // restore the ID + q->ID = s_ChangeStore.PrevID; + // insert the cube into storage again + CubeInsert( q ); + + // extract cube p + p = CubeExtract( s_ChangeStore.p ); + + // modify it back + if ( s_ChangeStore.fInput ) // the input has changed + { + ExorVar( p, s_ChangeStore.Var, (varvalue)s_ChangeStore.Value ); + p->a = s_ChangeStore.PrevPa; + p->q = s_ChangeStore.PrevPq; + // p->z did not change + } + else // if ( s_ChangeStore.fInput ) // the output has changed + { + int i; + for ( i = 0; i < g_CoverInfo.nWordsOut; i++ ) + p->pCubeDataOut[i] ^= q->pCubeDataOut[i]; + p->z = s_ChangeStore.PrevPz; + // p->a did not change + } +} + +/////////////////////////////////////////////////////////////////// +/// CUBE SET MANIPULATION PROCEDURES /// +/////////////////////////////////////////////////////////////////// + +// Cube set is a list of cubes +//static Cube* s_List; + +/////////////////////////////////////////////////////////////////// +/// Memory Allocation/Delocation /// +/////////////////////////////////////////////////////////////////// + +int AllocateCubeSets( int nVarsIn, int nVarsOut ) +{ + s_List = NULL; + + // clean other data + s_fDistEnable2 = 1; + s_fDistEnable3 = 0; + s_fDistEnable4 = 0; + memset( s_CubeGroup, 0, sizeof(void *) * 5 ); + memset( s_fInserted, 0, sizeof(int) * 5 ); + s_fDecreaseLiterals = 0; + s_cEnquequed = 0; + s_cAttempts = 0; + s_cReshapes = 0; + s_nCubesBefore = 0; + s_Gain = 0; + s_GainTotal = 0; + s_GroupCounter = 0; + s_GroupBest = 0; + s_pC1 = s_pC2 = NULL; + + return 4; +} + +void DelocateCubeSets() +{ +} + +/////////////////////////////////////////////////////////////////// +/// Insertion Operators /// +/////////////////////////////////////////////////////////////////// + +void CubeInsert( Cube* p ) +// inserts the cube into storage (puts it at the beginning of the list) +{ + assert( p->Prev == NULL && p->Next == NULL ); + assert( p->ID ); + + if ( s_List == NULL ) + s_List = p; + else + { + p->Next = s_List; + + s_List->Prev = p; + s_List = p; + } + + g_CoverInfo.nCubesInUse++; +} + +Cube* CubeExtract( Cube* p ) +// extracts the cube from storage +{ +// assert( p->Prev && p->Next ); // can be done only with rings + assert( p->ID ); + +// if ( s_List == p ) +// s_List = p->Next; +// if ( p->Prev ) +// p->Prev->Next = p->Next; + + if ( s_List == p ) + s_List = p->Next; + else + p->Prev->Next = p->Next; + + if ( p->Next ) + p->Next->Prev = p->Prev; + + p->Prev = NULL; + p->Next = NULL; + + g_CoverInfo.nCubesInUse--; + return p; +} + +/////////////////////////////////////////////////////////////////// +/// CUBE ITERATOR /// +/////////////////////////////////////////////////////////////////// + +// the iterator starts from the Head and stops when it sees NULL +Cube* s_pCubeLast; + +/////////////////////////////////////////////////////////////////// +/// Cube Set Iterator /// +/////////////////////////////////////////////////////////////////// + +Cube* IterCubeSetStart() +// starts an iterator that traverses all the cubes in the ring +{ + assert( s_pCubeLast == NULL ); + + // check whether the List has cubes + if ( s_List == NULL ) + return NULL; + + return ( s_pCubeLast = s_List ); +} + +Cube* IterCubeSetNext() +// returns the next cube in the cube set +// to use it again after it has returned NULL, first call IterCubeSetStart() +{ + assert( s_pCubeLast ); + return ( s_pCubeLast = s_pCubeLast->Next ); +} + +/////////////////////////////////////////////////////////////////// +//// ADJACENCY QUEQUES ////// +/////////////////////////////////////////////////////////////////// + +typedef struct +{ + Cube** pC1; // the pointer to the first cube + Cube** pC2; // the pointer to the second cube + byte* ID1; // the ID of the first cube + byte* ID2; // the ID of the second cube + int PosOut; // extract position + int PosIn; // insert position + int PosCur; // temporary insert position + int PosMark; // the marked position + int fEmpty; // this flag is 1 if there is nothing in the queque +} que; + +static que s_Que[3]; // Dist-2, Dist-3, Dist-4 queques + +// the number of allocated places +//int s_nPosAlloc; +// the maximum number of occupied places +//int s_nPosMax[3]; + +////////////////////////////////////////////////////////////////////// +// Conditional Adding Cube Pairs To Queques // +////////////////////////////////////////////////////////////////////// + +int GetPosDiff( int PosBeg, int PosEnd ) +{ + return (PosEnd - PosBeg + s_nPosAlloc) % s_nPosAlloc; +} + +void MarkSet() +// sets marks in the cube pair queques +{ + s_Que[0].PosMark = s_Que[0].PosIn; + s_Que[1].PosMark = s_Que[1].PosIn; + s_Que[2].PosMark = s_Que[2].PosIn; +} + +void MarkRewind() +// rewinds the queques to the previously set marks +{ + s_Que[0].PosIn = s_Que[0].PosMark; + s_Que[1].PosIn = s_Que[1].PosMark; + s_Que[2].PosIn = s_Que[2].PosMark; +} + +void NewRangeReset() +// resets temporarily stored new range of cube pairs +{ + s_Que[0].PosCur = s_Que[0].PosIn; + s_Que[1].PosCur = s_Que[1].PosIn; + s_Que[2].PosCur = s_Que[2].PosIn; +} + +void NewRangeAdd() +// adds temporarily stored new range of cube pairs to the queque +{ + s_Que[0].PosIn = s_Que[0].PosCur; + s_Que[1].PosIn = s_Que[1].PosCur; + s_Que[2].PosIn = s_Que[2].PosCur; +} + +void NewRangeInsertCubePair( cubedist Dist, Cube* p1, Cube* p2 ) +// insert one cube pair into the new range +{ + que* p = &s_Que[Dist]; + int Pos = p->PosCur; + + if ( p->fEmpty || Pos != p->PosOut ) + { + p->pC1[Pos] = p1; + p->pC2[Pos] = p2; + p->ID1[Pos] = p1->ID; + p->ID2[Pos] = p2->ID; + + p->PosCur = (p->PosCur+1)%s_nPosAlloc; + } + else + assert(0); +// cout << endl << "DIST-" << (int)(Dist+2) << ": Have run out of queque space!" << endl; +} + +void PrintQuequeStats() +{ +/* + cout << endl << "Queque statistics: "; + cout << " Alloc = " << s_nPosAlloc; + cout << " DIST2 = " << GetPosDiff( s_Que[0].PosOut, s_Que[0].PosIn ); + cout << " DIST3 = " << GetPosDiff( s_Que[1].PosOut, s_Que[1].PosIn ); + cout << " DIST4 = " << GetPosDiff( s_Que[2].PosOut, s_Que[2].PosIn ); + cout << endl; + cout << endl; +*/ +} + +int GetQuequeStats( cubedist Dist ) +{ + return GetPosDiff( s_Que[Dist].PosOut, s_Que[Dist].PosIn ); +} + +////////////////////////////////////////////////////////////////////// +// Queque Iterators // +////////////////////////////////////////////////////////////////////// + +// iterating through the queque (with authomatic garbage collection) +// only one iterator can be active at a time +static struct +{ + int fStarted; // status of the iterator (1 if working) + cubedist Dist; // the currently iterated queque + Cube** ppC1; // the position where the first cube pointer goes + Cube** ppC2; // the position where the second cube pointer goes + int PosStop; // the stop position (to prevent the iterator from + // choking when new pairs are added during iteration) + int CutValue; // the number of literals below which the cubes are not used +} s_Iter; + +static que* pQ; +static Cube *p1, *p2; + +int IteratorCubePairStart( cubedist CubeDist, Cube** ppC1, Cube** ppC2 ) +// start an iterator through cubes of dist CubeDist, +// the resulting pointers are written into ppC1 and ppC2 +// returns 1 if the first cube pair is found +{ + int fEntryFound; + + assert( s_Iter.fStarted == 0 ); + assert( CubeDist >= 0 && CubeDist <= 2 ); + + s_Iter.fStarted = 1; + s_Iter.Dist = CubeDist; + s_Iter.ppC1 = ppC1; + s_Iter.ppC2 = ppC2; + + s_Iter.PosStop = s_Que[ CubeDist ].PosIn; + + // determine the cut value +// s_Iter.CutValue = s_nLiteralsInUse/s_nCubesInUse/2; + s_Iter.CutValue = -1; + + fEntryFound = 0; + // go through the entries while there is something in the queque + for ( pQ = &s_Que[ CubeDist ]; pQ->PosOut != s_Iter.PosStop; pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc ) + { + p1 = pQ->pC1[ pQ->PosOut ]; + p2 = pQ->pC2[ pQ->PosOut ]; + + // check whether the entry is valid + if ( p1->ID == pQ->ID1[ pQ->PosOut ] && + p2->ID == pQ->ID2[ pQ->PosOut ] ) //&& + //p1->x + p1->y + p2->x + p2->y > s_Iter.CutValue ) + { + fEntryFound = 1; + break; + } + } + + if ( fEntryFound ) + { // write the result into the pick-up place + *ppC1 = pQ->pC1[ pQ->PosOut ]; + *ppC2 = pQ->pC2[ pQ->PosOut ]; + + pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc; + } + else + s_Iter.fStarted = 0; + return fEntryFound; +} + +int IteratorCubePairNext() +// gives the next VALID cube pair (the previous one is automatically dequequed) +{ + int fEntryFound = 0; + assert( s_Iter.fStarted ); + + // go through the entries while there is something in the queque + for ( pQ = &s_Que[ s_Iter.Dist ]; pQ->PosOut != s_Iter.PosStop; pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc ) + { + p1 = pQ->pC1[ pQ->PosOut ]; + p2 = pQ->pC2[ pQ->PosOut ]; + + // check whether the entry is valid + if ( p1->ID == pQ->ID1[ pQ->PosOut ] && + p2->ID == pQ->ID2[ pQ->PosOut ] ) //&& + //p1->x + p1->y + p2->x + p2->y > s_Iter.CutValue ) + { + fEntryFound = 1; + break; + } + } + + if ( fEntryFound ) + { // write the result into the pick-up place + *(s_Iter.ppC1) = pQ->pC1[ pQ->PosOut ]; + *(s_Iter.ppC2) = pQ->pC2[ pQ->PosOut ]; + + pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc; + } + else // iteration has finished + s_Iter.fStarted = 0; + + return fEntryFound; +} + +////////////////////////////////////////////////////////////////////// +// Allocation/Delocation // +////////////////////////////////////////////////////////////////////// + +int AllocateQueques( int nPlaces ) +// nPlaces should be approximately nCubes*nCubes/10 +// allocates memory for cube pair queques +{ + int i; + s_nPosAlloc = nPlaces; + + for ( i = 0; i < 3; i++ ) + { + // clean data + memset( &s_Que[i], 0, sizeof(que) ); + + s_Que[i].pC1 = (Cube**) ABC_ALLOC( Cube*, nPlaces ); + s_Que[i].pC2 = (Cube**) ABC_ALLOC( Cube*, nPlaces ); + s_Que[i].ID1 = (byte*) ABC_ALLOC( byte, nPlaces ); + s_Que[i].ID2 = (byte*) ABC_ALLOC( byte, nPlaces ); + + if ( s_Que[i].pC1==NULL || s_Que[i].pC2==NULL || s_Que[i].ID1==NULL || s_Que[i].ID2==NULL ) + return 0; + + s_nPosMax[i] = 0; + s_Que[i].fEmpty = 1; + } + + return nPlaces * (sizeof(Cube*) + sizeof(Cube*) + 2*sizeof(byte) ); +} + +void DelocateQueques() +{ + int i; + for ( i = 0; i < 3; i++ ) + { + ABC_FREE( s_Que[i].pC1 ); + ABC_FREE( s_Que[i].pC2 ); + ABC_FREE( s_Que[i].ID1 ); + ABC_FREE( s_Que[i].ID2 ); + } +} + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} diff --git a/lib/abcesop/exorUtil.cpp b/lib/abcesop/exorUtil.cpp new file mode 100644 index 0000000..2309e0b --- /dev/null +++ b/lib/abcesop/exorUtil.cpp @@ -0,0 +1,228 @@ +/**CFile**************************************************************** + + FileName [exorUtil.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Exclusive sum-of-product minimization.] + + Synopsis [Utilities.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: exorUtil.c,v 1.0 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +//////////////////////////////////////////////////////////////////////// +/// /// +/// Implementation of EXORCISM - 4 /// +/// An Exclusive Sum-of-Product Minimizer /// +/// Alan Mishchenko /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// /// +/// Utility Functions /// +/// /// +/// 1) allocating memory for and creating the ESOP cover /// +/// 2) writing the resultant cover into an ESOP PLA file /// +/// /// +/// Ver. 1.0. Started - July 15, 2000. Last update - July 20, 2000 /// +/// Ver. 1.4. Started - Aug 10, 2000. Last update - Aug 10, 2000 /// +/// Ver. 1.5. Started - Aug 19, 2000. Last update - Aug 19, 2000 /// +/// Ver. 1.7. Started - Sep 20, 2000. Last update - Sep 23, 2000 /// +/// /// +//////////////////////////////////////////////////////////////////////// +/// This software was tested with the BDD package "CUDD", v.2.3.0 /// +/// by Fabio Somenzi /// +/// http://vlsi.colorado.edu/~fabio/ /// +//////////////////////////////////////////////////////////////////////// + +#include "eabc/exor.h" + +namespace abc::exorcism { + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL VARIABLES //// +//////////////////////////////////////////////////////////////////////// + +// information about the options, the function, and the cover +extern cinfo g_CoverInfo; + +//////////////////////////////////////////////////////////////////////// +/// EXTERNAL FUNCTIONS /// +//////////////////////////////////////////////////////////////////////// + +// Cube Cover Iterator +// starts an iterator that traverses all the cubes in the ring +extern Cube* IterCubeSetStart(); +// returns the next cube in the ring +extern Cube* IterCubeSetNext(); + +// retrieves the variable from the cube +extern varvalue GetVar( Cube* pC, int Var ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////// +//////////// Cover Service Procedures ///////////////// +/////////////////////////////////////////////////////////////////// + +int CountLiterals() +{ + Cube* p; + int LitCounter = 0; + for ( p = IterCubeSetStart( ); p; p = IterCubeSetNext() ) + LitCounter += p->a; + return LitCounter; +} + +int CountLiteralsCheck() +{ + Cube* p; + int Value, v; + int LitCounter = 0; + int LitCounterControl = 0; + + for ( p = IterCubeSetStart( ); p; p = IterCubeSetNext() ) + { + LitCounterControl += p->a; + + assert( p->fMark == 0 ); + + // write the input variables + for ( v = 0; v < g_CoverInfo.nVarsIn; v++ ) + { + Value = GetVar( p, v ); + if ( Value == VAR_NEG ) + LitCounter++; + else if ( Value == VAR_POS ) + LitCounter++; + else if ( Value != VAR_ABS ) + { + assert(0); + } + } + } + + if ( LitCounterControl != LitCounter ) + printf( "Warning! The recorded number of literals (%d) differs from the actual number (%d)\n", LitCounterControl, LitCounter ); + return LitCounter; +} + +int CountQCost() +{ + Cube* p; + int QCost = 0; + int QCostControl = 0; + for ( p = IterCubeSetStart( ); p; p = IterCubeSetNext() ) + { + QCostControl += p->q; + QCost += ComputeQCostBits( p ); + } +// if ( QCostControl != QCost ) +// printf( "Warning! The recorded number of literals (%d) differs from the actual number (%d)\n", QCostControl, QCost ); + return QCost; +} + + +void WriteTableIntoFile( FILE * pFile ) +// nCubesAlloc is the number of allocated cubes +{ + int v, w; + Cube * p; + int cOutputs; + int nOutput; + int WordSize; + + for ( p = IterCubeSetStart( ); p; p = IterCubeSetNext() ) + { + assert( p->fMark == 0 ); + + // write the input variables + for ( v = 0; v < g_CoverInfo.nVarsIn; v++ ) + { + int Value = GetVar( p, v ); + if ( Value == VAR_NEG ) + fprintf( pFile, "0" ); + else if ( Value == VAR_POS ) + fprintf( pFile, "1" ); + else if ( Value == VAR_ABS ) + fprintf( pFile, "-" ); + else + assert(0); + } + fprintf( pFile, " " ); + + // write the output variables + cOutputs = 0; + nOutput = g_CoverInfo.nVarsOut; + WordSize = 8*sizeof( unsigned ); + for ( w = 0; w < g_CoverInfo.nWordsOut; w++ ) + for ( v = 0; v < WordSize; v++ ) + { + if ( p->pCubeDataOut[w] & (1< +{ + FILE * pFile; + time_t ltime; + char * TimeStr; + + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + fprintf( pFile, "\n\nCannot open the output file\n" ); + return 1; + } + + // get current time + time( <ime ); + TimeStr = asctime( localtime( <ime ) ); + // get the number of literals + g_CoverInfo.nLiteralsAfter = CountLiteralsCheck(); + g_CoverInfo.QCostAfter = CountQCost(); + fprintf( pFile, "# EXORCISM-4 output for command line arguments: " ); + fprintf( pFile, "\"-Q %d -V %d\"\n", g_CoverInfo.Quality, g_CoverInfo.Verbosity ); + fprintf( pFile, "# Minimization performed %s", TimeStr ); + fprintf( pFile, "# Initial statistics: " ); + fprintf( pFile, "Cubes = %d Literals = %d QCost = %d\n", g_CoverInfo.nCubesBefore, g_CoverInfo.nLiteralsBefore, g_CoverInfo.QCostBefore ); + fprintf( pFile, "# Final statistics: " ); + fprintf( pFile, "Cubes = %d Literals = %d QCost = %d\n", g_CoverInfo.nCubesInUse, g_CoverInfo.nLiteralsAfter, g_CoverInfo.QCostAfter ); + fprintf( pFile, "# File reading and reordering time = %.2f sec\n", TICKS_TO_SECONDS(g_CoverInfo.TimeRead) ); + fprintf( pFile, "# Starting cover generation time = %.2f sec\n", TICKS_TO_SECONDS(g_CoverInfo.TimeStart) ); + fprintf( pFile, "# Pure ESOP minimization time = %.2f sec\n", TICKS_TO_SECONDS(g_CoverInfo.TimeMin) ); + fprintf( pFile, ".i %d\n", g_CoverInfo.nVarsIn ); + fprintf( pFile, ".o %d\n", g_CoverInfo.nVarsOut ); + fprintf( pFile, ".p %d\n", g_CoverInfo.nCubesInUse ); + fprintf( pFile, ".type esop\n" ); + WriteTableIntoFile( pFile ); + fprintf( pFile, ".e\n" ); + fclose( pFile ); + return 0; +} + +/////////////////////////////////////////////////////////////////// +//////////// End of File ///////////////// +/////////////////////////////////////////////////////////////////// + + +} + diff --git a/lib/abcsat/AbcGlucose.cpp b/lib/abcsat/AbcGlucose.cpp new file mode 100644 index 0000000..d1c55fd --- /dev/null +++ b/lib/abcsat/AbcGlucose.cpp @@ -0,0 +1,596 @@ +/**CFile**************************************************************** + + FileName [AbcGlucose.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver Glucose 3.0 by Gilles Audemard and Laurent Simon.] + + Synopsis [Interface to Glucose.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 6, 2017.] + + Revision [$Id: AbcGlucose.cpp,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc/system.h" +#include "abc/Dimacs.h" +#include "abc/SimpSolver.h" + +#include "abc/AbcGlucose.h" +#include "abc/abc_global.h" + +//#include "base/abc/abc.h" +//#include "sat/cnf/cnf.h" +//#include "misc/extra/extra.h" + +ABC_NAMESPACE_IMPL_START + +using namespace Gluco; + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define USE_SIMP_SOLVER 1 + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef USE_SIMP_SOLVER + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gluco::SimpSolver * glucose_solver_start() +{ + SimpSolver * S = new SimpSolver; + S->setIncrementalMode(); + return S; +} + +void glucose_solver_stop(Gluco::SimpSolver* S) +{ + delete S; +} + +void glucose_solver_reset(Gluco::SimpSolver* S) +{ + S->reset(); +} + +int glucose_solver_addclause(Gluco::SimpSolver* S, int * plits, int nlits) +{ + vec lits; + for ( int i = 0; i < nlits; i++,plits++) + { + // note: Glucose uses the same var->lit conventiaon as ABC + while ((*plits)/2 >= S->nVars()) S->newVar(); + assert((*plits)/2 < S->nVars()); // NOTE: since we explicitely use new function bmc_add_var + Lit p; + p.x = *plits; + lits.push(p); + } + return S->addClause(lits); // returns 0 if the problem is UNSAT +} + +void glucose_solver_setcallback(Gluco::SimpSolver* S, void * pman, int(*pfunc)(void*, int, int*)) +{ + S->pCnfMan = pman; + S->pCnfFunc = pfunc; + S->nCallConfl = 1000; +} + +int glucose_solver_solve(Gluco::SimpSolver* S, int * plits, int nlits) +{ + vec lits; + for (int i=0;isolveLimited(lits, 0); + return (res == l_True ? 1 : res == l_False ? -1 : 0); +} + +int glucose_solver_addvar(Gluco::SimpSolver* S) +{ + S->newVar(); + return S->nVars() - 1; +} + +int glucose_solver_read_cex_varvalue(Gluco::SimpSolver* S, int ivar) +{ + return S->model[ivar] == l_True; +} + +void glucose_solver_setstop(Gluco::SimpSolver* S, int * pstop) +{ + S->pstop = pstop; +} + + +/**Function************************************************************* + + Synopsis [Wrapper APIs to calling from ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bmcg_sat_solver * bmcg_sat_solver_start() +{ + return (bmcg_sat_solver *)glucose_solver_start(); +} +void bmcg_sat_solver_stop(bmcg_sat_solver* s) +{ + glucose_solver_stop((Gluco::SimpSolver*)s); +} +void bmcg_sat_solver_reset(bmcg_sat_solver* s) +{ + glucose_solver_reset((Gluco::SimpSolver*)s); +} + + +int bmcg_sat_solver_addclause(bmcg_sat_solver* s, int * plits, int nlits) +{ + return glucose_solver_addclause((Gluco::SimpSolver*)s,plits,nlits); +} + +void bmcg_sat_solver_setcallback(bmcg_sat_solver* s, void * pman, int(*pfunc)(void*, int, int*)) +{ + glucose_solver_setcallback((Gluco::SimpSolver*)s,pman,pfunc); +} + +int bmcg_sat_solver_solve(bmcg_sat_solver* s, int * plits, int nlits) +{ + return glucose_solver_solve((Gluco::SimpSolver*)s,plits,nlits); +} + +int bmcg_sat_solver_final(bmcg_sat_solver* s, int ** ppArray) +{ + *ppArray = (int *)(Lit *)((Gluco::SimpSolver*)s)->conflict; + return ((Gluco::SimpSolver*)s)->conflict.size(); +} + +int bmcg_sat_solver_addvar(bmcg_sat_solver* s) +{ + return glucose_solver_addvar((Gluco::SimpSolver*)s); +} + +void bmcg_sat_solver_set_nvars( bmcg_sat_solver* s, int nvars ) +{ + int i; + for ( i = bmcg_sat_solver_varnum(s); i < nvars; i++ ) + bmcg_sat_solver_addvar(s); +} + +int bmcg_sat_solver_eliminate( bmcg_sat_solver* s, int turn_off_elim ) +{ +// return 1; + return ((Gluco::SimpSolver*)s)->eliminate(turn_off_elim != 0); +} + +int bmcg_sat_solver_var_is_elim( bmcg_sat_solver* s, int v ) +{ +// return 0; + return ((Gluco::SimpSolver*)s)->isEliminated(v); +} + +void bmcg_sat_solver_var_set_frozen( bmcg_sat_solver* s, int v, int freeze ) +{ + ((Gluco::SimpSolver*)s)->setFrozen(v, freeze != 0); +} + +int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s) +{ +// return 0; + return ((Gluco::SimpSolver*)s)->eliminated_vars; +} + +int bmcg_sat_solver_read_cex_varvalue(bmcg_sat_solver* s, int ivar) +{ + return glucose_solver_read_cex_varvalue((Gluco::SimpSolver*)s, ivar); +} + +void bmcg_sat_solver_set_stop(bmcg_sat_solver* s, int * pstop) +{ + glucose_solver_setstop((Gluco::SimpSolver*)s, pstop); +} + +abctime bmcg_sat_solver_set_runtime_limit(bmcg_sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = ((Gluco::SimpSolver*)s)->nRuntimeLimit; + ((Gluco::SimpSolver*)s)->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +void bmcg_sat_solver_set_conflict_budget(bmcg_sat_solver* s, int Limit) +{ + if ( Limit > 0 ) + ((Gluco::SimpSolver*)s)->setConfBudget( (int64_t)Limit ); + else + ((Gluco::SimpSolver*)s)->budgetOff(); +} + +int bmcg_sat_solver_varnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*)s)->nVars(); +} +int bmcg_sat_solver_clausenum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*)s)->nClauses(); +} +int bmcg_sat_solver_learntnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*)s)->nLearnts(); +} +int bmcg_sat_solver_conflictnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*)s)->conflicts; +} + +int bmcg_sat_solver_minimize_assumptions( bmcg_sat_solver * s, int * plits, int nlits, int pivot ) +{ + vec*array = &((Gluco::SimpSolver*)s)->user_vec; + int i, nlitsL, nlitsR, nresL, nresR, status; + assert( pivot >= 0 ); +// assert( nlits - pivot >= 2 ); + assert( nlits - pivot >= 1 ); + if ( nlits - pivot == 1 ) + { + // since the problem is UNSAT, we try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + status = bmcg_sat_solver_solve( s, plits, pivot ); + return status != GLUCOSE_UNSAT; // return 1 if the problem is not UNSAT + } + assert( nlits - pivot >= 2 ); + nlitsL = (nlits - pivot) / 2; + nlitsR = (nlits - pivot) - nlitsL; + assert( nlitsL + nlitsR == nlits - pivot ); + // solve with these assumptions + status = bmcg_sat_solver_solve( s, plits, pivot + nlitsL ); + if ( status == GLUCOSE_UNSAT ) // these are enough + return bmcg_sat_solver_minimize_assumptions( s, plits, pivot + nlitsL, pivot ); + // these are not enough + // solve for the right lits +// nResL = nLitsR == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nLitsL, nLitsR, nConfLimit ); + nresL = nlitsR == 1 ? 1 : bmcg_sat_solver_minimize_assumptions( s, plits, nlits, pivot + nlitsL ); + // swap literals + array->clear(); + for ( i = 0; i < nlitsL; i++ ) + array->push(plits[pivot + i]); + for ( i = 0; i < nresL; i++ ) + plits[pivot + i] = plits[pivot + nlitsL + i]; + for ( i = 0; i < nlitsL; i++ ) + plits[pivot + nresL + i] = (*array)[i]; + // solve with these assumptions + status = bmcg_sat_solver_solve( s, plits, pivot + nresL ); + if ( status == GLUCOSE_UNSAT ) // these are enough + return nresL; + // solve for the left lits +// nResR = nLitsL == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nResL, nLitsL, nConfLimit ); + nresR = nlitsL == 1 ? 1 : bmcg_sat_solver_minimize_assumptions( s, plits, pivot + nresL + nlitsL, pivot + nresL ); + return nresL + nresR; +} + +int bmcg_sat_solver_add_and( bmcg_sat_solver * s, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1, int fCompl ) +{ + int Lits[3]; + + Lits[0] = Abc_Var2Lit( iVar, !fCompl ); + Lits[1] = Abc_Var2Lit( iVar0, fCompl0 ); + if ( !bmcg_sat_solver_addclause( s, Lits, 2 ) ) + return 0; + + Lits[0] = Abc_Var2Lit( iVar, !fCompl ); + Lits[1] = Abc_Var2Lit( iVar1, fCompl1 ); + if ( !bmcg_sat_solver_addclause( s, Lits, 2 ) ) + return 0; + + Lits[0] = Abc_Var2Lit( iVar, fCompl ); + Lits[1] = Abc_Var2Lit( iVar0, !fCompl0 ); + Lits[2] = Abc_Var2Lit( iVar1, !fCompl1 ); + if ( !bmcg_sat_solver_addclause( s, Lits, 3 ) ) + return 0; + + return 1; +} + +#else + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gluco::Solver * glucose_solver_start() +{ + Solver * S = new Solver; + S->setIncrementalMode(); + return S; +} + +void glucose_solver_stop(Gluco::Solver* S) +{ + delete S; +} + +int glucose_solver_addclause(Gluco::Solver* S, int * plits, int nlits) +{ + vec lits; + for ( int i = 0; i < nlits; i++,plits++) + { + // note: Glucose uses the same var->lit conventiaon as ABC + while ((*plits)/2 >= S->nVars()) S->newVar(); + assert((*plits)/2 < S->nVars()); // NOTE: since we explicitely use new function bmc_add_var + Lit p; + p.x = *plits; + lits.push(p); + } + return S->addClause(lits); // returns 0 if the problem is UNSAT +} + +void glucose_solver_setcallback(Gluco::Solver* S, void * pman, int(*pfunc)(void*, int, int*)) +{ + S->pCnfMan = pman; + S->pCnfFunc = pfunc; + S->nCallConfl = 1000; +} + +int glucose_solver_solve(Gluco::Solver* S, int * plits, int nlits) +{ + vec lits; + for (int i=0;isolveLimited(lits); + return (res == l_True ? 1 : res == l_False ? -1 : 0); +} + +int glucose_solver_addvar(Gluco::Solver* S) +{ + S->newVar(); + return S->nVars() - 1; +} + +int glucose_solver_read_cex_varvalue(Gluco::Solver* S, int ivar) +{ + return S->model[ivar] == l_True; +} + +void glucose_solver_setstop(Gluco::Solver* S, int * pstop) +{ + S->pstop = pstop; +} + + +/**Function************************************************************* + + Synopsis [Wrapper APIs to calling from ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +bmcg_sat_solver * bmcg_sat_solver_start() +{ + return (bmcg_sat_solver *)glucose_solver_start(); +} +void bmcg_sat_solver_stop(bmcg_sat_solver* s) +{ + glucose_solver_stop((Gluco::Solver*)s); +} + +int bmcg_sat_solver_addclause(bmcg_sat_solver* s, int * plits, int nlits) +{ + return glucose_solver_addclause((Gluco::Solver*)s,plits,nlits); +} + +void bmcg_sat_solver_setcallback(bmcg_sat_solver* s, void * pman, int(*pfunc)(void*, int, int*)) +{ + glucose_solver_setcallback((Gluco::Solver*)s,pman,pfunc); +} + +int bmcg_sat_solver_solve(bmcg_sat_solver* s, int * plits, int nlits) +{ + return glucose_solver_solve((Gluco::Solver*)s,plits,nlits); +} + +int bmcg_sat_solver_final(bmcg_sat_solver* s, int ** ppArray) +{ + *ppArray = (int *)(Lit *)((Gluco::Solver*)s)->conflict; + return ((Gluco::Solver*)s)->conflict.size(); +} + +int bmcg_sat_solver_addvar(bmcg_sat_solver* s) +{ + return glucose_solver_addvar((Gluco::Solver*)s); +} + +void bmcg_sat_solver_set_nvars( bmcg_sat_solver* s, int nvars ) +{ + int i; + for ( i = bmcg_sat_solver_varnum(s); i < nvars; i++ ) + bmcg_sat_solver_addvar(s); +} + +int bmcg_sat_solver_eliminate( bmcg_sat_solver* s, int turn_off_elim ) +{ + return 1; +// return ((Gluco::SimpSolver*)s)->eliminate(turn_off_elim != 0); +} + +int bmcg_sat_solver_var_is_elim( bmcg_sat_solver* s, int v ) +{ + return 0; +// return ((Gluco::SimpSolver*)s)->isEliminated(v); +} + +void bmcg_sat_solver_var_set_frozen( bmcg_sat_solver* s, int v, int freeze ) +{ +// ((Gluco::SimpSolver*)s)->setFrozen(v, freeze); +} + +int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s) +{ + return 0; +// return ((Gluco::SimpSolver*)s)->eliminated_vars; +} + +int bmcg_sat_solver_read_cex_varvalue(bmcg_sat_solver* s, int ivar) +{ + return glucose_solver_read_cex_varvalue((Gluco::Solver*)s, ivar); +} + +void bmcg_sat_solver_set_stop(bmcg_sat_solver* s, int * pstop) +{ + glucose_solver_setstop((Gluco::Solver*)s, pstop); +} + +abctime bmcg_sat_solver_set_runtime_limit(bmcg_sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = ((Gluco::Solver*)s)->nRuntimeLimit; + ((Gluco::Solver*)s)->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +void bmcg_sat_solver_set_conflict_budget(bmcg_sat_solver* s, int Limit) +{ + if ( Limit > 0 ) + ((Gluco::Solver*)s)->setConfBudget( (int64_t)Limit ); + else + ((Gluco::Solver*)s)->budgetOff(); +} + +int bmcg_sat_solver_varnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*)s)->nVars(); +} +int bmcg_sat_solver_clausenum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*)s)->nClauses(); +} +int bmcg_sat_solver_learntnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*)s)->nLearnts(); +} +int bmcg_sat_solver_conflictnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*)s)->conflicts; +} + +int bmcg_sat_solver_minimize_assumptions( bmcg_sat_solver * s, int * plits, int nlits, int pivot ) +{ + vec*array = &((Gluco::Solver*)s)->user_vec; + int i, nlitsL, nlitsR, nresL, nresR, status; + assert( pivot >= 0 ); +// assert( nlits - pivot >= 2 ); + assert( nlits - pivot >= 1 ); + if ( nlits - pivot == 1 ) + { + // since the problem is UNSAT, we try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + status = bmcg_sat_solver_solve( s, plits, pivot ); + return status != GLUCOSE_UNSAT; // return 1 if the problem is not UNSAT + } + assert( nlits - pivot >= 2 ); + nlitsL = (nlits - pivot) / 2; + nlitsR = (nlits - pivot) - nlitsL; + assert( nlitsL + nlitsR == nlits - pivot ); + // solve with these assumptions + status = bmcg_sat_solver_solve( s, plits, pivot + nlitsL ); + if ( status == GLUCOSE_UNSAT ) // these are enough + return bmcg_sat_solver_minimize_assumptions( s, plits, pivot + nlitsL, pivot ); + // these are not enough + // solve for the right lits +// nResL = nLitsR == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nLitsL, nLitsR, nConfLimit ); + nresL = nlitsR == 1 ? 1 : bmcg_sat_solver_minimize_assumptions( s, plits, nlits, pivot + nlitsL ); + // swap literals + array->clear(); + for ( i = 0; i < nlitsL; i++ ) + array->push(plits[pivot + i]); + for ( i = 0; i < nresL; i++ ) + plits[pivot + i] = plits[pivot + nlitsL + i]; + for ( i = 0; i < nlitsL; i++ ) + plits[pivot + nresL + i] = (*array)[i]; + // solve with these assumptions + status = bmcg_sat_solver_solve( s, plits, pivot + nresL ); + if ( status == GLUCOSE_UNSAT ) // these are enough + return nresL; + // solve for the left lits +// nResR = nLitsL == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nResL, nLitsL, nConfLimit ); + nresR = nlitsL == 1 ? 1 : bmcg_sat_solver_minimize_assumptions( s, plits, pivot + nresL + nlitsL, pivot + nresL ); + return nresL + nresR; +} + +#endif + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void glucose_print_stats(SimpSolver& s, abctime clk) +{ + double cpu_time = (double)(unsigned)clk / CLOCKS_PER_SEC; + printf("c restarts : %d (%d conflicts on average)\n", (int)s.starts, s.starts > 0 ? (int)(s.conflicts/s.starts) : 0); + printf("c blocked restarts : %d (multiple: %d) \n", (int)s.nbstopsrestarts, (int)s.nbstopsrestartssame); + printf("c last block at restart : %d\n", (int)s.lastblockatrestart); + printf("c nb ReduceDB : %-12d\n", (int)s.nbReduceDB); + printf("c nb removed Clauses : %-12d\n", (int)s.nbRemovedClauses); + printf("c nb learnts DL2 : %-12d\n", (int)s.nbDL2); + printf("c nb learnts size 2 : %-12d\n", (int)s.nbBin); + printf("c nb learnts size 1 : %-12d\n", (int)s.nbUn); + printf("c conflicts : %-12d (%.0f /sec)\n", (int)s.conflicts, s.conflicts /cpu_time); + printf("c decisions : %-12d (%4.2f %% random) (%.0f /sec)\n", (int)s.decisions, (float)s.rnd_decisions*100 / (float)s.decisions, s.decisions /cpu_time); + printf("c propagations : %-12d (%.0f /sec)\n", (int)s.propagations, s.propagations/cpu_time); + printf("c conflict literals : %-12d (%4.2f %% deleted)\n", (int)s.tot_literals, (s.max_literals - s.tot_literals)*100 / (double)s.max_literals); + printf("c nb reduced Clauses : %-12d\n", (int)s.nbReducedClauses); + //printf("c CPU time : %.2f sec\n", cpu_time); +} + + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END diff --git a/lib/abcsat/CMakeLists.txt b/lib/abcsat/CMakeLists.txt new file mode 100644 index 0000000..edcec0a --- /dev/null +++ b/lib/abcsat/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required (VERSION 3.6) + +project(libabcsat LANGUAGES CXX) + +include_directories(${PROJECT_SOURCE_DIR}) +file(GLOB ABC_SRC *.cpp) + +# Surpress warnings in external library +if (UNIX) + add_compile_options("-w") +elseif (MSVC) + string(REPLACE "/W3" "/w" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +endif() + +if (STATIC_LIBABC) + add_library(libabcsat STATIC EXCLUDE_FROM_ALL ${ABC_SRC}) + set_property(TARGET libabcsat PROPERTY OUTPUT_NAME libabcsat) +else() + add_library(libabcsat SHARED EXCLUDE_FROM_ALL ${ABC_SRC}) + set_property(TARGET libabcsat PROPERTY OUTPUT_NAME libabcsat) + set_property(TARGET libabcsat PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(libabcsat INTERFACE ${PROJECT_SOURCE_DIR}) diff --git a/lib/abcsat/Glucose.cpp b/lib/abcsat/Glucose.cpp new file mode 100644 index 0000000..10135fe --- /dev/null +++ b/lib/abcsat/Glucose.cpp @@ -0,0 +1,1499 @@ +/***************************************************************************************[Solver.cc] + Glucose -- Copyright (c) 2013, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 "abc/Sort.h" +#include "abc/Solver.h" +#include "abc/Constants.h" +#include "abc/system.h" + +ABC_NAMESPACE_IMPL_START + +using namespace Gluco; + +//================================================================================================= +// Options: + +static const char* _cat = "CORE"; +static const char* _cr = "CORE -- RESTART"; +static const char* _cred = "CORE -- REDUCE"; +static const char* _cm = "CORE -- MINIMIZE"; +static const char* _certified = "CORE -- CERTIFIED UNSAT"; + + +const bool opt_incremental = false; +const double opt_K = 0.8; +const double opt_R = 1.4; +const int opt_size_lbd_queue = 50; + +const int opt_first_reduce_db = 2000; +const int opt_inc_reduce_db = 300; +const int opt_spec_inc_reduce_db = 1000; +const int opt_lb_lbd_frozen_clause = 30; + +const int opt_lb_size_minimzing_clause = 30; +const int opt_lb_lbd_minimzing_clause = 6; + +const double opt_var_decay = 0.8; +const double opt_clause_decay = 0.999; +const double opt_random_var_freq = 0; +const double opt_random_seed = 91648253; +const int opt_ccmin_mode = 2; +const int opt_phase_saving = 2; +const bool opt_rnd_init_act = false; +/* +static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); +static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); +*/ +const double opt_garbage_frac = 0.20; + + +const bool opt_certified_ = false; +const char* const opt_certified_file_ = "NULL"; + + +//================================================================================================= +// Constructor/Destructor: + + +Solver::Solver() : + + // Parameters (user settable): + // + SolverType(0) + , pCnfFunc(NULL) + , nCallConfl(1000) + , terminate_search_early(false) + , pstop(NULL) + , nRuntimeLimit(0) + + , verbosity (0) + , verbEveryConflicts(10000) + , showModel (0) + , K (opt_K) + , R (opt_R) + , sizeLBDQueue (50) + , sizeTrailQueue (5000) + , firstReduceDB (opt_first_reduce_db) + , incReduceDB (opt_inc_reduce_db) + , specialIncReduceDB (opt_spec_inc_reduce_db) + , lbLBDFrozenClause (opt_lb_lbd_frozen_clause) + , lbSizeMinimizingClause (opt_lb_size_minimzing_clause) + , lbLBDMinimizingClause (opt_lb_lbd_minimzing_clause) + , var_decay (opt_var_decay) + , clause_decay (opt_clause_decay) + , random_var_freq (opt_random_var_freq) + , random_seed (opt_random_seed) + , ccmin_mode (opt_ccmin_mode) + , phase_saving (opt_phase_saving) + , rnd_pol (false) + , rnd_init_act (opt_rnd_init_act) + , garbage_frac (opt_garbage_frac) + , certifiedOutput (NULL) + , certifiedUNSAT (opt_certified_) + // Statistics: (formerly in 'SolverStats') + // + , nbRemovedClauses(0),nbReducedClauses(0), nbDL2(0),nbBin(0),nbUn(0) , nbReduceDB(0) + , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0),conflicts(0),conflictsRestarts(0),nbstopsrestarts(0),nbstopsrestartssame(0),lastblockatrestart(0) + , dec_vars(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) + , curRestart(1) + + , ok (true) + , cla_inc (1) + , var_inc (1) + , watches (WatcherDeleted(ca)) + , watchesBin (WatcherDeleted(ca)) + , qhead (0) + , simpDB_assigns (-1) + , simpDB_props (0) + , order_heap (VarOrderLt(activity)) + , progress_estimate (0) + , remove_satisfied (true) + + // Resource constraints: + // + , conflict_budget (-1) + , propagation_budget (-1) + , asynch_interrupt (false) + , incremental(opt_incremental) + , nbVarsInitialFormula(INT32_MAX) +{ + MYFLAG=0; + // Initialize only first time. Useful for incremental solving, useless otherwise + lbdQueue.initSize(sizeLBDQueue); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + totalTime4Sat=0;totalTime4Unsat=0; + nbSatCalls=0;nbUnsatCalls=0; + + + if(certifiedUNSAT) { + if(!strcmp(opt_certified_file_,"NULL")) { + certifiedOutput = fopen("/dev/stdout", "wb"); + } else { + certifiedOutput = fopen(opt_certified_file_, "wb"); + } + // fprintf(certifiedOutput,"o proof DRUP\n"); + } +} + + +Solver::~Solver() +{ +} + + +/**************************************************************** + Set the incremental mode +****************************************************************/ + +// This function set the incremental mode to true. +// You can add special code for this mode here. + +void Solver::setIncrementalMode() { + incremental = true; +} + +// Number of variables without selectors +void Solver::initNbInitialVars(int nb) { + nbVarsInitialFormula = nb; +} + + +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// +Var Solver::newVar(bool sign, bool dvar) +{ + int v = nVars(); + watches .init(mkLit(v, false)); + watches .init(mkLit(v, true )); + watchesBin .init(mkLit(v, false)); + watchesBin .init(mkLit(v, true )); + assigns .push(l_Undef); + vardata .push(mkVarData(CRef_Undef, 0)); + //activity .push(0); + activity .push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen .push(0); + permDiff .push(0); + polarity .push(sign); + decision .push(); + trail .capacity(v+1); + setDecisionVar(v, dvar); + return v; +} + + + +bool Solver::addClause_(vec& ps) +{ + assert(decisionLevel() == 0); + if (!ok) return false; + + if ( 0 ) { + for ( int i = 0; i < ps.size(); i++ ) + printf( "%s%d ", (toInt(ps[i]) & 1) ? "-":"", toInt(ps[i]) >> 1 ); + printf( "\n" ); + } + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + + vec oc; + oc.clear(); + + Lit p; int i, j, flag = 0; + if(certifiedUNSAT) { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) { + oc.push(ps[i]); + if (value(ps[i]) == l_True || ps[i] == ~p || value(ps[i]) == l_False) + flag = 1; + } + } + + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + if (value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if (value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if ( 0 ) { + for ( int i = 0; i < ps.size(); i++ ) + printf( "%s%d ", (toInt(ps[i]) & 1) ? "-":"", toInt(ps[i]) >> 1 ); + printf( "\n" ); + } + + if (flag && (certifiedUNSAT)) { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + fprintf(certifiedOutput, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + + fprintf(certifiedOutput, "d "); + for (i = j = 0, p = lit_Undef; i < oc.size(); i++) + fprintf(certifiedOutput, "%i ", (var(oc[i]) + 1) * (-2 * sign(oc[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + if (ps.size() == 0) + return ok = false; + else if (ps.size() == 1){ + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + }else{ + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + + +void Solver::attachClause(CRef cr) { + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if(c.size()==2) { + watchesBin[~c[0]].push(Watcher(cr, c[1])); + watchesBin[~c[1]].push(Watcher(cr, c[0])); + } else { + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + } + if (c.learnt()) learnts_literals += c.size(); + else clauses_literals += c.size(); } + + + + +void Solver::detachClause(CRef cr, bool strict) { + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if(c.size()==2) { + if (strict){ + remove(watchesBin[~c[0]], Watcher(cr, c[1])); + remove(watchesBin[~c[1]], Watcher(cr, c[0])); + }else{ + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watchesBin.smudge(~c[0]); + watchesBin.smudge(~c[1]); + } + } else { + if (strict){ + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + }else{ + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + } + if (c.learnt()) learnts_literals -= c.size(); + else clauses_literals -= c.size(); } + + +void Solver::removeClause(CRef cr) { + + Clause& c = ca[cr]; + + if (certifiedUNSAT) { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + detachClause(cr); + // Don't leave pointers to free'd memory! + if (locked(c)) vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free_(cr); +} + + +bool Solver::satisfied(const Clause& c) const { + if(incremental) // Check clauses with many selectors is too time consuming + return (value(c[0]) == l_True) || (value(c[1]) == l_True); + + // Default mode. + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) + return true; + return false; +} + +/************************************************************ + * Compute LBD functions + *************************************************************/ + +inline unsigned int Solver::computeLBD(const vec & lits,int end) { + int nblevels = 0; + MYFLAG++; + + if(incremental) { // ----------------- INCREMENTAL MODE + if(end==-1) end = lits.size(); + unsigned int nbDone = 0; + for(int i=0;i=end) break; + if(isSelector(var(lits[i]))) continue; + nbDone++; + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for(int i=0;i=c.sizeWithoutSelectors()) break; + if(isSelector(var(c[i]))) continue; + nbDone++; + int l = level(var(c[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for(int i=0;i &out_learnt) { + + // Find the LBD measure + unsigned int lbd = computeLBD(out_learnt); + Lit p = ~out_learnt[0]; + + if(lbd<=lbLBDMinimizingClause){ + MYFLAG++; + + for(int i = 1;i& wbin = watchesBin[p]; + int nb = 0; + for(int k = 0;k0) { + nbReducedClauses++; + for(int i = 1;i level){ + for (int c = trail.size()-1; c >= trail_lim[level]; c--){ + Var x = var(trail[c]); + assigns [x] = l_Undef; + if (phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) + polarity[x] = sign(trail[c]); + insertVarOrder(x); } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + + +//================================================================================================= +// Major methods: + + +Lit Solver::pickBranchLit() +{ + Var next = var_Undef; + + // Random decision: + if (drand(random_seed) < random_var_freq && !order_heap.empty()){ + next = order_heap[irand(random_seed,order_heap.size())]; + if (value(next) == l_Undef && decision[next]) + rnd_decisions++; } + + // Activity based decision: + while (next == var_Undef || value(next) != l_Undef || !decision[next]) + if (order_heap.empty()){ + next = var_Undef; + break; + }else + next = order_heap.removeMin(); + + return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : (polarity[next] != 0)); +} + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +void Solver::analyze(CRef confl, vec& out_learnt,vec&selectors, int& out_btlevel,unsigned int &lbd,unsigned int &szWithoutSelectors) +{ + int pathC = 0; + Lit p = lit_Undef; + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + + do{ + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause& c = ca[confl]; + + // Special case for binary clauses + // The first one has to be SAT + if( p != lit_Undef && c.size()==2 && value(c[0])==l_False) { + + assert(value(c[1])==l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + if (c.learnt()) + claBumpActivity(c); + +#ifdef DYNAMICNBLEVEL + // DYNAMIC NBLEVEL trick (see competition'09 companion paper) + if(c.learnt() && c.lbd()>2) { + unsigned int nblevels = computeLBD(c); + if(nblevels+1 0){ + if(!isSelector(var(q))) + varBumpActivity(var(q)); + seen[var(q)] = 1; + if (level(var(q)) >= decisionLevel()) { + pathC++; +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(!isSelector(var(q)) && (reason(var(q))!= CRef_Undef) && ca[reason(var(q))].learnt()) + lastDecisionLevel.push(q); +#endif + + } else { + if(isSelector(var(q))) { + assert(value(q) == l_False); + selectors.push(q); + } else + out_learnt.push(q); + } + } + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]); + p = trail[index+1]; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + }while (pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + + for(i = 0;i 0){ + out_learnt[j++] = out_learnt[i]; + break; } + } + } + }else + i = j = out_learnt.size(); + + max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + tot_literals += out_learnt.size(); + + + /* *************************************** + Minimisation with binary clauses of the asserting clause + First of all : we look for small clauses + Then, we reduce clauses with small LBD. + Otherwise, this can be useless + */ + if(!incremental && out_learnt.size()<=lbSizeMinimizingClause) { + minimisationWithBinaryResolution(out_learnt); + } + // Find correct backtrack level: + // + if (out_learnt.size() == 1) + out_btlevel = 0; + else{ + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for (int i = 2; i < out_learnt.size(); i++) + if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } + + + // Compute the size of the clause without selectors (incremental mode) + if(incremental) { + szWithoutSelectors = 0; + for(int i=0;i0) break; + } + } else + szWithoutSelectors = out_learnt.size(); + + // Compute LBD + lbd = computeLBD(out_learnt,out_learnt.size()-selectors.size()); + + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(lastDecisionLevel.size()>0) { + for(int i = 0;i 0){ + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause& c = ca[reason(var(analyze_stack.last()))]; analyze_stack.pop(); + if(c.size()==2 && value(c[0])==l_False) { + assert(value(c[1])==l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + for (int i = 1; i < c.size(); i++){ + Lit p = c[i]; + if (!seen[var(p)] && level(var(p)) > 0){ + if (reason(var(p)) != CRef_Undef && (abstractLevel(var(p)) & abstract_levels) != 0){ + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + }else{ + for (int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +void Solver::analyzeFinal(Lit p, vec& out_conflict) +{ + out_conflict.clear(); + out_conflict.push(p); + + if (decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for (int i = trail.size()-1; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + if (reason(x) == CRef_Undef){ + assert(level(x) > 0); + out_conflict.push(~trail[i]); + }else{ + Clause& c = ca[reason(x)]; + // for (int j = 1; j < c.size(); j++) Minisat (glucose 2.0) loop + // Bug in case of assumptions due to special data structures for Binary. + // Many thanks to Sam Bayless (sbayless@cs.ubc.ca) for discover this bug. + for (int j = ((c.size()==2) ? 0:1); j < c.size(); j++) + if (level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + + +void Solver::uncheckedEnqueue(Lit p, CRef from) +{ + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel()); + trail.push_(p); +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +CRef Solver::propagate() +{ + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watchesBin.cleanAll(); + while (qhead < trail.size()){ + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec& ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + // First, Propagate binary clauses + vec& wbin = watchesBin[p]; + for(int k = 0;kblocker; + if (value(blocker) == l_True){ + *j++ = *i++; continue; } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True){ + *j++ = w; continue; } + + // Look for new watch: + if(incremental) { // ----------------- INCREMENTAL MODE + int choosenPos = -1; + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + if(decisionLevel()>assumptions.size()) { + choosenPos = k; + break; + } else { + choosenPos = k; + + if(value(c[k])==l_True || !isSelector(var(c[k]))) { + break; + } + } + + } + } + if(choosenPos!=-1) { + c[1] = c[choosenPos]; c[choosenPos] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } else { // ----------------- DEFAULT MODE (NOT INCREMENTAL) + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + c[1] = c[k]; c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } + } + + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if (value(first) == l_False){ + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + }else { + uncheckedEnqueue(first, cr); + + + } + NextClause:; + } + ws.shrink(i - j); + } + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ +struct reduceDB_lt { + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} + bool operator () (CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if(ca[x].size()> 2 && ca[y].size()==2) return 1; + + if(ca[y].size()> 2 && ca[x].size()==2) return 0; + if(ca[x].size()==2 && ca[y].size()==2) return 0; + + // Second one based on literal block distance + if(ca[x].lbd()> ca[y].lbd()) return 1; + if(ca[x].lbd()< ca[y].lbd()) return 0; + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + //return x->size() < y->size(); + //return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + +void Solver::reduceDB() +{ + int i, j; + nbReduceDB++; + sort(learnts, reduceDB_lt(ca)); + + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + if(ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd()<=3) nbclausesbeforereduce +=specialIncReduceDB; + // Useless :-) + if(ca[learnts.last()].lbd()<=5) nbclausesbeforereduce +=specialIncReduceDB; + + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + int limit = learnts.size() / 2; + for (i = j = 0; i < learnts.size(); i++){ + Clause& c = ca[learnts[i]]; + if (c.lbd()>2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + nbRemovedClauses++; + } + else { + if(!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + checkGarbage(); +} + + +void Solver::removeSatisfied(vec& cs) +{ + int i, j; + for (i = j = 0; i < cs.size(); i++){ + Clause& c = ca[cs[i]]; + if (satisfied(c)) + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + + +void Solver::rebuildOrderHeap() +{ + vec vs; + for (Var v = 0; v < nVars(); v++) + if (decision[v] && value(v) == l_Undef) + vs.push(v); + order_heap.build(vs); +} + + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +bool Solver::simplify() +{ + assert(decisionLevel() == 0); + + if (!ok || propagate() != CRef_Undef) + return ok = false; + + if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + removeSatisfied(learnts); + if (remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + + checkGarbage(); + + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +lbool Solver::search(int nof_conflicts) +{ + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause,selectors; + unsigned int nblevels,szWoutSelectors; + bool blocked=false; + starts++; + for (;;){ + CRef confl = propagate(); + if (confl != CRef_Undef){ + // CONFLICT + conflicts++; conflictC++;conflictsRestarts++; + if(conflicts%5000==0 && var_decay<0.95) + var_decay += 0.01; + + if (verbosity >= 1 && conflicts%verbEveryConflicts==0){ + printf("c | %8d %7d %5d | %7d %8d %8d | %5d %8d %6d %8d | %6.3f %% |\n", + (int)starts,(int)nbstopsrestarts, (int)(conflicts/starts), + (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, + (int)nbReduceDB, nLearnts(), (int)nbDL2,(int)nbRemovedClauses, progressEstimate()*100); + } + if (decisionLevel() == 0) { + return l_False; + + } + + trailQueue.push(trail.size()); + // BLOCK RESTART (CP 2012 paper) + if( conflictsRestarts>LOWER_BOUND_FOR_BLOCKING_RESTART && lbdQueue.isvalid() && trail.size()>R*trailQueue.getavg()) { + lbdQueue.fastclear(); + nbstopsrestarts++; + if(!blocked) {lastblockatrestart=starts;nbstopsrestartssame++;blocked=true;} + } + + learnt_clause.clear(); + selectors.clear(); + analyze(confl, learnt_clause, selectors,backtrack_level,nblevels,szWoutSelectors); + + lbdQueue.push(nblevels); + sumLBD += nblevels; + + cancelUntil(backtrack_level); + + if (certifiedUNSAT) { + for (int i = 0; i < learnt_clause.size(); i++) + fprintf(certifiedOutput, "%i " , (var(learnt_clause[i]) + 1) * + (-2 * sign(learnt_clause[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + + if (learnt_clause.size() == 1){ + uncheckedEnqueue(learnt_clause[0]);nbUn++; + }else{ + CRef cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(nblevels); + ca[cr].setSizeWithoutSelectors(szWoutSelectors); + if(nblevels<=2) nbDL2++; // stats + if(ca[cr].size()==2) nbBin++; // stats + learnts.push(cr); + attachClause(cr); + + claBumpActivity(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + } + varDecayActivity(); + claDecayActivity(); + + + }else{ + + // Our dynamic restart, see the SAT09 competition compagnion paper + if ( (conflictsRestarts && lbdQueue.isvalid() && lbdQueue.getavg()*K > sumLBD/conflictsRestarts) || (pstop && *pstop) ) { + lbdQueue.fastclear(); + progress_estimate = progressEstimate(); + int bt = 0; + if(incremental) { // DO NOT BACKTRACK UNTIL 0.. USELESS + bt = (decisionLevel()=curRestart* nbclausesbeforereduce) + { + + assert(learnts.size()>0); + curRestart = (conflicts/ nbclausesbeforereduce)+1; + reduceDB(); + nbclausesbeforereduce += incReduceDB; + } + + Lit next = lit_Undef; + while (decisionLevel() < assumptions.size()){ + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if (value(p) == l_True){ + // Dummy decision level: + newDecisionLevel(); + }else if (value(p) == l_False){ + analyzeFinal(~p, conflict); + return l_False; + }else{ + next = p; + break; + } + } + + if (next == lit_Undef){ + // New variable decision: + decisions++; + next = pickBranchLit(); + + if (next == lit_Undef){ + //printf("c last restart ## conflicts : %d %d \n",conflictC,decisionLevel()); + // Model found: + return l_True; + } + } + + // Increase decision level and enqueue 'next' + newDecisionLevel(); + uncheckedEnqueue(next); + } + } +} + + +double Solver::progressEstimate() const +{ + double progress = 0; + double F = 1.0 / nVars(); + + for (int i = 0; i <= decisionLevel(); i++){ + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + +void Solver::printIncrementalStats() { + + printf("c---------- Glucose Stats -------------------------\n"); + printf("c restarts : %ld\n", starts); + printf("c nb ReduceDB : %ld\n", nbReduceDB); + printf("c nb removed Clauses : %ld\n", nbRemovedClauses); + printf("c nb learnts DL2 : %ld\n", nbDL2); + printf("c nb learnts size 2 : %ld\n", nbBin); + printf("c nb learnts size 1 : %ld\n", nbUn); + + printf("c conflicts : %ld\n", conflicts); + printf("c decisions : %ld\n", decisions); + printf("c propagations : %ld\n", propagations); + + printf("c SAT Calls : %d in %g seconds\n", nbSatCalls, totalTime4Sat); + printf("c UNSAT Calls : %d in %g seconds\n", nbUnsatCalls, totalTime4Unsat); + printf("c--------------------------------------------------\n"); + + +} + + +// NOTE: assumptions passed in member-variable 'assumptions'. +lbool Solver::solve_() +{ + + if(incremental && certifiedUNSAT) { + printf("Can not use incremental and certified unsat in the same time\n"); + exit(-1); + } + model.clear(); + conflict.clear(); + if (!ok) return l_False; + double curTime = cpuTime(); + + + solves++; + + + + lbool status = l_Undef; + if(!incremental && verbosity>=1) { + printf("c ========================================[ MAGIC CONSTANTS ]==============================================\n"); + printf("c | Constants are supposed to work well together :-) |\n"); + printf("c | however, if you find better choices, please let us known... |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + printf("c | | | |\n"); + printf("c | - Restarts: | - Reduce Clause DB: | - Minimize Asserting: |\n"); + printf("c | * LBD Queue : %6d | * First : %6d | * size < %3d |\n",lbdQueue.maxSize(),nbclausesbeforereduce,lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * Inc : %6d | * lbd < %3d |\n",trailQueue.maxSize(),incReduceDB,lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Special : %6d | |\n",K,specialIncReduceDB); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n",R,lbLBDFrozenClause); + printf("c | | | |\n"); +printf("c ==================================[ Search Statistics (every %6d conflicts) ]=========================\n",verbEveryConflicts); + printf("c | |\n"); + + printf("c | RESTARTS | ORIGINAL | LEARNT | Progress |\n"); + printf("c | NB Blocked Avg Cfc | Vars Clauses Literals | Red Learnts LBD2 Removed | |\n"); + printf("c =========================================================================================================\n"); + } + + // Search: + int curr_restarts = 0; + while (status == l_Undef){ + status = search(0); // the parameter is useless in glucose, kept to allow modifications + if (!withinBudget() || terminate_search_early || (pstop && *pstop)) break; + if (nRuntimeLimit && Abc_Clock() > nRuntimeLimit) break; + curr_restarts++; + } + + if (!incremental && verbosity >= 1) + printf("c =========================================================================================================\n"); + + + if (certifiedUNSAT){ // Want certified output + if (status == l_False) + fprintf(certifiedOutput, "0\n"); + fclose(certifiedOutput); + } + + + if (status == l_True){ + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + }else if (status == l_False && conflict.size() == 0) + ok = false; + + cancelUntil(0); + + double finalTime = cpuTime(); + if(status==l_True) { + nbSatCalls++; + totalTime4Sat +=(finalTime-curTime); + } + if(status==l_False) { + nbUnsatCalls++; + totalTime4Unsat +=(finalTime-curTime); + } + + // ABC callback + if (pCnfFunc && !terminate_search_early) {// hack to avoid calling callback twise if the solver was terminated early + int * pCex = NULL; + int message = (status == l_True ? 1 : status == l_False ? 0 : -1); + if (status == l_True) { + pCex = new int[nVars()]; + for (int i = 0; i < nVars(); i++) + pCex[i] = (model[i] == l_True); + } + + int callback_result = pCnfFunc(pCnfMan, message, pCex); + assert(callback_result == 0); + } + else if (pCnfFunc) + terminate_search_early = false; // for next run + + return status; +} + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec& map, Var& max) +{ + if (map.size() <= x || map[x] == -1){ + map.growTo(x+1, -1); + map[x] = max++; + } + return map[x]; +} + + +void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) +{ + if (satisfied(c)) return; + + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1); + fprintf(f, "0\n"); +} + + +void Solver::toDimacs(const char *file, const vec& assumps) +{ + FILE* f = fopen(file, "wr"); + if (f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + + +void Solver::toDimacs(FILE* f, const vec& assumps) +{ + // Handle case when solver is in contradictory state: + if (!ok){ + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; } + + vec map; Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int i, cnt = 0; + for (i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) + cnt++; + + for (i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])){ + Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumptions.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for (i = 0; i < assumptions.size(); i++){ + assert(value(assumptions[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumptions[i]) ? "-" : "", mapVar(var(assumptions[i]), map, max)+1); + } + + for (i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + if (verbosity > 0) + printf("Wrote %d clauses with %d variables.\n", cnt, max); +} + + +//================================================================================================= +// Garbage Collection methods: + +void Solver::relocAll(ClauseAllocator& to) +{ + int v, s, i, j; + // All watchers: + // + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watchesBin.cleanAll(); + for (v = 0; v < nVars(); v++) + for (s = 0; s < 2; s++){ + Lit p = mkLit(v, s != 0); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec& ws = watches[p]; + for (j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec& ws2 = watchesBin[p]; + for (j = 0; j < ws2.size(); j++) + ca.reloc(ws2[j].cref, to); + } + + // All reasons: + // + for (i = 0; i < trail.size(); i++){ + Var v = var(trail[i]); + + if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + for (i = 0; i < learnts.size(); i++) + ca.reloc(learnts[i], to); + + // All original: + // + for (i = 0; i < clauses.size(); i++) + ca.reloc(clauses[i], to); +} + + +void Solver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +void Solver::reset() +{ + // Reset everything + ok = true; + K = (double)opt_K; + R = (double)opt_R; + firstReduceDB = opt_first_reduce_db; + var_decay = (double)opt_var_decay; + //max_var_decay = opt_max_var_decay; + solves = starts = decisions = propagations = conflicts = conflictsRestarts = 0; + curRestart = 1; + cla_inc = var_inc = 1; + watches.clear(false); // We don't free the memory, new calls should be of the same size order. + watchesBin.clear(false); + //unaryWatches.clear(false); + qhead = 0; + simpDB_assigns = -1; + simpDB_props = 0; + order_heap.clear(false); + progress_estimate = 0; + //lastLearntClause = CRef_Undef; + conflict_budget = -1; + propagation_budget = -1; + nbVarsInitialFormula = INT32_MAX; + totalTime4Sat = 0.; + totalTime4Unsat = 0.; + nbSatCalls = nbUnsatCalls = 0; + MYFLAG = 0; + lbdQueue.clear(false); + lbdQueue.initSize(sizeLBDQueue); + trailQueue.clear(false); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + //stats.clear(); + //stats.growTo(coreStatsSize, 0); + clauses.clear(false); + learnts.clear(false); + //permanentLearnts.clear(false); + //unaryWatchedClauses.clear(false); + model.clear(false); + conflict.clear(false); + activity.clear(false); + assigns.clear(false); + polarity.clear(false); + //forceUNSAT.clear(false); + decision.clear(false); + trail.clear(false); + nbpos.clear(false); + trail_lim.clear(false); + vardata.clear(false); + assumptions.clear(false); + permDiff.clear(false); + lastDecisionLevel.clear(false); + ca.clear(); + seen.clear(false); + analyze_stack.clear(false); + analyze_toclear.clear(false); + add_tmp.clear(false); + assumptionPositions.clear(false); + initialPositions.clear(false); +} + +ABC_NAMESPACE_IMPL_END diff --git a/lib/abcsat/SimpSolver.cpp b/lib/abcsat/SimpSolver.cpp new file mode 100644 index 0000000..15279f3 --- /dev/null +++ b/lib/abcsat/SimpSolver.cpp @@ -0,0 +1,761 @@ +/***********************************************************************************[SimpSolver.cc] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 "abc/Sort.h" +#include "abc/SimpSolver.h" +#include "abc/system.h" + +ABC_NAMESPACE_IMPL_START + +using namespace Gluco; + +//================================================================================================= +// Options: + + +static const char* _cat = "SIMP"; + +//================================================================================================= +// Constructor/Destructor: + + +SimpSolver::SimpSolver() : + grow (0) + , clause_lim (20) + , subsumption_lim (1000) + , simp_garbage_frac (0.5) + , use_asymm (false) + , use_rcheck (false) + , use_elim (true) + , merges (0) + , asymm_lits (0) + , eliminated_vars (0) + , eliminated_clauses (0) + , elimorder (1) + , use_simplification (true) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (0) + , n_touched (0) +{ + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + + +SimpSolver::~SimpSolver() +{ +} + + +Var SimpSolver::newVar(bool sign, bool dvar) { + Var v = Solver::newVar(sign, dvar); + + frozen .push((char)false); + eliminated.push((char)false); + + if (use_simplification){ + n_occ .push(0); + n_occ .push(0); + occurs .init(v); + touched .push(0); + elim_heap .insert(v); + } + return v; } + + + +lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + + do_simp &= use_simplification; + + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("===============================================================================\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + return result; +} + + + +bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if (use_simplification && clauses.size() == nclauses + 1){ + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++){ + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + + +void SimpSolver::removeClause(CRef cr) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++){ + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr); +} + + +bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (certifiedUNSAT) { + for (int i = 0; i < c.size(); i++) + if (c[i] != l) fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + + if (c.size() == 2){ + removeClause(cr); + c.strengthen(l); + }else{ + if (certifiedUNSAT) { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + int i, j; + for (i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) { + if (ps[j] == ~qs[i]) + return false; + else + goto next; + } + out_clause.push(qs[i]); + } + next:; + } + + for (i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +// Returns FALSE if clause is always satisfied. +bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*)ps; + const Lit* __qs = (const Lit*)qs; + + size = ps.size()-1; + + for (int i = 0; i < qs.size(); i++){ + if (var(__qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) { + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + } + size++; + } + next:; + } + + return true; +} + + +void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) return; + + int i,j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0){ + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + + +bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True){ + cancelUntil(0); + return false; + }else if (value(c[i]) != l_False){ + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + + +// Backward subsumption + backward subsumption resolution +bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt){ + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); } + + CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*)_cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){ + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error){ + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v && value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + else + l = c[i]; + + if (propagate() != CRef_Undef){ + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + }else + cancelUntil(0); + + return true; +} + + +bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++){ + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + + + +bool SimpSolver::eliminateVar(Var v) +{ + int i, j; + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (i = 0; i < pos.size(); i++) + for (j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && + (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses: + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()){ + for (i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + eliminated_clauses += neg.size(); + }else{ + for (i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + eliminated_clauses += pos.size(); + } + + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (i = 0; i < pos.size(); i++) + for (j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); + if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + + +bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++){ + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++){ + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + + } + + return true; +} + + +void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + for (i = elimclauses.size()-1; i > 0; i -= j){ + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + + +bool SimpSolver::eliminate(bool turn_off_elim) +{ + //abctime clk = Abc_Clock(); + if (!simplify()) + return false; + else if (!use_simplification) + return true; + + // Main simplification loop: + // + + int toPerform = clauses.size()<=4800000; + + if(!toPerform) { + printf("c Too many clauses... No preprocessing\n"); + } + + while (toPerform && (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0)){ + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && + !backwardSubsumptionCheck(true)){ + ok = false; goto cleanup; } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt){ + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++){ + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) break; + + if (isEliminated(elim) || value(elim) != l_Undef) continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("elimination left: %10d\r", elim_heap.size()); + + if (use_asymm){ + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim] != 0; + frozen[elim] = true; + if (!asymmVar(elim)){ + ok = false; goto cleanup; } + frozen[elim] = was_frozen; } + + // At this point, the variable may have been set by assymetric branching, so check it + // again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){ + ok = false; goto cleanup; } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } + cleanup: + + // If no more simplification is needed, free all simplification-related data structures: + if (turn_off_elim){ + touched .clear(true); + occurs .clear(true); + n_occ .clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + }else{ + // Cheaper cleanup: + cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? + checkGarbage(); + } + + if (verbosity >= 1 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + return ok; +} + + +void SimpSolver::cleanUpClauses() +{ + occurs.cleanAll(); + int i,j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); +} + + +//================================================================================================= +// Garbage Collection methods: + + +void SimpSolver::relocAll(ClauseAllocator& to) +{ + int i; + if (!use_simplification) return; + + // All occurs lists: + // + for (i = 0; i < nVars(); i++){ + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + + +void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + cleanUpClauses(); + to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +void SimpSolver::reset() +{ + Solver::reset(); + grow = 0; + asymm_lits = eliminated_vars = bwdsub_assigns = n_touched = 0; + elimclauses.clear(false); + touched.clear(false); + occurs.clear(false); + n_occ.clear(false); + elim_heap.clear(false); + subsumption_queue.clear(false); + frozen.clear(false); + eliminated.clear(false); + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + +ABC_NAMESPACE_IMPL_END diff --git a/lib/abcsat/abc/AbcGlucose.h b/lib/abcsat/abc/AbcGlucose.h new file mode 100644 index 0000000..129a713 --- /dev/null +++ b/lib/abcsat/abc/AbcGlucose.h @@ -0,0 +1,107 @@ +/**CFile**************************************************************** + + FileName [AbcGlucose.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver Glucose 3.0 by Gilles Audemard and Laurent Simon.] + + Synopsis [Interface to Glucose.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 6, 2017.] + + Revision [$Id: AbcGlucose.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC_SAT_GLUCOSE_H_ +#define ABC_SAT_GLUCOSE_H_ + +#include "abc/abc_global.h" + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define GLUCOSE_UNSAT -1 +#define GLUCOSE_SAT 1 +#define GLUCOSE_UNDEC 0 + + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Glucose_Pars_ Glucose_Pars; +struct Glucose_Pars_ { + int pre; // preprocessing + int verb; // verbosity + int cust; // customizable + int nConfls; // conflict limit (0 = no limit) +}; + +static inline Glucose_Pars Glucose_CreatePars(int p, int v, int c, int nConfls) +{ + Glucose_Pars pars; + pars.pre = p; + pars.verb = v; + pars.cust = c; + pars.nConfls = nConfls; + return pars; +} + +typedef void bmcg_sat_solver; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern bmcg_sat_solver * bmcg_sat_solver_start(); +extern void bmcg_sat_solver_stop( bmcg_sat_solver* s ); +extern void bmcg_sat_solver_reset( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_addclause( bmcg_sat_solver* s, int * plits, int nlits ); +extern void bmcg_sat_solver_setcallback( bmcg_sat_solver* s, void * pman, int(*pfunc)(void*, int, int*) ); +extern int bmcg_sat_solver_solve( bmcg_sat_solver* s, int * plits, int nlits ); +extern int bmcg_sat_solver_final( bmcg_sat_solver* s, int ** ppArray ); +extern int bmcg_sat_solver_addvar( bmcg_sat_solver* s ); +extern void bmcg_sat_solver_set_nvars( bmcg_sat_solver* s, int nvars ); +extern int bmcg_sat_solver_eliminate( bmcg_sat_solver* s, int turn_off_elim ); +extern int bmcg_sat_solver_var_is_elim( bmcg_sat_solver* s, int v ); +extern void bmcg_sat_solver_var_set_frozen( bmcg_sat_solver* s, int v, int freeze ); +extern int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s); +extern int bmcg_sat_solver_read_cex_varvalue( bmcg_sat_solver* s, int ); +extern void bmcg_sat_solver_set_stop( bmcg_sat_solver* s, int * pstop ); +extern abctime bmcg_sat_solver_set_runtime_limit( bmcg_sat_solver* s, abctime Limit ); +extern void bmcg_sat_solver_set_conflict_budget( bmcg_sat_solver* s, int Limit ); +extern int bmcg_sat_solver_varnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_clausenum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_learntnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_conflictnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_minimize_assumptions( bmcg_sat_solver * s, int * plits, int nlits, int pivot ); +extern int bmcg_sat_solver_add_and( bmcg_sat_solver * s, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1, int fCompl ); + +extern void Glucose_SolveCnf( char * pFilename, Glucose_Pars * pPars ); + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcsat/abc/Alg.h b/lib/abcsat/abc/Alg.h new file mode 100644 index 0000000..c33ccc0 --- /dev/null +++ b/lib/abcsat/abc/Alg.h @@ -0,0 +1,88 @@ +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Alg_h +#define Glucose_Alg_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Alloc.h b/lib/abcsat/abc/Alloc.h new file mode 100644 index 0000000..b28d618 --- /dev/null +++ b/lib/abcsat/abc/Alloc.h @@ -0,0 +1,136 @@ +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Glucose_Alloc_h +#define Glucose_Alloc_h + +#include "XAlloc.h" +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free_ (int size) { wasted_ += size; } + void clear () { sz = 0; wasted_=0; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + //printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + //printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/BoundedQueue.h b/lib/abcsat/abc/BoundedQueue.h new file mode 100644 index 0000000..ead1443 --- /dev/null +++ b/lib/abcsat/abc/BoundedQueue.h @@ -0,0 +1,114 @@ +/***********************************************************************************[BoundedQueue.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +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. +**************************************************************************************************/ + + +#ifndef BoundedQueue_h +#define BoundedQueue_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +//================================================================================================= + +namespace Gluco { + +template +class bqueue { + vec elems; + int first; + int last; + uint64_t sumofqueue; + int maxsize; + int queuesize; // Number of current elements (must be < maxsize !) + bool expComputed; + double exp,value; +public: + bqueue(void) : first(0), last(0), sumofqueue(0), maxsize(0), queuesize(0),expComputed(false) { } + + void initSize(int size) {growTo(size);exp = 2.0/(size+1);} // Init size of bounded size queue + + void push(T x) { + expComputed = false; + if (queuesize==maxsize) { + assert(last==first); // The queue is full, next value to enter will replace oldest one + sumofqueue -= elems[last]; + if ((++last) == maxsize) last = 0; + } else + queuesize++; + sumofqueue += x; + elems[first] = x; + if ((++first) == maxsize) {first = 0;last = 0;} + } + + T peek() { assert(queuesize>0); return elems[last]; } + void pop() {sumofqueue-=elems[last]; queuesize--; if ((++last) == maxsize) last = 0;} + + uint64_t getsum() const {return sumofqueue;} + unsigned int getavg() const {return (unsigned int)(sumofqueue/((uint64_t)queuesize));} + int maxSize() const {return maxsize;} + double getavgDouble() const { + double tmp = 0; + for(int i=0;i + +#include "SolverTypes.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// DIMACS Parser: + +template +static void readClause(B& in, Solver& S, vec& lits) { + int parsed_lit, var; + lits.clear(); + for (;;){ + parsed_lit = parseInt(in); + if (parsed_lit == 0) break; + var = abs(parsed_lit)-1; + while (var >= S.nVars()) S.newVar(); + lits.push( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) ); + } +} + +template +static void parse_DIMACS_main(B& in, Solver& S) { + vec lits; + int vars = 0; + int clauses = 0; + int cnt = 0; + for (;;){ + skipWhitespace(in); + if (*in == EOF) break; + else if (*in == 'p'){ + if (eagerMatch(in, "p cnf")){ + vars = parseInt(in); + clauses = parseInt(in); + // SATRACE'06 hack + // if (clauses > 4000000) + // S.eliminate(true); + }else{ + printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + } + } else if (*in == 'c' || *in == 'p') + skipLine(in); + else{ + cnt++; + readClause(in, S, lits); + S.addClause_(lits); } + } + if (vars != S.nVars()) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of variables.\n"); + if (cnt != clauses) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of clauses.\n"); +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Heap.h b/lib/abcsat/abc/Heap.h new file mode 100644 index 0000000..99142d8 --- /dev/null +++ b/lib/abcsat/abc/Heap.h @@ -0,0 +1,154 @@ +/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Heap_h +#define Glucose_Heap_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(vec& ns) { + int i; + for (i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + int i; + for (i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/IntTypes.h b/lib/abcsat/abc/IntTypes.h new file mode 100644 index 0000000..bddcebe --- /dev/null +++ b/lib/abcsat/abc/IntTypes.h @@ -0,0 +1,49 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_IntTypes_h +#define Glucose_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +#define __STDC_LIMIT_MACROS +# include "pstdint.h" +//# include + +#endif + +#include + +#ifndef PRIu64 +#define PRIu64 "lu" +#define PRIi64 "ld" +#endif +//================================================================================================= + +#include "abc_namespaces.h" + +#endif diff --git a/lib/abcsat/abc/Map.h b/lib/abcsat/abc/Map.h new file mode 100644 index 0000000..47b7eef --- /dev/null +++ b/lib/abcsat/abc/Map.h @@ -0,0 +1,197 @@ +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Map_h +#define Glucose_Map_h + +#include "IntTypes.h" +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Default hash/equals functions +// + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Queue.h b/lib/abcsat/abc/Queue.h new file mode 100644 index 0000000..577f566 --- /dev/null +++ b/lib/abcsat/abc/Queue.h @@ -0,0 +1,73 @@ +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Queue_h +#define Glucose_Queue_h + +#include "abc/Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int j, i = 0; + for (j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/SimpSolver.h b/lib/abcsat/abc/SimpSolver.h new file mode 100644 index 0000000..04b1dab --- /dev/null +++ b/lib/abcsat/abc/SimpSolver.h @@ -0,0 +1,208 @@ +/************************************************************************************[SimpSolver.h] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_SimpSolver_h +#define Glucose_SimpSolver_h + +#include "Queue.h" +#include "Solver.h" +#include "abc_global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); + void addVar (Var v); + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + + // Memory managment: + // + virtual void reset(); + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + int parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + int eliminated_clauses; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + bool use_simplification; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr); + bool strengthenClause (CRef cr, Lit l); + void cleanUpClauses (); + bool implied (const vec& c); + void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +//inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline bool SimpSolver::isEliminated (Var v) const { return eliminated.size() > 0 ? eliminated[v] != 0 : 0; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +inline void SimpSolver::addVar(Var v) { while (v >= nVars()) newVar(); } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Solver.h b/lib/abcsat/abc/Solver.h new file mode 100644 index 0000000..a9a6f62 --- /dev/null +++ b/lib/abcsat/abc/Solver.h @@ -0,0 +1,493 @@ +/****************************************************************************************[Solver.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Solver_h +#define Glucose_Solver_h + +#include "Vec.h" +#include "Heap.h" +#include "Alg.h" +#include "SolverTypes.h" +#include "BoundedQueue.h" +#include "Constants.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Solver -- the main class: + +class Solver { +public: + + int SolverType; // ABC identifies Glucose's type as 0 + + // Constructor/Destructor: + // + Solver(); + virtual ~Solver(); + + // ABC callbacks + void * pCnfMan; // external CNF manager + int(*pCnfFunc)(void * p, int, int*); // external callback. messages: 0: unsat; 1: sat; -1: still working + int nCallConfl; // callback will be called every this number of conflicts + bool terminate_search_early; // used to stop the solver early if it as instructed by an external caller + int * pstop; // another callback + uint64_t nRuntimeLimit; // runtime limit + vec user_vec; + vec user_lits; + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + void addVar (Var v); // Add enough variables to make sure there is variable v. + + bool addClause (const vec& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + void printLit(Lit l); + void printClause(CRef c); + void printInitialClause(CRef c); + // Convenience versions of 'toDimacs()': + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () const; + + // Incremental mode + void setIncrementalMode(); + void initNbInitialVars(int nb); + void printIncrementalStats(); + + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void reset(); + virtual void garbageCollect(); // virtuality causes segfault for some reason + void checkGarbage(double gf); + void checkGarbage(); + + + + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + int verbosity; + int verbEveryConflicts; + int showModel; + // Constants For restarts + double K; + double R; + double sizeLBDQueue; + double sizeTrailQueue; + + // Constants for reduce DB + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB; + unsigned int lbLBDFrozenClause; + + // Constant for reducing clause + int lbSizeMinimizingClause; + unsigned int lbLBDMinimizingClause; + + double var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + // Certified UNSAT ( Thanks to Marijn Heule) + FILE* certifiedOutput; + bool certifiedUNSAT; + + + // Statistics: (read-only member variable) + // + int64_t nbRemovedClauses,nbReducedClauses,nbDL2,nbBin,nbUn,nbReduceDB,solves, starts, decisions, rnd_decisions, propagations, conflicts,conflictsRestarts,nbstopsrestarts,nbstopsrestartssame,lastblockatrestart; + int64_t dec_vars, clauses_literals, learnts_literals, max_literals, tot_literals; + +protected: + long curRestart; + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + + // Solver state: + // + int lastIndexRed; + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + double cla_inc; // Amount to bump next clause with. + vec activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + watchesBin; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + vec clauses; // List of problem clauses. + vec learnts; // List of learnt clauses. + + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec nbpos; + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap; // A priority queue of variables ordered with respect to the variable activity. + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + vec permDiff; // permDiff[var] contains the current conflict number... Used to count the number of LBD + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + vec lastDecisionLevel; +#endif + + ClauseAllocator ca; + + int nbclausesbeforereduce; // To know when it is time to reduce clause database + + bqueue trailQueue,lbdQueue; // Bounded queues for restarts. + float sumLBD; // used to compute the global average of LBD. Restarts... + int sumAssumptions; + + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + unsigned int MYFLAG; + + + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + + // Variables added for incremental mode + int incremental; // Use incremental SAT Solver + int nbVarsInitialFormula; // nb VAR in formula without assumptions (incremental SAT) + double totalTime4Sat,totalTime4Unsat; + int nbSatCalls,nbUnsatCalls; + vec assumptionPositions,initialPositions; + + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, vec & selectors, int& out_btlevel,unsigned int &nblevels,unsigned int &szWithoutSelectors); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int nof_conflicts); // Search for a given number of conflicts. + lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). + void reduceDB (); // Reduce the set of learnt clauses. + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap (); + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void removeClause (CRef cr); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + unsigned int computeLBD(const vec & lits,int end=-1); + unsigned int computeLBD(const Clause &c); + void minimisationWithBinaryResolution(vec &out_learnt); + + void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + int level (Var x) const; + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + inline bool isSelector(Var v) {return (incremental && v>nbVarsInitialFormula);} + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } +inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } +inline void Solver::varBumpActivity(Var v, double inc) { + if ( (activity[v] += inc) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) + order_heap.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) + ca[learnts[i]].activity() *= (float)1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect();} + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline bool Solver::locked (const Clause& c) const { + if(c.size()>2) + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; + return + (value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c) + || + (value(c[1]) == l_True && reason(var(c[1])) != CRef_Undef && ca.lea(reason(var(c[1]))) == &c); + } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) dec_vars++; + else if (!b && decision[v]) dec_vars--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + +inline void Solver::addVar(Var v) { while (v >= nVars()) newVar(); } + +//================================================================================================= +// Debug etc: + + +inline void Solver::printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void Solver::printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } +} + +inline void Solver::printInitialClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + if(!isSelector(var(c[i]))) { + printLit(c[i]); + printf(" "); + } + } +} + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/SolverTypes.h b/lib/abcsat/abc/SolverTypes.h new file mode 100644 index 0000000..34dc2cc --- /dev/null +++ b/lib/abcsat/abc/SolverTypes.h @@ -0,0 +1,437 @@ +/***********************************************************************************[SolverTypes.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Glucose_SolverTypes_h +#define Glucose_SolverTypes_h + +#include + +#include "IntTypes.h" +#include "Alg.h" +#include "Vec.h" +#include "Map.h" +#include "Alloc.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + + +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + +#define l_True (Gluco::lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants. +#define l_False (Gluco::lbool((uint8_t)1)) +#define l_Undef (Gluco::lbool((uint8_t)2)) + +class lbool { + uint8_t value; + +public: + explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return (((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value))) != 0; } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause; +typedef RegionAllocator::Ref CRef; + +class Clause { + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned has_extra : 1; + unsigned reloced : 1; + unsigned lbd : 26; + unsigned canbedel : 1; + unsigned size : 32; + unsigned szWithoutSelectors : 32; + + } header; + union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, bool use_extra, bool learnt) { + header.mark = 0; + header.learnt = learnt; + header.has_extra = use_extra; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.canbedel = 1; + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.has_extra){ + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); } + } + +public: + void calcAbstraction() { + assert(header.has_extra); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + bool has_extra () const { return header.has_extra; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + float& activity () { assert(header.has_extra); return data[header.size].act; } + uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; } + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + void setLBD(int i) {header.lbd = i;} + // unsigned int& lbd () { return header.lbd; } + unsigned int lbd () const { return header.lbd; } + void setCanBeDel(bool b) {header.canbedel = b;} + bool canBeDel() {return header.canbedel;} + void setSizeWithoutSelectors (unsigned int n) {header.szWithoutSelectors = n; } + unsigned int sizeWithoutSelectors () const { return header.szWithoutSelectors; } + +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + +const CRef CRef_Undef = RegionAllocator::Ref_Undef; +class ClauseAllocator : public RegionAllocator +{ + static int clauseWord32Size(int size, bool has_extra){ + return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / sizeof(uint32_t); } + public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + bool use_extra = learnt | extra_clause_field; + + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), use_extra)); + new (lea(cid)) Clause(ps, use_extra, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free_(CRef cid) + { + Clause& c = operator[](cid); + RegionAllocator::free_(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()) { + to[cr].activity() = c.activity(); + to[cr].setLBD(c.lbd()); + to[cr].setSizeWithoutSelectors(c.sizeWithoutSelectors()); + to[cr].setCanBeDel(c.canBeDel()); + } + else if (to[cr].has_extra()) to[cr].calcAbstraction(); + } +}; + + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + + public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void clean (const Idx& idx); + void smudge (const Idx& idx){ + if (dirty[toInt(idx)] == 0){ + dirty[toInt(idx)] = 1; + dirties.push(idx); + } + } + + void clear(bool free = true){ + occs .clear(free); + dirty .clear(free); + dirties.clear(free); + } +}; + + +template +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + + public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.has_extra); assert(other.header.has_extra); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Sort.h b/lib/abcsat/abc/Sort.h new file mode 100644 index 0000000..b82f240 --- /dev/null +++ b/lib/abcsat/abc/Sort.h @@ -0,0 +1,101 @@ +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Sort_h +#define Glucose_Sort_h + +#include "Vec.h" + +//================================================================================================= +// Some sorting algorithms for vec's + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/Vec.h b/lib/abcsat/abc/Vec.h new file mode 100644 index 0000000..e52dace --- /dev/null +++ b/lib/abcsat/abc/Vec.h @@ -0,0 +1,135 @@ +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Vec_h +#define Glucose_Vec_h + +#include +#include + +#include "IntTypes.h" +#include "XAlloc.h" +#include "abc_global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but + // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not + // happen given the way capacities are calculated (below). Essentially, all capacities are + // even, but INT_MAX is odd. + + const T& last (void) const { return data[sz-1]; } + T& last (void) { return data[sz-1]; } + + // Vector interface: + const T& operator [] (int index) const { return data[index]; } + T& operator [] (int index) { return data[index]; } + + // Duplicatation (preferred instead): + void copyTo(vec& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/XAlloc.h b/lib/abcsat/abc/XAlloc.h new file mode 100644 index 0000000..3fa8142 --- /dev/null +++ b/lib/abcsat/abc/XAlloc.h @@ -0,0 +1,53 @@ +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Glucose_XAlloc_h +#define Glucose_XAlloc_h + +#include +#include +#include + +#include "abc_namespaces.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else { + return mem; + } +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/abcsat/abc/abc_global.h b/lib/abcsat/abc/abc_global.h new file mode 100644 index 0000000..9ca6d92 --- /dev/null +++ b/lib/abcsat/abc/abc_global.h @@ -0,0 +1,422 @@ +/**CFile**************************************************************** + + FileName [abc_global.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Global declarations.] + + Synopsis [Global declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Jan 30, 2009.] + + Revision [$Id: abc_global.h,v 1.00 2009/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__util__abc_global_h +#define ABC__misc__util__abc_global_h + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#ifdef _MSC_VER +//#define inline __inline // compatible with MS VS 6.0 +#pragma warning(push) +#pragma warning(disable : 4152) // warning C4152: nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4244) // warning C4244: '+=' : conversion from 'int ' to 'unsigned short ', possible loss of data +#pragma warning(disable : 4302) // warning C4302: 'type cast': truncation from 'void *' to 'pabc::ABC_PTRINT_T' +#pragma warning(disable : 4311) // warning C4311: 'type cast': pointer truncation from 'void *' to 'pabc::ABC_PTRINT_T' +#pragma warning(disable : 4312) // warning C4312: 'type cast': conversion from 'pabc::ABC_PTRINT_T' to 'void *' of greater size +#pragma warning(disable : 4514) // warning C4514: 'Vec_StrPop' : unreferenced inline function has been removed +#pragma warning(disable : 4710) // warning C4710: function 'Vec_PtrGrow' not inlined +//#pragma warning( disable : 4273 ) +#endif + +#ifdef WIN32 + #ifdef WIN32_NO_DLL + #define ABC_DLLEXPORT + #define ABC_DLLIMPORT + #else + #define ABC_DLLEXPORT __declspec(dllexport) + #define ABC_DLLIMPORT __declspec(dllimport) + #endif +#else /* defined(WIN32) */ +#define ABC_DLLIMPORT +#endif /* defined(WIN32) */ + +#ifndef ABC_DLL +#define ABC_DLL ABC_DLLIMPORT +#endif + +#if !defined(___unused) +#if defined(__GNUC__) +#define ___unused __attribute__ ((__unused__)) +#else +#define ___unused +#endif +#endif + +/* +#ifdef __cplusplus +#error "C++ code" +#else +#error "C code" +#endif +*/ + +#include +#include +#include +#include +#include +#include + +// catch memory leaks in Visual Studio +#ifdef WIN32 + #ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #endif +#endif + +#ifdef _WIN32 +#include +#endif + +#include "abc_namespaces.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +/** + * Pointer difference type; replacement for ptrdiff_t. + * This is a signed integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRDIFF_T; +#elif defined(LIN64) +typedef long ABC_PTRDIFF_T; +#elif defined(NT64) +typedef long long ABC_PTRDIFF_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef int ABC_PTRDIFF_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * Unsigned integral type that can contain a pointer. + * This is an unsigned integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRUINT_T; +#elif defined(LIN64) +typedef unsigned long ABC_PTRUINT_T; +#elif defined(NT64) +typedef unsigned long long ABC_PTRUINT_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef unsigned int ABC_PTRUINT_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * Signed integral type that can contain a pointer. + * This is a signed integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRINT_T; +#elif defined(LIN64) +typedef long ABC_PTRINT_T; +#elif defined(NT64) +typedef long long ABC_PTRINT_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef int ABC_PTRINT_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * 64-bit signed integral type. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_INT64_T; +#elif defined(LIN64) +typedef long ABC_INT64_T; +#elif defined(NT64) || defined(LIN) +typedef long long ABC_INT64_T; +#elif defined(WIN32) || defined(NT) +typedef signed __int64 ABC_INT64_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * 64-bit unsigned integral type. + */ +typedef uint64_t ABC_UINT64_T; + +#ifdef LIN + #define ABC_CONST(number) number ## ULL +#else // LIN64 and windows + #define ABC_CONST(number) number +#endif + +typedef ABC_UINT64_T word; +typedef ABC_INT64_T iword; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_INFINITY (1000000000) + +#define ABC_SWAP(Type, a, b) { Type t = a; a = b; b = t; } + +#define ABC_PRT(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec\n", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTr(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec\r", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTn(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec ", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTP(a,t,T) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec (%6.2f %%)\n", 1.0*(t)/(CLOCKS_PER_SEC), (T)? 100.0*(t)/(T) : 0.0)) +#define ABC_PRM(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB\n", 1.0*(f)/(1<<20))) +#define ABC_PRMr(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB\r", 1.0*(f)/(1<<20))) +#define ABC_PRMn(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB ", 1.0*(f)/(1<<20))) +#define ABC_PRMP(a,f,F) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB (%6.2f %%)\n", (1.0*(f)/(1<<20)), ((F)? 100.0*(f)/(F) : 0.0) ) ) + +#define ABC_ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#define ABC_CALLOC(type, num) ((type *) calloc((num), sizeof(type))) +#define ABC_FALLOC(type, num) ((type *) memset(malloc(sizeof(type) * (num)), 0xff, sizeof(type) * (num))) +#define ABC_FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define ABC_REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) + +static inline int Abc_AbsInt( int a ) { return a < 0 ? -a : a; } +static inline int Abc_MaxInt( int a, int b ) { return a > b ? a : b; } +static inline int Abc_MinInt( int a, int b ) { return a < b ? a : b; } +static inline word Abc_MaxWord( word a, word b ) { return a > b ? a : b; } +static inline word Abc_MinWord( word a, word b ) { return a < b ? a : b; } +static inline float Abc_AbsFloat( float a ) { return a < 0 ? -a : a; } +static inline float Abc_MaxFloat( float a, float b ) { return a > b ? a : b; } +static inline float Abc_MinFloat( float a, float b ) { return a < b ? a : b; } +static inline double Abc_AbsDouble( double a ) { return a < 0 ? -a : a; } +static inline double Abc_MaxDouble( double a, double b ) { return a > b ? a : b; } +static inline double Abc_MinDouble( double a, double b ) { return a < b ? a : b; } + +static inline int Abc_Float2Int( float Val ) { union { int x; float y; } v; v.y = Val; return v.x; } +static inline float Abc_Int2Float( int Num ) { union { int x; float y; } v; v.x = Num; return v.y; } +static inline word Abc_Dbl2Word( double Dbl ) { union { word x; double y; } v; v.y = Dbl; return v.x; } +static inline double Abc_Word2Dbl( word Num ) { union { word x; double y; } v; v.x = Num; return v.y; } +static inline int Abc_Base2Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n >>= 1, r++ ) {}; return r; } +static inline int Abc_Base10Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n /= 10, r++ ) {}; return r; } +static inline int Abc_Base16Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n /= 16, r++ ) {}; return r; } +static inline char * Abc_UtilStrsav( char * s ) { return s ? strcpy(ABC_ALLOC(char, strlen(s)+1), s) : NULL; } +static inline int Abc_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Abc_Bit6WordNum( int nBits ) { return (nBits>>6) + ((nBits&63) > 0); } +static inline int Abc_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Abc_Truth6WordNum( int nVars ) { return nVars <= 6 ? 1 : (1 << (nVars - 6)); } +static inline int Abc_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Abc_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Abc_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } +static inline unsigned Abc_InfoMask( int nVar ) { return (~(unsigned)0) >> (32-nVar); } + +static inline int Abc_Var2Lit( int Var, int c ) { assert(Var >= 0 && !(c >> 1)); return Var + Var + c; } +static inline int Abc_Lit2Var( int Lit ) { assert(Lit >= 0); return Lit >> 1; } +static inline int Abc_LitIsCompl( int Lit ) { assert(Lit >= 0); return Lit & 1; } +static inline int Abc_LitNot( int Lit ) { assert(Lit >= 0); return Lit ^ 1; } +static inline int Abc_LitNotCond( int Lit, int c ) { assert(Lit >= 0); return Lit ^ (int)(c > 0); } +static inline int Abc_LitRegular( int Lit ) { assert(Lit >= 0); return Lit & ~01; } +static inline int Abc_Lit2LitV( int * pMap, int Lit ) { assert(Lit >= 0); return Abc_Var2Lit( pMap[Abc_Lit2Var(Lit)], Abc_LitIsCompl(Lit) ); } +static inline int Abc_Lit2LitL( int * pMap, int Lit ) { assert(Lit >= 0); return Abc_LitNotCond( pMap[Abc_Lit2Var(Lit)], Abc_LitIsCompl(Lit) ); } + +static inline int Abc_Ptr2Int( void * p ) { return (int)(ABC_PTRINT_T)p; } +static inline void * Abc_Int2Ptr( int i ) { return (void *)(ABC_PTRINT_T)i; } +static inline word Abc_Ptr2Wrd( void * p ) { return (word)(ABC_PTRUINT_T)p; } +static inline void * Abc_Wrd2Ptr( word i ) { return (void *)(ABC_PTRUINT_T)i; } + +static inline int Abc_Var2Lit2( int Var, int Att ) { assert(!(Att >> 2)); return (Var << 2) + Att; } +static inline int Abc_Lit2Var2( int Lit ) { assert(Lit >= 0); return Lit >> 2; } +static inline int Abc_Lit2Att2( int Lit ) { assert(Lit >= 0); return Lit & 3; } +static inline int Abc_Var2Lit3( int Var, int Att ) { assert(!(Att >> 3)); return (Var << 3) + Att; } +static inline int Abc_Lit2Var3( int Lit ) { assert(Lit >= 0); return Lit >> 3; } +static inline int Abc_Lit2Att3( int Lit ) { assert(Lit >= 0); return Lit & 7; } +static inline int Abc_Var2Lit4( int Var, int Att ) { assert(!(Att >> 4)); return (Var << 4) + Att; } +static inline int Abc_Lit2Var4( int Lit ) { assert(Lit >= 0); return Lit >> 4; } +static inline int Abc_Lit2Att4( int Lit ) { assert(Lit >= 0); return Lit & 15; } + +// time counting +typedef ABC_INT64_T abctime; +static inline abctime Abc_Clock() +{ +#if (defined(LIN) || defined(LIN64)) && !(__APPLE__ & __MACH__) && !defined(__MINGW32__) + struct timespec ts; +#ifdef _WIN32 + if ( clock_gettime(0, &ts) < 0 ) + return (abctime)-1; +#else + if ( clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) < 0 ) + return (abctime)-1; +#endif + abctime res = ((abctime) ts.tv_sec) * CLOCKS_PER_SEC; + res += (((abctime) ts.tv_nsec) * CLOCKS_PER_SEC) / 1000000000; + return res; +#else + return (abctime) clock(); +#endif +} + +// bridge communication +#define BRIDGE_NETLIST 106 +#define BRIDGE_ABS_NETLIST 107 +extern int Gia_ManToBridgeText( FILE * pFile, int Size, unsigned char * pBuffer ); +extern int Gia_ManToBridgeAbsNetlist( FILE * pFile, void * p, int pkg_type ); + +// string printing +extern char * vnsprintf(const char* format, va_list args); +extern char * nsprintf(const char* format, ...); + + +// misc printing procedures +enum Abc_VerbLevel +{ + ABC_PROMPT = -2, + ABC_ERROR = -1, + ABC_WARNING = 0, + ABC_STANDARD = 1, + ABC_VERBOSE = 2 +}; +static inline void Abc_Print( int level, const char * format, ... ) +{ + extern ABC_DLL int Abc_FrameIsBridgeMode(); + va_list args; + + if ( ! Abc_FrameIsBridgeMode() ){ + if ( level == ABC_ERROR ) + printf( "Error: " ); + else if ( level == ABC_WARNING ) + printf( "Warning: " ); + }else{ + if ( level == ABC_ERROR ) + Gia_ManToBridgeText( stdout, (int)strlen("Error: "), (unsigned char*)"Error: " ); + else if ( level == ABC_WARNING ) + Gia_ManToBridgeText( stdout, (int)strlen("Warning: "), (unsigned char*)"Warning: " ); + } + + va_start( args, format ); + if ( Abc_FrameIsBridgeMode() ) + { + char * tmp = vnsprintf( format, args ); + Gia_ManToBridgeText( stdout, (int)strlen(tmp), (unsigned char*)tmp ); + free( tmp ); + } + else + vprintf( format, args ); + va_end( args ); +} + +static inline void Abc_PrintInt( int i ) +{ + double v3 = (double)i/1000; + double v6 = (double)i/1000000; + + Abc_Print( 1, " " ); + + if ( i > -1000 && i < 1000 ) + Abc_Print( 1, " %4d", i ); + + else if ( v3 > -9.995 && v3 < 9.995 ) + Abc_Print( 1, "%4.2fk", v3 ); + else if ( v3 > -99.95 && v3 < 99.95 ) + Abc_Print( 1, "%4.1fk", v3 ); + else if ( v3 > -999.5 && v3 < 999.5 ) + Abc_Print( 1, "%4.0fk", v3 ); + + else if ( v6 > -9.995 && v6 < 9.995 ) + Abc_Print( 1, "%4.2fm", v6 ); + else if ( v6 > -99.95 && v6 < 99.95 ) + Abc_Print( 1, "%4.1fm", v6 ); + else if ( v6 > -999.5 && v6 < 999.5 ) + Abc_Print( 1, "%4.0fm", v6 ); +} + +static inline void Abc_PrintTime( int level, const char * pStr, abctime time ) +{ + ABC_PRT( pStr, time ); +} + +static inline void Abc_PrintTimeP( int level, const char * pStr, abctime time, abctime Time ) +{ + ABC_PRTP( pStr, time, Time ); +} + +static inline void Abc_PrintMemoryP( int level, const char * pStr, int mem, int Mem ) +{ + ABC_PRMP( pStr, mem, Mem ); +} + +// Returns the next prime >= p +static inline int Abc_PrimeCudd( unsigned int p ) +{ + int i,pn; + p--; + do { + p++; + if (p&1) + { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) + { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } + else + pn = 0; + } while (!pn); + return(p); + +} // end of Cudd_Prime + + +// sorting +extern void Abc_MergeSort( int * pInput, int nSize ); +extern int * Abc_MergeSortCost( int * pCosts, int nSize ); +extern void Abc_QuickSort1( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSort2( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSort3( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSortCostData( int * pCosts, int nSize, int fDecrease, word * pData, int * pResult ); +extern int * Abc_QuickSortCost( int * pCosts, int nSize, int fDecrease ); + + +ABC_NAMESPACE_HEADER_END + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/lib/abcsat/abc/abc_namespaces.h b/lib/abcsat/abc/abc_namespaces.h new file mode 100644 index 0000000..8cd681c --- /dev/null +++ b/lib/abcsat/abc/abc_namespaces.h @@ -0,0 +1,74 @@ +/**CFile**************************************************************** + + FileName [abc_namespaces.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Namespace logic.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Nov 20, 2015.] + + Revision [] + +***********************************************************************/ + +#ifndef ABC__misc__util__abc_namespaces_h +#define ABC__misc__util__abc_namespaces_h + + +//////////////////////////////////////////////////////////////////////// +/// NAMESPACES /// +//////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +# ifdef ABC_NAMESPACE +# define ABC_NAMESPACE_HEADER_START namespace ABC_NAMESPACE { +# define ABC_NAMESPACE_HEADER_END } +# define ABC_NAMESPACE_CXX_HEADER_START ABC_NAMESPACE_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END ABC_NAMESPACE_HEADER_END +# define ABC_NAMESPACE_IMPL_START namespace ABC_NAMESPACE { +# define ABC_NAMESPACE_IMPL_END } +# define ABC_NAMESPACE_PREFIX ABC_NAMESPACE:: +# define ABC_NAMESPACE_USING_NAMESPACE using namespace ABC_NAMESPACE; +# else +# define ABC_NAMESPACE_HEADER_START extern "C" { +# define ABC_NAMESPACE_HEADER_END } +# define ABC_NAMESPACE_CXX_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END +# define ABC_NAMESPACE_IMPL_START +# define ABC_NAMESPACE_IMPL_END +# define ABC_NAMESPACE_PREFIX +# define ABC_NAMESPACE_USING_NAMESPACE +# endif // #ifdef ABC_NAMESPACE +#ifdef SATOKO_NAMESPACE + #define SATOKO_NAMESPACE_HEADER_START namespace SATOKO_NAMESPACE { + #define SATOKO_NAMESPACE_HEADER_END } + #define SATOKO_NAMESPACE_CXX_HEADER_START ABC_NAMESPACE_HEADER_START + #define SATOKO_NAMESPACE_CXX_HEADER_END ABC_NAMESPACE_HEADER_END + #define SATOKO_NAMESPACE_IMPL_START namespace SATOKO_NAMESPACE { + #define SATOKO_NAMESPACE_IMPL_END } + #define SATOKO_NAMESPACE_PREFIX SATOKO_NAMESPACE:: + #define SATOKO_NAMESPACE_USING_NAMESPACE using namespace SATOKO_NAMESPACE; +#endif +#else +# define ABC_NAMESPACE_HEADER_START +# define ABC_NAMESPACE_HEADER_END +# define ABC_NAMESPACE_CXX_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END +# define ABC_NAMESPACE_IMPL_START +# define ABC_NAMESPACE_IMPL_END +# define ABC_NAMESPACE_PREFIX +# define ABC_NAMESPACE_USING_NAMESPACE +#endif // #ifdef __cplusplus + +#endif // #ifndef ABC__misc__util__abc_namespaces_h + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/lib/abcsat/abc/pstdint.h b/lib/abcsat/abc/pstdint.h new file mode 100644 index 0000000..989d016 --- /dev/null +++ b/lib/abcsat/abc/pstdint.h @@ -0,0 +1,919 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2016 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.16.0 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is critically + * important for making several classes of applications portable + * including cryptography, hashing, variable length integer libraries + * and so on. But for most developers its likely useful just for + * programming sanity. + * + * The problem is that some compiler vendors chose to ignore the C99 + * standard and some older compilers have no opportunity to be updated. + * Because of this situation, simply including stdint.h in your code + * makes it unportable. + * + * So that's what this file is all about. It's an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. Even compilers + * that already come with stdint.h can use this file instead without + * any loss of functionality. A few things that should be noted about + * this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current version, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * PRINTF_UINTMAX_DEC_WIDTH + * PRINTF_UINT64_DEC_WIDTH + * PRINTF_UINT32_DEC_WIDTH + * PRINTF_UINT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * Florian Wobbe + * Christopher Sean Morrison + * Mikkel Fahnoe Jorgensen + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "l" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# else +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# if (UINT_MAX == UINT32_MAX) +# define PRINTF_INT32_MODIFIER "" +# else +# define PRINTF_INT32_MODIFIER "l" +# endif +# endif +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_UINT64_HEX_WIDTH +# define PRINTF_UINT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_UINT32_HEX_WIDTH +# define PRINTF_UINT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_UINT16_HEX_WIDTH +# define PRINTF_UINT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_UINT8_HEX_WIDTH +# define PRINTF_UINT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +# endif +# ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_UINTMAX_HEX_WIDTH +# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif +# ifndef PRINTF_UINTMAX_DEC_WIDTH +# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +/* + * I have no idea what is the truly correct thing to do on older Solaris. + * From some online discussions, this seems to be what is being + * recommended. For people who actually are developing on older Solaris, + * what I would like to know is, does this define all of the relevant + * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is + * considered optional. + */ + +#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)-1) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks) +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks) +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks) +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks) +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks) +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks) +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) && !defined(vxWorks) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif +#ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee has defined *int*_fast*_t types as well. This, + * of course, defies rationality -- you can't know what will be fast + * just from the type itself. Even for a given architecture, compatible + * implementations might have different performance characteristics. + * Developers are warned to stay away from these types when using this + * or any other stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# else +/* #error "This platform might not be supported yet" */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are + * not defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits) + +#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } + +#define X_SIZE_MAX ((size_t)-1) + +int main () { + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + +#define STR(v) #v +#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) +#ifdef INT64_MAX + Q(int64_t) +#endif + +#if UINT_MAX < X_SIZE_MAX + printf ("UINT_MAX < X_SIZE_MAX\n"); +#else + printf ("UINT_MAX >= X_SIZE_MAX\n"); +#endif + printf ("%" PRINTF_INT64_MODIFIER "u vs %" PRINTF_INT64_MODIFIER "u\n", UINT_MAX, X_SIZE_MAX); + + return EXIT_SUCCESS; +} + +#endif diff --git a/lib/abcsat/abc/satClause.h b/lib/abcsat/abc/satClause.h new file mode 100644 index 0000000..1a0bd74 --- /dev/null +++ b/lib/abcsat/abc/satClause.h @@ -0,0 +1,485 @@ +/**CFile**************************************************************** + + FileName [satMem.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver.] + + Synopsis [Memory management.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: satMem.h,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__sat__bsat__satMem_h +#define ABC__sat__bsat__satMem_h + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4200) +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//#define LEARNT_MAX_START_DEFAULT 0 +#define LEARNT_MAX_START_DEFAULT 10000 +#define LEARNT_MAX_INCRE_DEFAULT 1000 +#define LEARNT_MAX_RATIO_DEFAULT 50 + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//================================================================================================= +// Clause datatype + minor functions: + +typedef struct clause_t clause; +struct clause_t +{ + unsigned lrn : 1; + unsigned mark : 1; + unsigned partA : 1; + unsigned lbd : 8; + unsigned size : 21; + lit lits[0]; +}; + +// learned clauses have "hidden" literal (c->lits[c->size]) to store clause ID + +// data-structure for logging entries +// memory is allocated in 2^nPageSize word-sized pages +// the first 'word' of each page are stores the word limit + +// although clause memory pieces are aligned to 64-bit words +// the integer clause handles are in terms of 32-bit unsigneds +// allowing for the first bit to be used for labeling 2-lit clauses + + +typedef struct Sat_Mem_t_ Sat_Mem_t; +struct Sat_Mem_t_ +{ + int nEntries[2]; // entry count + int BookMarkH[2]; // bookmarks for handles + int BookMarkE[2]; // bookmarks for entries + int iPage[2]; // current memory page + int nPageSize; // page log size in terms of ints + unsigned uPageMask; // page mask + unsigned uLearnedMask; // learned mask + int nPagesAlloc; // page count allocated + int ** pPages; // page pointers +}; + +static inline int Sat_MemLimit( int * p ) { return p[0]; } +static inline int Sat_MemIncLimit( int * p, int nInts ) { return p[0] += nInts; } +static inline void Sat_MemWriteLimit( int * p, int nInts ) { p[0] = nInts; } + +static inline int Sat_MemHandPage( Sat_Mem_t * p, cla h ) { return h >> p->nPageSize; } +static inline int Sat_MemHandShift( Sat_Mem_t * p, cla h ) { return h & p->uPageMask; } + +static inline int Sat_MemIntSize( int size, int lrn ) { return (size + 2 + lrn) & ~01; } +static inline int Sat_MemClauseSize( clause * p ) { return Sat_MemIntSize(p->size, p->lrn); } +static inline int Sat_MemClauseSize2( clause * p ) { return Sat_MemIntSize(p->size, 1); } + +//static inline clause * Sat_MemClause( Sat_Mem_t * p, int i, int k ) { assert(i <= p->iPage[i&1] && k <= Sat_MemLimit(p->pPages[i])); return (clause *)(p->pPages[i] + k ); } +static inline clause * Sat_MemClause( Sat_Mem_t * p, int i, int k ) { assert( k ); return (clause *)(p->pPages[i] + k); } +//static inline clause * Sat_MemClauseHand( Sat_Mem_t * p, cla h ) { assert(Sat_MemHandPage(p, h) <= p->iPage[(h & p->uLearnedMask) > 0]); assert(Sat_MemHandShift(p, h) >= 2 && Sat_MemHandShift(p, h) < (int)p->uLearnedMask); return Sat_MemClause( p, Sat_MemHandPage(p, h), Sat_MemHandShift(p, h) ); } +static inline clause * Sat_MemClauseHand( Sat_Mem_t * p, cla h ) { return h ? Sat_MemClause( p, Sat_MemHandPage(p, h), Sat_MemHandShift(p, h) ) : NULL; } +static inline int Sat_MemEntryNum( Sat_Mem_t * p, int lrn ) { return p->nEntries[lrn]; } + +static inline cla Sat_MemHand( Sat_Mem_t * p, int i, int k ) { return (i << p->nPageSize) | k; } +static inline cla Sat_MemHandCurrent( Sat_Mem_t * p, int lrn ) { return (p->iPage[lrn] << p->nPageSize) | Sat_MemLimit( p->pPages[p->iPage[lrn]] ); } + +static inline int Sat_MemClauseUsed( Sat_Mem_t * p, cla h ) { return h < p->BookMarkH[(h & p->uLearnedMask) > 0]; } + +static inline double Sat_MemMemoryHand( Sat_Mem_t * p, cla h ) { return 1.0 * ((Sat_MemHandPage(p, h) + 2)/2 * (1 << (p->nPageSize+2)) + Sat_MemHandShift(p, h) * 4); } +static inline double Sat_MemMemoryUsed( Sat_Mem_t * p, int lrn ) { return Sat_MemMemoryHand( p, Sat_MemHandCurrent(p, lrn) ); } +static inline double Sat_MemMemoryAllUsed( Sat_Mem_t * p ) { return Sat_MemMemoryUsed( p, 0 ) + Sat_MemMemoryUsed( p, 1 ); } +static inline double Sat_MemMemoryAll( Sat_Mem_t * p ) { return 1.0 * (p->iPage[0] + p->iPage[1] + 2) * (1 << (p->nPageSize+2)); } + +// p is memory storage +// c is clause pointer +// i is page number +// k is page offset + +// print problem clauses NOT in proof mode +#define Sat_MemForEachClause( p, c, i, k ) \ + for ( i = 0; i <= p->iPage[0]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize(c) ) if ( i == 0 && k == 2 ) {} else + +// print problem clauses in proof mode +#define Sat_MemForEachClause2( p, c, i, k ) \ + for ( i = 0; i <= p->iPage[0]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize2(c) ) if ( i == 0 && k == 2 ) {} else + +#define Sat_MemForEachLearned( p, c, i, k ) \ + for ( i = 1; i <= p->iPage[1]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize(c) ) + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int clause_from_lit( lit l ) { return l + l + 1; } +static inline int clause_is_lit( cla h ) { return (h & 1); } +static inline lit clause_read_lit( cla h ) { return (lit)(h >> 1); } + +static inline int clause_learnt_h( Sat_Mem_t * p, cla h ) { return (h & p->uLearnedMask) > 0; } +static inline int clause_learnt( clause * c ) { return c->lrn; } +static inline int clause_id( clause * c ) { return c->lits[c->size]; } +static inline void clause_set_id( clause * c, int id ) { c->lits[c->size] = id; } +static inline int clause_size( clause * c ) { return c->size; } +static inline lit * clause_begin( clause * c ) { return c->lits; } +static inline lit * clause_end( clause * c ) { return c->lits + c->size; } +static inline void clause_print_( clause * c ) +{ + int i; + printf( "{ " ); + for ( i = 0; i < clause_size(c); i++ ) + printf( "%d ", (clause_begin(c)[i] & 1)? -(clause_begin(c)[i] >> 1) : clause_begin(c)[i] >> 1 ); + printf( "}\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocating vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemCountL( Sat_Mem_t * p ) +{ + clause * c; + int i, k, Count = 0; + Sat_MemForEachLearned( p, c, i, k ) + Count++; + return Count; +} + +/**Function************************************************************* + + Synopsis [Allocating vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemAlloc_( Sat_Mem_t * p, int nPageSize ) +{ + assert( nPageSize > 8 && nPageSize < 32 ); + memset( p, 0, sizeof(Sat_Mem_t) ); + p->nPageSize = nPageSize; + p->uLearnedMask = (unsigned)(1 << nPageSize); + p->uPageMask = (unsigned)((1 << nPageSize) - 1); + p->nPagesAlloc = 256; + p->pPages = ABC_CALLOC( int *, p->nPagesAlloc ); + p->pPages[0] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + p->pPages[1] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + p->iPage[0] = 0; + p->iPage[1] = 1; + Sat_MemWriteLimit( p->pPages[0], 2 ); + Sat_MemWriteLimit( p->pPages[1], 2 ); +} +static inline Sat_Mem_t * Sat_MemAlloc( int nPageSize ) +{ + Sat_Mem_t * p; + p = ABC_CALLOC( Sat_Mem_t, 1 ); + Sat_MemAlloc_( p, nPageSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Resetting vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemRestart( Sat_Mem_t * p ) +{ + p->nEntries[0] = 0; + p->nEntries[1] = 0; + p->iPage[0] = 0; + p->iPage[1] = 1; + Sat_MemWriteLimit( p->pPages[0], 2 ); + Sat_MemWriteLimit( p->pPages[1], 2 ); +} + +/**Function************************************************************* + + Synopsis [Sets the bookmark.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemBookMark( Sat_Mem_t * p ) +{ + p->BookMarkE[0] = p->nEntries[0]; + p->BookMarkE[1] = p->nEntries[1]; + p->BookMarkH[0] = Sat_MemHandCurrent( p, 0 ); + p->BookMarkH[1] = Sat_MemHandCurrent( p, 1 ); +} +static inline void Sat_MemRollBack( Sat_Mem_t * p ) +{ + p->nEntries[0] = p->BookMarkE[0]; + p->nEntries[1] = p->BookMarkE[1]; + p->iPage[0] = Sat_MemHandPage( p, p->BookMarkH[0] ); + p->iPage[1] = Sat_MemHandPage( p, p->BookMarkH[1] ); + Sat_MemWriteLimit( p->pPages[p->iPage[0]], Sat_MemHandShift( p, p->BookMarkH[0] ) ); + Sat_MemWriteLimit( p->pPages[p->iPage[1]], Sat_MemHandShift( p, p->BookMarkH[1] ) ); +} + +/**Function************************************************************* + + Synopsis [Freeing vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemFree_( Sat_Mem_t * p ) +{ + int i; + for ( i = 0; i < p->nPagesAlloc; i++ ) + ABC_FREE( p->pPages[i] ); + ABC_FREE( p->pPages ); +} +static inline void Sat_MemFree( Sat_Mem_t * p ) +{ + Sat_MemFree_( p ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Creates new clause.] + + Description [The resulting clause is fully initialized.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemAppend( Sat_Mem_t * p, int * pArray, int nSize, int lrn, int fPlus1 ) +{ + clause * c; + int * pPage = p->pPages[p->iPage[lrn]]; + int nInts = Sat_MemIntSize( nSize, lrn | fPlus1 ); + assert( nInts + 3 < (1 << p->nPageSize) ); + // need two extra at the begining of the page and one extra in the end + if ( Sat_MemLimit(pPage) + nInts + 2 >= (1 << p->nPageSize) ) + { + p->iPage[lrn] += 2; + if ( p->iPage[lrn] >= p->nPagesAlloc ) + { + p->pPages = ABC_REALLOC( int *, p->pPages, p->nPagesAlloc * 2 ); + memset( p->pPages + p->nPagesAlloc, 0, sizeof(int *) * p->nPagesAlloc ); + p->nPagesAlloc *= 2; + } + if ( p->pPages[p->iPage[lrn]] == NULL ) + p->pPages[p->iPage[lrn]] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + pPage = p->pPages[p->iPage[lrn]]; + Sat_MemWriteLimit( pPage, 2 ); + } + pPage[Sat_MemLimit(pPage)] = 0; + c = (clause *)(pPage + Sat_MemLimit(pPage)); + c->size = nSize; + c->lrn = lrn; + if ( pArray ) + memcpy( c->lits, pArray, sizeof(int) * nSize ); + if ( lrn | fPlus1 ) + c->lits[c->size] = p->nEntries[lrn]; + p->nEntries[lrn]++; + Sat_MemIncLimit( pPage, nInts ); + return Sat_MemHandCurrent(p, lrn) - nInts; +} + +/**Function************************************************************* + + Synopsis [Shrinking vector size.] + + Description [] + + SideEffects [This procedure does not update the number of entries.] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemShrink( Sat_Mem_t * p, int h, int lrn ) +{ + assert( clause_learnt_h(p, h) == lrn ); + assert( h && h <= Sat_MemHandCurrent(p, lrn) ); + p->iPage[lrn] = Sat_MemHandPage(p, h); + Sat_MemWriteLimit( p->pPages[p->iPage[lrn]], Sat_MemHandShift(p, h) ); +} + + +/**Function************************************************************* + + Synopsis [Compacts learned clauses by removing marked entries.] + + Description [Returns the number of remaining entries.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemCompactLearned( Sat_Mem_t * p, int fDoMove ) +{ + clause * c, * cPivot = NULL; + int i, k, iNew = 1, kNew = 2, nInts, fStartLooking, Counter = 0; + int hLimit = Sat_MemHandCurrent(p, 1); + if ( hLimit == Sat_MemHand(p, 1, 2) ) + return 0; + if ( fDoMove && p->BookMarkH[1] ) + { + // move the pivot + assert( p->BookMarkH[1] >= Sat_MemHand(p, 1, 2) && p->BookMarkH[1] <= hLimit ); + // get the pivot and remember it may be pointed offlimit + cPivot = Sat_MemClauseHand( p, p->BookMarkH[1] ); + if ( p->BookMarkH[1] < hLimit && !cPivot->mark ) + { + p->BookMarkH[1] = cPivot->lits[cPivot->size]; + cPivot = NULL; + } + // else find the next used clause after cPivot + } + // iterate through the learned clauses + fStartLooking = 0; + Sat_MemForEachLearned( p, c, i, k ) + { + assert( c->lrn ); + // skip marked entry + if ( c->mark ) + { + // if pivot is a marked clause, start looking for the next non-marked one + if ( cPivot && cPivot == c ) + { + fStartLooking = 1; + cPivot = NULL; + } + continue; + } + // if we started looking before, we found it! + if ( fStartLooking ) + { + fStartLooking = 0; + p->BookMarkH[1] = c->lits[c->size]; + } + // compute entry size + nInts = Sat_MemClauseSize(c); + assert( !(nInts & 1) ); + // check if we need to scroll to the next page + if ( kNew + nInts >= (1 << p->nPageSize) ) + { + // set the limit of the current page + if ( fDoMove ) + Sat_MemWriteLimit( p->pPages[iNew], kNew ); + // move writing position to the new page + iNew += 2; + kNew = 2; + } + if ( fDoMove ) + { + // make sure the result is the same as previous dry run + assert( c->lits[c->size] == Sat_MemHand(p, iNew, kNew) ); + // only copy the clause if it has changed + if ( i != iNew || k != kNew ) + { + memmove( p->pPages[iNew] + kNew, c, sizeof(int) * nInts ); +// c = Sat_MemClause( p, iNew, kNew ); // assersions do not hold during dry run + c = (clause *)(p->pPages[iNew] + kNew); + assert( nInts == Sat_MemClauseSize(c) ); + } + // set the new ID value + c->lits[c->size] = Counter; + } + else // remember the address of the clause in the new location + c->lits[c->size] = Sat_MemHand(p, iNew, kNew); + // update writing position + kNew += nInts; + assert( iNew <= i && kNew < (1 << p->nPageSize) ); + // update counter + Counter++; + } + if ( fDoMove ) + { + // update the counter + p->nEntries[1] = Counter; + // update the page count + p->iPage[1] = iNew; + // set the limit of the last page + Sat_MemWriteLimit( p->pPages[iNew], kNew ); + // check if the pivot need to be updated + if ( p->BookMarkH[1] ) + { + if ( cPivot ) + { + p->BookMarkH[1] = Sat_MemHandCurrent(p, 1); + p->BookMarkE[1] = p->nEntries[1]; + } + else + p->BookMarkE[1] = clause_id(Sat_MemClauseHand( p, p->BookMarkH[1] )); + } + + } + return Counter; +} + + +ABC_NAMESPACE_HEADER_END + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcsat/abc/satSolver.h b/lib/abcsat/abc/satSolver.h new file mode 100644 index 0000000..bedc6dc --- /dev/null +++ b/lib/abcsat/abc/satSolver.h @@ -0,0 +1,661 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef ABC__sat__bsat__satSolver_h +#define ABC__sat__bsat__satSolver_h + + +#include +#include +#include +#include + +#include "satVec.h" +#include "satClause.h" +#include + +ABC_NAMESPACE_HEADER_START + +//================================================================================================= +// Public interface: + +struct sat_solver_t; +typedef struct sat_solver_t sat_solver; + +extern sat_solver* sat_solver_new(void); +extern sat_solver* zsat_solver_new_seed(double seed); +extern void sat_solver_delete(sat_solver* s); + +extern void solver_init_activities(sat_solver* s); + +extern int sat_solver_addclause(sat_solver* s, lit* begin, lit* end); +extern int sat_solver_clause_new(sat_solver* s, lit* begin, lit* end, int learnt); +extern int sat_solver_simplify(sat_solver* s); +extern int sat_solver_solve(sat_solver* s, lit* begin, lit* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal); +extern int sat_solver_solve_internal(sat_solver* s); +extern int sat_solver_solve_lexsat(sat_solver* s, int * pLits, int nLits); +extern int sat_solver_minimize_assumptions( sat_solver* s, int * pLits, int nLits, int nConfLimit ); +extern int sat_solver_minimize_assumptions2( sat_solver* s, int * pLits, int nLits, int nConfLimit ); +extern int sat_solver_push(sat_solver* s, int p); +extern void sat_solver_pop(sat_solver* s); +extern void sat_solver_set_resource_limits(sat_solver* s, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal); +extern void sat_solver_restart( sat_solver* s ); +extern void zsat_solver_restart_seed( sat_solver* s, double seed ); +extern void sat_solver_rollback( sat_solver* s ); + +extern int sat_solver_nvars(sat_solver* s); +extern int sat_solver_nclauses(sat_solver* s); +extern int sat_solver_nconflicts(sat_solver* s); +extern double sat_solver_memory(sat_solver* s); +extern int sat_solver_count_assigned(sat_solver* s); + +extern int sat_solver_addvar(sat_solver* s); +extern void sat_solver_setnvars(sat_solver* s,int n); +extern int sat_solver_get_var_value(sat_solver* s, int v); +extern void sat_solver_set_var_activity(sat_solver* s, int * pVars, int nVars); + +extern void Sat_SolverWriteDimacs( sat_solver * p, char * pFileName, lit* assumptionsBegin, lit* assumptionsEnd, int incrementVars ); +extern void Sat_SolverPrintStats( FILE * pFile, sat_solver * p ); +extern int * Sat_SolverGetModel( sat_solver * p, int * pVars, int nVars ); +extern void Sat_SolverDoubleClauses( sat_solver * p, int iVar ); + +// trace recording +extern void Sat_SolverTraceStart( sat_solver * pSat, char * pName ); +extern void Sat_SolverTraceStop( sat_solver * pSat ); +extern void Sat_SolverTraceWrite( sat_solver * pSat, int * pBeg, int * pEnd, int fRoot ); + +// clause storage +extern void sat_solver_store_alloc( sat_solver * s ); +extern void sat_solver_store_write( sat_solver * s, char * pFileName ); +extern void sat_solver_store_free( sat_solver * s ); +extern void sat_solver_store_mark_roots( sat_solver * s ); +extern void sat_solver_store_mark_clauses_a( sat_solver * s ); +extern void * sat_solver_store_release( sat_solver * s ); + +//================================================================================================= +// Solver representation: + +//struct clause_t; +//typedef struct clause_t clause; + +struct varinfo_t; +typedef struct varinfo_t varinfo; + +struct sat_solver_t +{ + int size; // nof variables + int cap; // size of varmaps + int qhead; // Head index of queue. + int qtail; // Tail index of queue. + + // clauses + Sat_Mem_t Mem; + int hLearnts; // the first learnt clause + int hBinary; // the special binary clause + clause * binary; + veci* wlists; // watcher lists + + // rollback + int iVarPivot; // the pivot for variables + int iTrailPivot; // the pivot for trail + int hProofPivot; // the pivot for proof records + + // activities + int VarActType = 0; + int ClaActType = 0; + word var_inc; // Amount to bump next variable with. + word var_inc2; // Amount to bump next variable with. + word var_decay; // INVERSE decay factor for variable activity: stores 1/decay. + word* activity; // A heuristic measurement of the activity of a variable. + word* activity2; // backup variable activity + unsigned cla_inc; // Amount to bump next clause with. + unsigned cla_decay; // INVERSE decay factor for clause activity: stores 1/decay. + veci act_clas; // contain clause activities + + char * pFreqs; // how many times this variable was assigned a value + int nVarUsed; + +// varinfo * vi; // variable information + int* levels; // + char* assigns; // Current values of variables. + char* polarity; // + char* tags; // + char* loads; // + + int* orderpos; // Index in variable order. + int* reasons; // + lit* trail; + veci tagged; // (contains: var) + veci stack; // (contains: var) + + veci order; // Variable order. (heap) (contains: var) + veci trail_lim; // Separator indices for different decision levels in 'trail'. (contains: int) +// veci model; // If problem is solved, this vector contains the model (contains: lbool). + int * model; // If problem is solved, this vector contains the model (contains: lbool). + veci conf_final; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + int root_level; // Level of first proper decision. + int simpdb_assigns;// Number of top-level assignments at last 'simplifyDB()'. + int simpdb_props; // Number of propagations before next 'simplifyDB()'. + double random_seed = 91648253; + double progress_estimate; + int verbosity; // Verbosity level. 0=silent, 1=some progress report, 2=everything + int fVerbose; + int fPrintClause; + + stats_t stats; + int nLearntMax; // max number of learned clauses + int nLearntStart; // starting learned clause limit + int nLearntDelta; // delta of learned clause limit + int nLearntRatio; // ratio percentage of learned clauses + int nDBreduces; // number of DB reductions + + ABC_INT64_T nConfLimit; // external limit on the number of conflicts + ABC_INT64_T nInsLimit; // external limit on the number of implications + abctime nRuntimeLimit; // external limit on runtime + + veci act_vars; // variables whose activity has changed + double* factors; // the activity factors + int nRestarts; // the number of local restarts + int nCalls; // the number of local restarts + int nCalls2; // the number of local restarts + veci unit_lits; // variables whose activity has changed + veci pivot_vars; // pivot variables + + int fSkipSimplify; // set to one to skip simplification of the clause database + int fNotUseRandom; // do not allow random decisions with a fixed probability + int fNoRestarts; // disables periodic restarts + + int * pGlobalVars; // for experiments with global vars during interpolation + // clause store + void * pStore; + int fSolved; + + // trace recording + FILE * pFile; + int nClauses; + int nRoots; + + veci temp_clause; // temporary storage for a CNF clause + + // assignment storage + veci user_vars; // variable IDs + veci user_values; // values of these variables + + // CNF loading + void * pCnfMan; // external CNF manager + int(*pCnfFunc)(void * p, int); // external callback + + // termination callback + int RunId; // SAT id in this run + int(*pFuncStop)(int); // callback to terminate +}; + +static inline clause * clause_read( sat_solver * s, cla h ) +{ + return Sat_MemClauseHand( &s->Mem, h ); +} + +static inline int sat_solver_var_value( sat_solver* s, int v ) +{ + assert( v >= 0 && v < s->size ); + return (int)(s->model[v] == l_True); +} +static inline int sat_solver_var_literal( sat_solver* s, int v ) +{ + assert( v >= 0 && v < s->size ); + return toLitCond( v, s->model[v] != l_True ); +} +static inline void sat_solver_flip_print_clause( sat_solver* s ) +{ + s->fPrintClause ^= 1; +} +static inline void sat_solver_act_var_clear(sat_solver* s) +{ + int i; + if ( s->VarActType == 0 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = (1 << 10); + s->var_inc = (1 << 5); + } + else if ( s->VarActType == 1 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = 0; + s->var_inc = 1; + } + else if ( s->VarActType == 2 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = Xdbl_Const1(); + s->var_inc = Xdbl_Const1(); + } + else assert(0); +} +static inline void sat_solver_compress(sat_solver* s) +{ + if ( s->qtail != s->qhead ) + { + int RetValue = sat_solver_simplify(s); + assert( RetValue != 0 ); + (void) RetValue; + } +} +static inline void sat_solver_delete_p( sat_solver ** ps ) +{ + if ( *ps ) + sat_solver_delete( *ps ); + *ps = NULL; +} +static inline void sat_solver_clean_polarity(sat_solver* s, int * pVars, int nVars ) +{ + int i; + for ( i = 0; i < nVars; i++ ) + s->polarity[pVars[i]] = 0; +} +static inline void sat_solver_set_polarity(sat_solver* s, int * pVars, int nVars ) +{ + int i; + for ( i = 0; i < s->size; i++ ) + s->polarity[i] = 0; + for ( i = 0; i < nVars; i++ ) + s->polarity[pVars[i]] = 1; +} +static inline void sat_solver_set_literal_polarity(sat_solver* s, int * pLits, int nLits ) +{ + int i; + for ( i = 0; i < nLits; i++ ) + s->polarity[Abc_Lit2Var(pLits[i])] = !Abc_LitIsCompl(pLits[i]); +} + +static inline int sat_solver_final(sat_solver* s, int ** ppArray) +{ + *ppArray = s->conf_final.ptr; + return s->conf_final.size; +} + +static inline abctime sat_solver_set_runtime_limit(sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = s->nRuntimeLimit; + s->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +static inline int sat_solver_set_random(sat_solver* s, int fNotUseRandom) +{ + int fNotUseRandomOld = s->fNotUseRandom; + s->fNotUseRandom = fNotUseRandom; + return fNotUseRandomOld; +} + +static inline void sat_solver_bookmark(sat_solver* s) +{ + if (s->qtail != s->qhead) + { + int status = sat_solver_simplify(s); + assert(status!=0); + assert(s->qtail == s->qhead); + } + assert( s->qhead == s->qtail ); + s->iVarPivot = s->size; + s->iTrailPivot = s->qhead; + Sat_MemBookMark( &s->Mem ); + if ( s->activity2 ) + { + s->var_inc2 = s->var_inc; + memcpy( s->activity2, s->activity, sizeof(word) * s->iVarPivot ); + } +} +static inline void sat_solver_set_pivot_variables( sat_solver* s, int * pPivots, int nPivots ) +{ + s->pivot_vars.cap = nPivots; + s->pivot_vars.size = nPivots; + s->pivot_vars.ptr = pPivots; +} +static inline int sat_solver_count_usedvars(sat_solver* s) +{ + int i, nVars = 0; + for ( i = 0; i < s->size; i++ ) + if ( s->pFreqs[i] ) + { + s->pFreqs[i] = 0; + nVars++; + } + return nVars; +} +static inline void sat_solver_set_runid( sat_solver *s, int id ) +{ + s->RunId = id; +} +static inline void sat_solver_set_stop_func( sat_solver *s, int (*fnct)(int) ) +{ + s->pFuncStop = fnct; +} + +static inline int sat_solver_add_const( sat_solver * pSat, int iVar, int fCompl ) +{ + lit Lits[1]; + int Cid; + assert( iVar >= 0 ); + + Lits[0] = toLitCond( iVar, fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 1 ); + assert( Cid ); + (void)Cid; + return 1; +} +static inline int sat_solver_add_buffer( sat_solver * pSat, int iVarA, int iVarB, int fCompl ) +{ + lit Lits[2]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVarB, !fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + if ( Cid == 0 ) + return 0; + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 1 ); + Lits[1] = toLitCond( iVarB, fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + if ( Cid == 0 ) + return 0; + assert( Cid ); + return 2; +} +static inline int sat_solver_add_buffer_enable( sat_solver * pSat, int iVarA, int iVarB, int iVarEn, int fCompl ) +{ + lit Lits[3]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 && iVarEn >= 0 ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVarB, !fCompl ); + Lits[2] = toLitCond( iVarEn, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 1 ); + Lits[1] = toLitCond( iVarB, fCompl ); + Lits[2] = toLitCond( iVarEn, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + (void)Cid; + return 2; +} +static inline int sat_solver_add_and( sat_solver * pSat, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1, int fCompl ) +{ + lit Lits[3]; + int Cid; + + Lits[0] = toLitCond( iVar, !fCompl ); + Lits[1] = toLitCond( iVar0, fCompl0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, !fCompl ); + Lits[1] = toLitCond( iVar1, fCompl1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar0, !fCompl0 ); + Lits[2] = toLitCond( iVar1, !fCompl1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + (void)Cid; + return 3; +} +static inline int sat_solver_add_xor( sat_solver * pSat, int iVarA, int iVarB, int iVarC, int fCompl ) +{ + lit Lits[3]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 && iVarC >= 0 ); + + Lits[0] = toLitCond( iVarA, !fCompl ); + Lits[1] = toLitCond( iVarB, 1 ); + Lits[2] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, !fCompl ); + Lits[1] = toLitCond( iVarB, 0 ); + Lits[2] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, fCompl ); + Lits[1] = toLitCond( iVarB, 1 ); + Lits[2] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, fCompl ); + Lits[1] = toLitCond( iVarB, 0 ); + Lits[2] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + (void)Cid; + return 4; +} +static inline int sat_solver_add_mux( sat_solver * pSat, int iVarZ, int iVarC, int iVarT, int iVarE, int iComplC, int iComplT, int iComplE, int iComplZ ) +{ + lit Lits[3]; + int Cid; + assert( iVarC >= 0 && iVarT >= 0 && iVarE >= 0 && iVarZ >= 0 ); + + Lits[0] = toLitCond( iVarC, 1 ^ iComplC ); + Lits[1] = toLitCond( iVarT, 1 ^ iComplT ); + Lits[2] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 1 ^ iComplC ); + Lits[1] = toLitCond( iVarT, 0 ^ iComplT ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 0 ^ iComplC ); + Lits[1] = toLitCond( iVarE, 1 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 0 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 0 ^ iComplC ); + Lits[1] = toLitCond( iVarE, 0 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + if ( iVarT == iVarE ) + return 4; + + Lits[0] = toLitCond( iVarT, 0 ^ iComplT ); + Lits[1] = toLitCond( iVarE, 0 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarT, 1 ^ iComplT ); + Lits[1] = toLitCond( iVarE, 1 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 0 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + (void)Cid; + return 6; +} +static inline int sat_solver_add_mux41( sat_solver * pSat, int iVarZ, int iVarC0, int iVarC1, int iVarD0, int iVarD1, int iVarD2, int iVarD3 ) +{ + lit Lits[4]; + int Cid; + assert( iVarC0 >= 0 && iVarC1 >= 0 && iVarD0 >= 0 && iVarD1 >= 0 && iVarD2 >= 0 && iVarD3 >= 0 && iVarZ >= 0 ); + + Lits[0] = toLitCond( iVarD0, 1 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD1, 1 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD2, 1 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD3, 1 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + + Lits[0] = toLitCond( iVarD0, 0 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD1, 0 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD2, 0 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD3, 0 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + (void)Cid; + return 8; +} +static inline int sat_solver_add_xor_and( sat_solver * pSat, int iVarF, int iVarA, int iVarB, int iVarC ) +{ + // F = (a (+) b) * c + lit Lits[4]; + int Cid; + assert( iVarF >= 0 && iVarA >= 0 && iVarB >= 0 && iVarC >= 0 ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarA, 1 ); + Lits[2] = toLitCond( iVarB, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarA, 0 ); + Lits[2] = toLitCond( iVarB, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 0 ); + Lits[1] = toLitCond( iVarA, 1 ); + Lits[2] = toLitCond( iVarB, 0 ); + Lits[3] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 0 ); + Lits[1] = toLitCond( iVarA, 0 ); + Lits[2] = toLitCond( iVarB, 1 ); + Lits[3] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + (void)Cid; + return 5; +} +static inline int sat_solver_add_constraint( sat_solver * pSat, int iVar, int iVar2, int fCompl ) +{ + lit Lits[2]; + int Cid; + assert( iVar >= 0 ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar2, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar2, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + (void)Cid; + return 2; +} + +static inline int sat_solver_add_half_sorter( sat_solver * pSat, int iVarA, int iVarB, int iVar0, int iVar1 ) +{ + lit Lits[3]; + int Cid; + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVar0, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVar1, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarB, 0 ); + Lits[1] = toLitCond( iVar0, 1 ); + Lits[2] = toLitCond( iVar1, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + (void)Cid; + return 3; +} + + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/abcsat/abc/satStore.h b/lib/abcsat/abc/satStore.h new file mode 100644 index 0000000..f2480a7 --- /dev/null +++ b/lib/abcsat/abc/satStore.h @@ -0,0 +1,158 @@ +/**CFile**************************************************************** + + FileName [satStore.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Proof recording.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: pr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__sat__bsat__satStore_h +#define ABC__sat__bsat__satStore_h + + +/* + The trace of SAT solving contains the original clauses of the problem + along with the learned clauses derived during SAT solving. + The first line of the resulting file contains 3 numbers instead of 2: + c +*/ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_HEADER_START + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#define STO_MAX(a,b) ((a) > (b) ? (a) : (b)) + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +/* +typedef unsigned lit; +// variable/literal conversions (taken from MiniSat) +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } +*/ + +typedef struct Sto_Cls_t_ Sto_Cls_t; +struct Sto_Cls_t_ +{ + Sto_Cls_t * pNext; // the next clause + Sto_Cls_t * pNext0; // the next 0-watch + Sto_Cls_t * pNext1; // the next 1-watch + int Id; // the clause ID + unsigned fA : 1; // belongs to A + unsigned fRoot : 1; // original clause + unsigned fVisit : 1; // visited clause + unsigned nLits : 24; // the number of literals + lit pLits[0]; // literals of this clause +}; + +typedef struct Sto_Man_t_ Sto_Man_t; +struct Sto_Man_t_ +{ + // general data + int nVars; // the number of variables + int nRoots; // the number of root clauses + int nClauses; // the number of all clauses + int nClausesA; // the number of clauses of A + Sto_Cls_t * pHead; // the head clause + Sto_Cls_t * pTail; // the tail clause + Sto_Cls_t * pEmpty; // the empty clause + // memory management + int nChunkSize; // the number of bytes in a chunk + int nChunkUsed; // the number of bytes used in the last chunk + char * pChunkLast; // the last memory chunk +}; + +// iterators through the clauses +#define Sto_ManForEachClause( p, pCls ) for( pCls = p->pHead; pCls; pCls = pCls->pNext ) +#define Sto_ManForEachClauseRoot( p, pCls ) for( pCls = p->pHead; pCls && pCls->fRoot; pCls = pCls->pNext ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== satStore.c ==========================================================*/ +extern Sto_Man_t * Sto_ManAlloc(); +extern void Sto_ManFree( Sto_Man_t * p ); +extern int Sto_ManAddClause( Sto_Man_t * p, lit * pBeg, lit * pEnd ); +extern int Sto_ManMemoryReport( Sto_Man_t * p ); +extern void Sto_ManMarkRoots( Sto_Man_t * p ); +extern void Sto_ManMarkClausesA( Sto_Man_t * p ); +extern void Sto_ManDumpClauses( Sto_Man_t * p, char * pFileName ); +extern int Sto_ManChangeLastClause( Sto_Man_t * p ); +extern Sto_Man_t * Sto_ManLoadClauses( char * pFileName ); + + +/*=== satInter.c ==========================================================*/ +typedef struct Int_Man_t_ Int_Man_t; +extern Int_Man_t * Int_ManAlloc(); +extern int * Int_ManSetGlobalVars( Int_Man_t * p, int nGloVars ); +extern void Int_ManFree( Int_Man_t * p ); +extern int Int_ManInterpolate( Int_Man_t * p, Sto_Man_t * pCnf, int fVerbose, unsigned ** ppResult ); + +/*=== satInterA.c ==========================================================*/ +typedef struct Inta_Man_t_ Inta_Man_t; +extern Inta_Man_t * Inta_ManAlloc(); +extern void Inta_ManFree( Inta_Man_t * p ); +extern void * Inta_ManInterpolate( Inta_Man_t * p, Sto_Man_t * pCnf, abctime TimeToStop, void * vVarsAB, int fVerbose ); + +/*=== satInterB.c ==========================================================*/ +typedef struct Intb_Man_t_ Intb_Man_t; +extern Intb_Man_t * Intb_ManAlloc(); +extern void Intb_ManFree( Intb_Man_t * p ); +extern void * Intb_ManInterpolate( Intb_Man_t * p, Sto_Man_t * pCnf, void * vVarsAB, int fVerbose ); + +/*=== satInterP.c ==========================================================*/ +typedef struct Intp_Man_t_ Intp_Man_t; +extern Intp_Man_t * Intp_ManAlloc(); +extern void Intp_ManFree( Intp_Man_t * p ); +extern void * Intp_ManUnsatCore( Intp_Man_t * p, Sto_Man_t * pCnf, int fLearned, int fVerbose ); +extern void Intp_ManUnsatCorePrintForBmc( FILE * pFile, Sto_Man_t * pCnf, void * vCore, void * vVarMap ); + + +ABC_NAMESPACE_HEADER_END + + + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcsat/abc/satVec.h b/lib/abcsat/abc/satVec.h new file mode 100644 index 0000000..120328e --- /dev/null +++ b/lib/abcsat/abc/satVec.h @@ -0,0 +1,169 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef ABC__sat__bsat__satVec_h +#define ABC__sat__bsat__satVec_h + +#include + +ABC_NAMESPACE_HEADER_START + + +// vector of 32-bit intergers (added for 64-bit portability) +struct veci_t { + int cap; + int size; + int* ptr; +}; +typedef struct veci_t veci; + +static inline void veci_new (veci* v) { + v->cap = 4; + v->size = 0; + v->ptr = (int*)ABC_ALLOC( char, sizeof(int)*v->cap); +} + +static inline void veci_delete (veci* v) { ABC_FREE(v->ptr); } +static inline int* veci_begin (veci* v) { return v->ptr; } +static inline int veci_size (veci* v) { return v->size; } +static inline void veci_resize (veci* v, int k) { + assert(k <= v->size); +// memset( veci_begin(v) + k, -1, sizeof(int) * (veci_size(v) - k) ); + v->size = k; +} // only safe to shrink !! +static inline int veci_pop (veci* v) { assert(v->size); return v->ptr[--v->size]; } +static inline void veci_push (veci* v, int e) +{ + if (v->size == v->cap) { +// int newsize = v->cap * 2;//+1; + int newsize = (v->cap < 4) ? v->cap * 2 : (v->cap / 2) * 3; + v->ptr = ABC_REALLOC( int, v->ptr, newsize ); + if ( v->ptr == NULL ) + { + printf( "Failed to realloc memory from %.1f MB to %.1f MB.\n", + 1.0 * v->cap / (1<<20), 1.0 * newsize / (1<<20) ); + fflush( stdout ); + } + v->cap = newsize; } + v->ptr[v->size++] = e; +} +static inline void veci_remove(veci* v, int e) +{ + int * ws = (int*)veci_begin(v); + int j = 0; + for (; ws[j] != e ; j++); + assert(j < veci_size(v)); + for (; j < veci_size(v)-1; j++) ws[j] = ws[j+1]; + veci_resize(v,veci_size(v)-1); +} + + +// vector of 32- or 64-bit pointers +struct vecp_t { + int cap; + int size; + void** ptr; +}; +typedef struct vecp_t vecp; + +static inline void vecp_new (vecp* v) { + v->size = 0; + v->cap = 4; + v->ptr = (void**)ABC_ALLOC( char, sizeof(void*)*v->cap); +} + +static inline void vecp_delete (vecp* v) { ABC_FREE(v->ptr); } +static inline void** vecp_begin (vecp* v) { return v->ptr; } +static inline int vecp_size (vecp* v) { return v->size; } +static inline void vecp_resize (vecp* v, int k) { assert(k <= v->size); v->size = k; } // only safe to shrink !! +static inline void vecp_push (vecp* v, void* e) +{ + if (v->size == v->cap) { +// int newsize = v->cap * 2;//+1; + int newsize = (v->cap < 4) ? v->cap * 2 : (v->cap / 2) * 3; + v->ptr = ABC_REALLOC( void*, v->ptr, newsize ); + v->cap = newsize; } + v->ptr[v->size++] = e; +} +static inline void vecp_remove(vecp* v, void* e) +{ + void** ws = vecp_begin(v); + int j = 0; + for (; ws[j] != e ; j++); + assert(j < vecp_size(v)); + for (; j < vecp_size(v)-1; j++) ws[j] = ws[j+1]; + vecp_resize(v,vecp_size(v)-1); +} + + + +//================================================================================================= +// Simple types: + +#ifndef __cplusplus +#ifndef false +# define false 0 +#endif +#ifndef true +# define true 1 +#endif +#endif + +typedef int lit; +typedef int cla; + +// Explicitly make it signed so promotion-to-int behavior doesn't vary +// across platforms that define signedness of char differently. +typedef signed char lbool; + +// CryptoMinisat defines it's own var_Undef values. +// When it's included we prefer the ABC version instead. +#ifdef var_Undef +#undef var_Undef +#endif + +static const int var_Undef = -1; +static const lit lit_Undef = -2; + +static const lbool l_Undef = 0; +static const lbool l_True = 1; +static const lbool l_False = -1; + +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } + +struct stats_t +{ + unsigned starts, clauses, learnts; + ABC_INT64_T decisions, propagations, inspects, conflicts; + ABC_INT64_T clauses_literals, learnts_literals, tot_literals; +}; +typedef struct stats_t stats_t; + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/abcsat/abc/system.h b/lib/abcsat/abc/system.h new file mode 100644 index 0000000..9e6bf11 --- /dev/null +++ b/lib/abcsat/abc/system.h @@ -0,0 +1,68 @@ +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_System_h +#define Glucose_System_h + + +#include "IntTypes.h" + +ABC_NAMESPACE_CXX_HEADER_START + +//------------------------------------------------------------------------------------------------- + +namespace Gluco { + +static inline double cpuTime(void); // CPU-time in seconds. + +} + +ABC_NAMESPACE_CXX_HEADER_END + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +ABC_NAMESPACE_CXX_HEADER_START + +static inline double Gluco::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +ABC_NAMESPACE_CXX_HEADER_END + + +#else +#include +#include +#include + +ABC_NAMESPACE_CXX_HEADER_START + +static inline double Gluco::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +ABC_NAMESPACE_CXX_HEADER_END + +#endif + +#endif diff --git a/lib/abcsat/abc/utilDouble.h b/lib/abcsat/abc/utilDouble.h new file mode 100644 index 0000000..877103b --- /dev/null +++ b/lib/abcsat/abc/utilDouble.h @@ -0,0 +1,224 @@ +/**CFile**************************************************************** + + FileName [utilDouble.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [] + + Synopsis [Double floating point number implementation.] + + Author [Alan Mishchenko, Bruno Schmitt] + + Affiliation [UC Berkeley / UFRGS] + + Date [Ver. 1.0. Started - February 11, 2017.] + + Revision [] + +***********************************************************************/ + +#ifndef ABC__sat__Xdbl__Xdbl_h +#define ABC__sat__Xdbl__Xdbl_h + +#include + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + The xdbl floating-point number is represented as a 64-bit unsigned int. + The number is (2^Exp)*Mnt, where Exp is a 16-bit exponent and Mnt is a + 48-bit mantissa. The decimal point is located between the MSB of Mnt, + which is always 1, and the remaining 15 digits of Mnt. + + Currently, only positive numbers are represented. + + The range of possible values is [1.0; 2^(2^16-1)*1.111111111111111] + that is, the smallest possible number is 1.0 and the largest possible + number is 2^(---16 ones---).(1.---47 ones---) + + Comparison of numbers can be done by comparing the underlying unsigned ints. + + Only addition, multiplication, and division by 2^n are currently implemented. +*/ + +typedef word xdbl; + +static inline word Xdbl_Exp( xdbl a ) { return a >> 48; } +static inline word Xdbl_Mnt( xdbl a ) { return (a << 16) >> 16; } + +static inline xdbl Xdbl_Create( word Exp, word Mnt ) { assert(!(Exp>>16) && (Mnt>>47)==(word)1); return (Exp<<48) | Mnt; } + +static inline xdbl Xdbl_Const1() { return Xdbl_Create( (word)0, (word)1 << 47 ); } +static inline xdbl Xdbl_Const2() { return Xdbl_Create( (word)1, (word)1 << 47 ); } +static inline xdbl Xdbl_Const3() { return Xdbl_Create( (word)1, (word)3 << 46 ); } +static inline xdbl Xdbl_Const12() { return Xdbl_Create( (word)3, (word)3 << 46 ); } +static inline xdbl Xdbl_Const1point5() { return Xdbl_Create( (word)0, (word)3 << 46 ); } +static inline xdbl Xdbl_Const2point5() { return Xdbl_Create( (word)1, (word)5 << 45 ); } +static inline xdbl Xdbl_Maximum() { return ~(word)0; } + +static inline double Xdbl_ToDouble( xdbl a ) { assert(Xdbl_Exp(a) < 1023); return Abc_Word2Dbl(((Xdbl_Exp(a) + 1023) << 52) | (((a<<17)>>17) << 5)); } +static inline xdbl Xdbl_FromDouble( double a ) { word A = Abc_Dbl2Word(a); assert(a >= 1.0); return Xdbl_Create((A >> 52)-1023, (((word)1) << 47) | ((A << 12) >> 17)); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adding two floating-point numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Add( xdbl a, xdbl b ) +{ + word Exp, Mnt; + if ( a < b ) a ^= b, b ^= a, a ^= b; + assert( a >= b ); + Mnt = Xdbl_Mnt(a) + (Xdbl_Mnt(b) >> (Xdbl_Exp(a) - Xdbl_Exp(b))); + Exp = Xdbl_Exp(a); + if ( Mnt >> 48 ) // new MSB is created + Exp++, Mnt >>= 1; + if ( Exp >> 16 ) // overflow + return Xdbl_Maximum(); + return Xdbl_Create( Exp, Mnt ); +} + +/**Function************************************************************* + + Synopsis [Multiplying two floating-point numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Mul( xdbl a, xdbl b ) +{ + word Exp, Mnt, MntA, MntB, MntAh, MntBh, MntAl, MntBl; + if ( a < b ) a ^= b, b ^= a, a ^= b; + assert( a >= b ); + MntA = Xdbl_Mnt(a); + MntB = Xdbl_Mnt(b); + MntAh = MntA>>32; + MntBh = MntB>>32; + MntAl = (MntA<<32)>>32; + MntBl = (MntB<<32)>>32; + Mnt = ((MntAh * MntBh) << 17) + ((MntAl * MntBl) >> 47) + ((MntAl * MntBh) >> 15) + ((MntAh * MntBl) >> 15); + Exp = Xdbl_Exp(a) + Xdbl_Exp(b); + if ( Mnt >> 48 ) // new MSB is created + Exp++, Mnt >>= 1; + if ( Exp >> 16 ) // overflow + return Xdbl_Maximum(); + return Xdbl_Create( Exp, Mnt ); +} + +/**Function************************************************************* + + Synopsis [Dividing floating point number by a degree of 2.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Div( xdbl a, unsigned Deg2 ) +{ + if ( Xdbl_Exp(a) >= (word)Deg2 ) + return Xdbl_Create( Xdbl_Exp(a) - Deg2, Xdbl_Mnt(a) ); + return Xdbl_Const1(); // underflow +} + +/**Function************************************************************* + + Synopsis [Testing procedure.] + + Description [Helpful link https://www.h-schmidt.net/FloatConverter/IEEE754.html] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Xdbl_Test() +{ + xdbl c1 = Xdbl_Const1(); + xdbl c2 = Xdbl_Const2(); + xdbl c3 = Xdbl_Const3(); + xdbl c12 = Xdbl_Const12(); + xdbl c1p5 = Xdbl_Const1point5(); + xdbl c2p5 = Xdbl_Const2point5(); + + xdbl c1_ = Xdbl_FromDouble(1.0); + xdbl c2_ = Xdbl_FromDouble(2.0); + xdbl c3_ = Xdbl_FromDouble(3.0); + xdbl c12_ = Xdbl_FromDouble(12.0); + xdbl c1p5_ = Xdbl_FromDouble(1.5); + xdbl c2p5_ = Xdbl_FromDouble(2.5); + + xdbl sum1 = Xdbl_Add(c1, c1p5); + xdbl mul1 = Xdbl_Mul(c2, c1p5); + + xdbl sum2 = Xdbl_Add(c1p5, c2p5); + xdbl mul2 = Xdbl_Mul(c1p5, c2p5); + + xdbl a = Xdbl_FromDouble(1.2929725); + xdbl b = Xdbl_FromDouble(10.28828287); + xdbl ab = Xdbl_Mul(a, b); + + xdbl ten100 = Xdbl_FromDouble( 1e100 ); + xdbl ten100_ = ABC_CONST(0x014c924d692ca61b); + + assert( ten100 == ten100_ ); + (void)ten100; + (void)ten100_; + +// float f1 = Xdbl_ToDouble(c1); +// Extra_PrintBinary( stdout, (int *)&c1, 32 ); printf( "\n" ); +// Extra_PrintBinary( stdout, (int *)&f1, 32 ); printf( "\n" ); + + printf( "1 = %lf\n", Xdbl_ToDouble(c1) ); + printf( "2 = %lf\n", Xdbl_ToDouble(c2) ); + printf( "3 = %lf\n", Xdbl_ToDouble(c3) ); + printf( "12 = %lf\n", Xdbl_ToDouble(c12) ); + printf( "1.5 = %lf\n", Xdbl_ToDouble(c1p5) ); + printf( "2.5 = %lf\n", Xdbl_ToDouble(c2p5) ); + + printf( "Converted 1 = %lf\n", Xdbl_ToDouble(c1_) ); + printf( "Converted 2 = %lf\n", Xdbl_ToDouble(c2_) ); + printf( "Converted 3 = %lf\n", Xdbl_ToDouble(c3_) ); + printf( "Converted 12 = %lf\n", Xdbl_ToDouble(c12_) ); + printf( "Converted 1.5 = %lf\n", Xdbl_ToDouble(c1p5_) ); + printf( "Converted 2.5 = %lf\n", Xdbl_ToDouble(c2p5_) ); + + printf( "1.0 + 1.5 = %lf\n", Xdbl_ToDouble(sum1) ); + printf( "2.0 * 1.5 = %lf\n", Xdbl_ToDouble(mul1) ); + + printf( "1.5 + 2.5 = %lf\n", Xdbl_ToDouble(sum2) ); + printf( "1.5 * 2.5 = %lf\n", Xdbl_ToDouble(mul2) ); + printf( "12 / 2^2 = %lf\n", Xdbl_ToDouble(Xdbl_Div(c12, 2)) ); + + printf( "12 / 2^2 = %lf\n", Xdbl_ToDouble(Xdbl_Div(c12, 2)) ); + + printf( "%.16lf * %.16lf = %.16lf (%.16lf)\n", Xdbl_ToDouble(a), Xdbl_ToDouble(b), Xdbl_ToDouble(ab), 1.2929725 * 10.28828287 ); + + assert( sum1 == c2p5 ); + assert( mul1 == c3 ); +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/abcsat/abc/vecInt.h b/lib/abcsat/abc/vecInt.h new file mode 100644 index 0000000..b8f8e20 --- /dev/null +++ b/lib/abcsat/abc/vecInt.h @@ -0,0 +1,2087 @@ +/**CFile**************************************************************** + + FileName [vecInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of integers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__vec__vecInt_h +#define ABC__misc__vec__vecInt_h + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) // warning C4244: '+=' : conversion from 'int ' to 'unsigned short ', possible loss of data +#endif + + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +ABC_NAMESPACE_HEADER_START + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Int_t_ Vec_Int_t; +struct Vec_Int_t_ +{ + int nCap; + int nSize; + int * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_IntForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStart( vVec, Entry, i, Start ) \ + for ( i = Start; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStop( vVec, Entry, i, Stop ) \ + for ( i = 0; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStartStop( vVec, Entry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_IntEntry(vVec, i)), 1); i-- ) +#define Vec_IntForEachEntryTwo( vVec1, vVec2, Entry1, Entry2, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec1)) && (((Entry1) = Vec_IntEntry(vVec1, i)), 1) && (((Entry2) = Vec_IntEntry(vVec2, i)), 1); i++ ) +#define Vec_IntForEachEntryDouble( vVec, Entry1, Entry2, i ) \ + for ( i = 0; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryDoubleStart( vVec, Entry1, Entry2, i, Start ) \ + for ( i = Start; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryTriple( vVec, Entry1, Entry2, Entry3, i ) \ + for ( i = 0; (i+2 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1) && (((Entry3) = Vec_IntEntry(vVec, i+2)), 1); i += 3 ) +#define Vec_IntForEachEntryThisNext( vVec, This, Next, i ) \ + for ( i = 0, (This) = (Next) = (Vec_IntSize(vVec) ? Vec_IntEntry(vVec, 0) : -1); (i+1 < Vec_IntSize(vVec)) && (((Next) = Vec_IntEntry(vVec, i+1)), 1); i += 2, (This) = (Next) ) +#define Vec_IntForEachEntryInVec( vVec2, vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec2, Vec_IntEntry(vVec, i))), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAlloc( int nCap ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} +static inline Vec_Int_t * Vec_IntAllocExact( int nCap ) +{ + Vec_Int_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStart( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(int) * nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartFull( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0xff, sizeof(int) * nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartRange( int First, int Range ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( Range ); + p->nSize = Range; + for ( i = 0; i < Range; i++ ) + p->pArray[i] = First + i; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStartNatural( int nSize ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArray( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArrayCopy( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ABC_ALLOC( int, nSize ); + memcpy( p->pArray, pArray, sizeof(int) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDup( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nSize; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(int) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDupArray( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntZero( Vec_Int_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntErase( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntFree( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFreeP( Vec_Int_t ** p ) +{ + if ( *p == NULL ) + return; + ABC_FREE( (*p)->pArray ); + ABC_FREE( (*p) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntReleaseArray( Vec_Int_t * p ) +{ + int * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntArray( Vec_Int_t * p ) +{ + return p->pArray; +} +static inline int ** Vec_IntArrayP( Vec_Int_t * p ) +{ + return &p->pArray; +} +static inline int * Vec_IntLimit( Vec_Int_t * p ) +{ + return p->pArray + p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSize( Vec_Int_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCap( Vec_Int_t * p ) +{ + return p->nCap; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_IntMemory( Vec_Int_t * p ) +{ + return !p ? 0.0 : 1.0 * sizeof(int) * p->nCap + sizeof(Vec_Int_t) ; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntry( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntEntryP( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntWriteEntry( Vec_Int_t * p, int i, int Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntAddToEntry( Vec_Int_t * p, int i, int Addition ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i] += Addition; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntUpdateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) < Value ) + Vec_IntWriteEntry( p, i, Value ); +} +static inline void Vec_IntDowndateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) > Value ) + Vec_IntWriteEntry( p, i, Value ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntryLast( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrow( Vec_Int_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrowResize( Vec_Int_t * p, int nCapMin ) +{ + p->nSize = nCapMin; + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFill( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} +static inline void Vec_IntFillTwo( Vec_Int_t * p, int nSize, int FillEven, int FillOdd ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = (i & 1) ? FillOdd : FillEven; + p->nSize = nSize; +} +static inline void Vec_IntFillNatural( Vec_Int_t * p, int nSize ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFillExtra( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + if ( nSize <= p->nSize ) + return; + if ( nSize > 2 * p->nCap ) + Vec_IntGrow( p, nSize ); + else if ( nSize > p->nCap ) + Vec_IntGrow( p, 2 * p->nCap ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntGetEntry( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntry( p, i ); +} +static inline int Vec_IntGetEntryFull( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + return Vec_IntEntry( p, i ); +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntGetEntryP( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntryP( p, i ); +} + +/**Function************************************************************* + + Synopsis [Inserts the entry even if the place does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSetEntry( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + Vec_IntWriteEntry( p, i, Entry ); +} +static inline void Vec_IntSetEntryFull( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + Vec_IntWriteEntry( p, i, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntShrink( Vec_Int_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntClear( Vec_Int_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPush( Vec_Int_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} +static inline void Vec_IntPushTwo( Vec_Int_t * p, int Entry1, int Entry2 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); +} +static inline void Vec_IntPushThree( Vec_Int_t * p, int Entry1, int Entry2, int Entry3 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); +} +static inline void Vec_IntPushFour( Vec_Int_t * p, int Entry1, int Entry2, int Entry3, int Entry4 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); + Vec_IntPush( p, Entry4 ); +} +static inline void Vec_IntPushArray( Vec_Int_t * p, int * pEntries, int nEntries ) +{ + int i; + for ( i = 0; i < nEntries; i++ ) + Vec_IntPush( p, pEntries[i] ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushFirst( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize - 1; i >= 1; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[0] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrder( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] > Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} +static inline void Vec_IntPushOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( Vec_IntEntry(vCost, p->pArray[i]) > Vec_IntEntry(vCost, Entry) ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrderReverse( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] < Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUniqueOrder( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrder( p, Entry ); + return 0; +} +static inline int Vec_IntPushUniqueOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrderCost( p, Entry, vCost ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUnique( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the next nWords entries in the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned * Vec_IntFetch( Vec_Int_t * p, int nWords ) +{ + if ( nWords == 0 ) + return NULL; + assert( nWords > 0 ); + p->nSize += nWords; + if ( p->nSize > p->nCap ) + { +// Vec_IntGrow( p, 2 * p->nSize ); + return NULL; + } + return ((unsigned *)p->pArray) + p->nSize - nWords; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPop( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFind( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntRemove( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i == p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} +static inline int Vec_IntRemove1( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 1; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i >= p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntDrop( Vec_Int_t * p, int i ) +{ + int k; + assert( i >= 0 && i < Vec_IntSize(p) ); + p->nSize--; + for ( k = i; k < p->nSize; k++ ) + p->pArray[k] = p->pArray[k+1]; +} + +/**Function************************************************************* + + Synopsis [Interts entry at the index iHere. Shifts other entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntInsert( Vec_Int_t * p, int iHere, int Entry ) +{ + int i; + assert( iHere >= 0 && iHere <= p->nSize ); + Vec_IntPush( p, 0 ); + for ( i = p->nSize - 1; i > iHere; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMax( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best < p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMin( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best > p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Reverses the order of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntReverseOrder( Vec_Int_t * p ) +{ + int i, Temp; + for ( i = 0; i < p->nSize/2; i++ ) + { + Temp = p->pArray[i]; + p->pArray[i] = p->pArray[p->nSize-1-i]; + p->pArray[p->nSize-1-i] = Temp; + } +} + +/**Function************************************************************* + + Synopsis [Removes odd entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemoveOdd( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i]; +} +static inline void Vec_IntRemoveEven( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i+1]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntInvert( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( 0 ); + if ( Vec_IntSize(p) == 0 ) + return vRes; + Vec_IntFill( vRes, Vec_IntFindMax(p) + 1, Fill ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntWriteEntry( vRes, Entry, i ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntCondense( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( Vec_IntSize(p) ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntPush( vRes, Entry ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSum( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += p->pArray[i]; + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountEntry( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == Entry); + return Counter; +} +static inline int Vec_IntCountLarger( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > Entry); + return Counter; +} +static inline int Vec_IntCountSmaller( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] < Entry); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountPositive( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > 0); + return Counter; +} +static inline int Vec_IntCountZero( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Checks if two vectors are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEqual( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + int i; + if ( p1->nSize != p2->nSize ) + return 0; + for ( i = 0; i < p1->nSize; i++ ) + if ( p1->pArray[i] != p2->pArray[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Counts the number of common entries.] + + Description [Assumes that the entries are non-negative integers that + are not very large, so inversion of the array can be performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountCommon( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + Vec_Int_t * vTemp; + int Entry, i, Counter = 0; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + vTemp = p1, p1 = p2, p2 = vTemp; + assert( Vec_IntSize(p1) >= Vec_IntSize(p2) ); + vTemp = Vec_IntInvert( p2, -1 ); + Vec_IntFillExtra( vTemp, Vec_IntFindMax(p1) + 1, -1 ); + Vec_IntForEachEntry( p1, Entry, i ) + if ( Vec_IntEntry(vTemp, Entry) >= 0 ) + Counter++; + Vec_IntFree( vTemp ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare1( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare2( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSort( Vec_Int_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} +static inline void Vec_IntSortMulti( Vec_Int_t * p, int nMulti, int fReverse ) +{ + assert( Vec_IntSize(p) % nMulti == 0 ); + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize/nMulti, nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize/nMulti, nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} + +/**Function************************************************************* + + Synopsis [Leaves only unique entries.] + + Description [Returns the number of duplicated entried found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqify( Vec_Int_t * p ) +{ + int i, k, RetValue; + if ( p->nSize < 2 ) + return 0; + Vec_IntSort( p, 0 ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( p->pArray[i] != p->pArray[i-1] ) + p->pArray[k++] = p->pArray[i]; + RetValue = p->nSize - k; + p->nSize = k; + return RetValue; +} +static inline int Vec_IntCountDuplicates( Vec_Int_t * p ) +{ + int RetValue; + Vec_Int_t * pDup = Vec_IntDup( p ); + Vec_IntUniqify( pDup ); + RetValue = Vec_IntSize(p) - Vec_IntSize(pDup); + Vec_IntFree( pDup ); + return RetValue; +} +static inline int Vec_IntCheckUniqueSmall( Vec_Int_t * p ) +{ + int i, k; + for ( i = 0; i < p->nSize; i++ ) + for ( k = i+1; k < p->nSize; k++ ) + if ( p->pArray[i] == p->pArray[k] ) + return 0; + return 1; +} +static inline int Vec_IntCountUnique( Vec_Int_t * p ) +{ + int i, Count = 0, Max = Vec_IntFindMax(p); + unsigned char * pPres = ABC_CALLOC( unsigned char, Max+1 ); + for ( i = 0; i < p->nSize; i++ ) + if ( pPres[p->pArray[i]] == 0 ) + pPres[p->pArray[i]] = 1, Count++; + ABC_FREE( pPres ); + return Count; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqifyPairs( Vec_Int_t * p ) +{ + int i, k, RetValue; + assert( p->nSize % 2 == 0 ); + if ( p->nSize < 4 ) + return 0; + Vec_IntSortMulti( p, 2, 0 ); + for ( i = k = 1; i < p->nSize/2; i++ ) + if ( p->pArray[2*i] != p->pArray[2*(i-1)] || p->pArray[2*i+1] != p->pArray[2*(i-1)+1] ) + { + p->pArray[2*k] = p->pArray[2*i]; + p->pArray[2*k+1] = p->pArray[2*i+1]; + k++; + } + RetValue = p->nSize/2 - k; + p->nSize = 2*k; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Vec_IntUniqueHashKeyDebug( unsigned char * pStr, int nChars, int TableMask ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + { + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + printf( "%d : ", c ); + printf( "%3d ", pStr[c] ); + printf( "%12u ", Key ); + printf( "%12u ", Key&TableMask ); + printf( "\n" ); + } + return Key; +} +static inline void Vec_IntUniqueProfile( Vec_Int_t * vData, int * pTable, int * pNexts, int TableMask, int nIntSize ) +{ + int i, Key, Counter; + for ( i = 0; i <= TableMask; i++ ) + { + Counter = 0; + for ( Key = pTable[i]; Key != -1; Key = pNexts[Key] ) + Counter++; + if ( Counter < 7 ) + continue; + printf( "%d\n", Counter ); + for ( Key = pTable[i]; Key != -1; Key = pNexts[Key] ) + { +// Extra_PrintBinary( stdout, (unsigned *)Vec_IntEntryP(vData, Key*nIntSize), 40 ), printf( "\n" ); +// Vec_IntUniqueHashKeyDebug( (unsigned char *)Vec_IntEntryP(vData, Key*nIntSize), 4*nIntSize, TableMask ); + } + } + printf( "\n" ); +} + +static inline unsigned Vec_IntUniqueHashKey2( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + return Key; +} + +static inline unsigned Vec_IntUniqueHashKey( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[16] = + { + 0x984b6ad9,0x18a6eed3,0x950353e2,0x6222f6eb,0xdfbedd47,0xef0f9023,0xac932a26,0x590eaf55, + 0x97d0a034,0xdc36cd2e,0x22736b37,0xdc9066b0,0x2eb2f98b,0x5d9c7baf,0x85747c9e,0x8aca1055 + }; + static unsigned s_BigPrimes2[16] = + { + 0x8d8a5ebe,0x1e6a15dc,0x197d49db,0x5bab9c89,0x4b55dea7,0x55dede49,0x9a6a8080,0xe5e51035, + 0xe148d658,0x8a17eb3b,0xe22e4b38,0xe5be2a9a,0xbe938cbb,0x3b981069,0x7f9c0c8e,0xf756df10 + }; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += s_BigPrimes2[(2*c)&15] * s_BigPrimes[(unsigned)pStr[c] & 15] + + s_BigPrimes2[(2*c+1)&15] * s_BigPrimes[(unsigned)pStr[c] >> 4]; + return Key; +} +static inline int * Vec_IntUniqueLookup( Vec_Int_t * vData, int i, int nIntSize, int * pNexts, int * pStart ) +{ + int * pData = Vec_IntEntryP( vData, i*nIntSize ); + for ( ; *pStart != -1; pStart = pNexts + *pStart ) + if ( !memcmp( pData, Vec_IntEntryP(vData, *pStart*nIntSize), sizeof(int) * nIntSize ) ) + return pStart; + return pStart; +} +static inline int Vec_IntUniqueCount( Vec_Int_t * vData, int nIntSize, Vec_Int_t ** pvMap ) +{ + int nEntries = Vec_IntSize(vData) / nIntSize; + int TableMask = (1 << pabc::Abc_Base2Log(nEntries)) - 1; + int * pTable = ABC_FALLOC( int, TableMask+1 ); + int * pNexts = ABC_FALLOC( int, TableMask+1 ); + int * pClass = ABC_ALLOC( int, nEntries ); + int i, Key, * pEnt, nUnique = 0; + assert( nEntries * nIntSize == Vec_IntSize(vData) ); + for ( i = 0; i < nEntries; i++ ) + { + pEnt = Vec_IntEntryP( vData, i*nIntSize ); + Key = TableMask & Vec_IntUniqueHashKey( (unsigned char *)pEnt, 4*nIntSize ); + pEnt = Vec_IntUniqueLookup( vData, i, nIntSize, pNexts, pTable+Key ); + if ( *pEnt == -1 ) + *pEnt = i, nUnique++; + pClass[i] = *pEnt; + } +// Vec_IntUniqueProfile( vData, pTable, pNexts, TableMask, nIntSize ); + ABC_FREE( pTable ); + ABC_FREE( pNexts ); + if ( pvMap ) + *pvMap = Vec_IntAllocArray( pClass, nEntries ); + else + ABC_FREE( pClass ); + return nUnique; +} +static inline Vec_Int_t * Vec_IntUniqifyHash( Vec_Int_t * vData, int nIntSize ) +{ + Vec_Int_t * vMap, * vUnique; + int i, Ent, nUnique = Vec_IntUniqueCount( vData, nIntSize, &vMap ); + vUnique = Vec_IntAlloc( nUnique * nIntSize ); + Vec_IntForEachEntry( vMap, Ent, i ) + { + if ( Ent < i ) continue; + assert( Ent == i ); + Vec_IntPushArray( vUnique, Vec_IntEntryP(vData, i*nIntSize), nIntSize ); + } + assert( Vec_IntSize(vUnique) == nUnique * nIntSize ); + Vec_IntFree( vMap ); + return vUnique; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompareUnsigned( unsigned * pp1, unsigned * pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSortUnsigned( Vec_Int_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompareUnsigned ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoCountCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int Counter = 0; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++, Counter++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Collects common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoFindCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} +static inline int Vec_IntTwoFindCommonReverse( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 > *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Collects and removes common entries] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemoveCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + int * pBeg2New = vArr2->pArray; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + *pBeg2New++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg2New++ = *pBeg2++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + Vec_IntShrink( vArr2, pBeg2New - vArr2->pArray ); + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Removes entries of the second one from the first one.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemove( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + return Vec_IntSize(vArr1); +} + +/**Function************************************************************* + + Synopsis [Returns the result of merging the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoMerge2Int( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg = vArr->pArray; + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + *pBeg++ = *pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg++ = *pBeg1++; + else + *pBeg++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg++ = *pBeg2++; + vArr->nSize = pBeg - vArr->pArray; + assert( vArr->nSize <= vArr->nCap ); + assert( vArr->nSize >= vArr1->nSize ); + assert( vArr->nSize >= vArr2->nSize ); +} +static inline Vec_Int_t * Vec_IntTwoMerge( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + Vec_Int_t * vArr = Vec_IntAlloc( vArr1->nSize + vArr2->nSize ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); + return vArr; +} +static inline void Vec_IntTwoMerge2( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + Vec_IntGrow( vArr, Vec_IntSize(vArr1) + Vec_IntSize(vArr2) ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); +} + +/**Function************************************************************* + + Synopsis [Returns the result of splitting of the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoSplit( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr, Vec_Int_t * vArr1n, Vec_Int_t * vArr2n ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1++ ), pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + else + Vec_IntPush( vArr2n, *pBeg2++ ); + } + while ( pBeg1 < pEnd1 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + while ( pBeg2 < pEnd2 ) + Vec_IntPush( vArr2n, *pBeg2++ ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSort( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] < pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} +static inline void Vec_IntSelectSortReverse( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] > pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSortCost( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) < Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} +static inline void Vec_IntSelectSortCostReverse( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) > Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} + +static inline void Vec_IntSelectSortCost2( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] < pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} +static inline void Vec_IntSelectSortCost2Reverse( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] > pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPrint( Vec_Int_t * vVec ) +{ + int i, Entry; + printf( "Vector has %d entries: {", Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, Entry, i ) + printf( " %d", Entry ); + printf( " }\n" ); +} +static inline void Vec_IntPrintBinary( Vec_Int_t * vVec ) +{ + int i, Entry; + Vec_IntForEachEntry( vVec, Entry, i ) + printf( "%d", (int)(Entry != 0) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCompareVec( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( p1 == NULL || p2 == NULL ) + return (p1 != NULL) - (p2 != NULL); + if ( Vec_IntSize(p1) != Vec_IntSize(p2) ) + return Vec_IntSize(p1) - Vec_IntSize(p2); + return memcmp( Vec_IntArray(p1), Vec_IntArray(p2), sizeof(int)*Vec_IntSize(p1) ); +} + +/**Function************************************************************* + + Synopsis [Appends the contents of the second vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntAppend( Vec_Int_t * vVec1, Vec_Int_t * vVec2 ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendSkip( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int iVar ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + if ( i != iVar ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendMinus( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int fMinus ) +{ + int Entry, i; + Vec_IntClear( vVec1 ); + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, fMinus ? -Entry : Entry ); +} + +/**Function************************************************************* + + Synopsis [Remapping attributes after objects were duplicated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemapArray( Vec_Int_t * vOld2New, Vec_Int_t * vOld, Vec_Int_t * vNew, int nNew ) +{ + int iOld, iNew; + if ( Vec_IntSize(vOld) == 0 ) + return; + Vec_IntFill( vNew, nNew, 0 ); + Vec_IntForEachEntry( vOld2New, iNew, iOld ) + if ( iNew > 0 && iNew < nNew && iOld < Vec_IntSize(vOld) && Vec_IntEntry(vOld, iOld) != 0 ) + Vec_IntWriteEntry( vNew, iNew, Vec_IntEntry(vOld, iOld) ); +} + +ABC_NAMESPACE_HEADER_END + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcsat/abc/vecWec.h b/lib/abcsat/abc/vecWec.h new file mode 100644 index 0000000..73899ed --- /dev/null +++ b/lib/abcsat/abc/vecWec.h @@ -0,0 +1,724 @@ +/**CFile**************************************************************** + + FileName [vecWec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable vector of resizable vectors.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecWec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__vec__vecWec_h +#define ABC__misc__vec__vecWec_h + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) // warning C4244: '+=' : conversion from 'int ' to 'unsigned short ', possible loss of data +#endif + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +ABC_NAMESPACE_HEADER_START + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Wec_t_ Vec_Wec_t; +struct Vec_Wec_t_ +{ + int nCap; + int nSize; + Vec_Int_t * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through levels +#define Vec_WecForEachLevel( vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelVec( vLevels, vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_IntSize(vLevels)) && (((vVec) = Vec_WecEntry(vGlob, Vec_IntEntry(vLevels, i))), 1); i++ ) +#define Vec_WecForEachLevelStart( vGlob, vVec, i, LevelStart ) \ + for ( i = LevelStart; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStop( vGlob, vVec, i, LevelStop ) \ + for ( i = 0; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelReverse( vGlob, vVec, i ) \ + for ( i = Vec_WecSize(vGlob)-1; (i >= 0) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelReverseStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart-1; (i >= LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelTwo( vGlob1, vGlob2, vVec1, vVec2, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob1)) && (((vVec1) = Vec_WecEntry(vGlob1, i)), 1) && (((vVec2) = Vec_WecEntry(vGlob2, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecAlloc( int nCap ) +{ + Vec_Wec_t * p; + p = ABC_ALLOC( Vec_Wec_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecAllocExact( int nCap ) +{ + Vec_Wec_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Wec_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecStart( int nSize ) +{ + Vec_Wec_t * p; + p = Vec_WecAlloc( nSize ); + p->nSize = nSize; + return p; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecGrow( Vec_Wec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( Vec_Int_t, p->pArray, nCapMin ); + memset( p->pArray + p->nCap, 0, sizeof(Vec_Int_t) * (nCapMin - p->nCap) ); + p->nCap = nCapMin; +} +static inline void Vec_WecInit( Vec_Wec_t * p, int nSize ) +{ + Vec_WecGrow( p, nSize ); + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecEntry( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} +static inline Vec_Int_t * Vec_WecEntryLast( Vec_Wec_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray + p->nSize - 1; +} +static inline int Vec_WecEntryEntry( Vec_Wec_t * p, int i, int k ) +{ + return Vec_IntEntry( Vec_WecEntry(p, i), k ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecArray( Vec_Wec_t * p ) +{ + return p->pArray; +} +static inline int Vec_WecLevelId( Vec_Wec_t * p, Vec_Int_t * vLevel ) +{ + assert( p->pArray <= vLevel && vLevel < p->pArray + p->nSize ); + return vLevel - p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCap( Vec_Wec_t * p ) +{ + return p->nCap; +} +static inline int Vec_WecSize( Vec_Wec_t * p ) +{ + return p->nSize; +} +static inline int Vec_WecLevelSize( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return Vec_IntSize( p->pArray + i ); +} +static inline int Vec_WecSizeSize( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += Vec_IntSize(vVec); + return Counter; +} +static inline int Vec_WecSizeUsed( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} +static inline int Vec_WecSizeUsedLimits( Vec_Wec_t * p, int iStart, int iStop ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevelStartStop( p, vVec, i, iStart, iStop ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecShrink( Vec_Wec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecClear( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i; + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntClear( vVec ); + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPush( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + { + Vec_WecGrow( p, Abc_MaxInt(2*p->nSize, Level + 1) ); + p->nSize = Level + 1; + } + Vec_IntPush( Vec_WecEntry(p, Level), Entry ); +} +static inline Vec_Int_t * Vec_WecPushLevel( Vec_Wec_t * p ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + return Vec_WecEntryLast( p ); +} +static inline Vec_Int_t * Vec_WecInsertLevel( Vec_Wec_t * p, int i ) +{ + Vec_Int_t * pTemp; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + assert( i >= 0 && i < p->nSize ); + for ( pTemp = p->pArray + p->nSize - 2; pTemp >= p->pArray + i; pTemp-- ) + pTemp[1] = pTemp[0]; + Vec_IntZero( p->pArray + i ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_WecMemory( Vec_Wec_t * p ) +{ + int i; + double Mem; + if ( p == NULL ) return 0.0; + Mem = sizeof(Vec_Int_t) * Vec_WecCap(p); + for ( i = 0; i < p->nSize; i++ ) + Mem += sizeof(int) * Vec_IntCap( Vec_WecEntry(p, i) ); + return Mem; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecZero( Vec_Wec_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecErase( Vec_Wec_t * p ) +{ + int i; + for ( i = 0; i < p->nCap; i++ ) + ABC_FREE( p->pArray[i].pArray ); + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecFree( Vec_Wec_t * p ) +{ + Vec_WecErase( p ); + ABC_FREE( p ); +} +static inline void Vec_WecFreeP( Vec_Wec_t ** p ) +{ + if ( *p == NULL ) + return; + Vec_WecFree( *p ); + *p = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPushUnique( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + Vec_WecPush( p, Level, Entry ); + else + Vec_IntPushUnique( Vec_WecEntry(p, Level), Entry ); +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecDup( Vec_Wec_t * p ) +{ + Vec_Wec_t * vNew; + Vec_Int_t * vVec; + int i, k, Entry; + vNew = Vec_WecAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntForEachEntry( vVec, Entry, k ) + Vec_WecPush( vNew, i, Entry ); + return vNew; +} + +/**Function************************************************************* + + Synopsis [Sorting by array size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare1( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare2( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSort( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare1 ); +} + + +/**Function************************************************************* + + Synopsis [Sorting by the first entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare3( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static int Vec_WecSortCompare4( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static inline void Vec_WecSortByFirstInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare4 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare3 ); +} + +/**Function************************************************************* + + Synopsis [Sorting by the last entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare5( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare6( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSortByLastInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare6 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare5 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPrint( Vec_Wec_t * p, int fSkipSingles ) +{ + Vec_Int_t * vVec; + int i, k, Entry; + Vec_WecForEachLevel( p, vVec, i ) + { + if ( fSkipSingles && Vec_IntSize(vVec) == 1 ) + continue; + printf( " %4d : {", i ); + Vec_IntForEachEntry( vVec, Entry, k ) + printf( " %d", Entry ); + printf( " }\n" ); + } +} +static inline void Vec_WecPrintLits( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, k, iLit; + Vec_WecForEachLevel( p, vVec, i ) + { + printf( " %4d : %2d {", i, Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, iLit, k ) + printf( " %c%d", Abc_LitIsCompl(iLit) ? '-' : '+', Abc_Lit2Var(iLit) ); + printf( " }\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Derives the set of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecCreateClasses( Vec_Int_t * vMap ) +{ + Vec_Wec_t * vClasses; + int i, Entry; + vClasses = Vec_WecStart( Vec_IntFindMax(vMap) + 1 ); + Vec_IntForEachEntry( vMap, Entry, i ) + Vec_WecPush( vClasses, Entry, i ); + return vClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCountNonTrivial( Vec_Wec_t * p, int * pnUsed ) +{ + Vec_Int_t * vClass; + int i, nClasses = 0; + *pnUsed = 0; + Vec_WecForEachLevel( p, vClass, i ) + { + if ( Vec_IntSize(vClass) < 2 ) + continue; + nClasses++; + (*pnUsed) += Vec_IntSize(vClass); + } + return nClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecCollectFirsts( Vec_Wec_t * p ) +{ + Vec_Int_t * vFirsts, * vLevel; + int i; + vFirsts = Vec_IntAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vLevel, i ) + if ( Vec_IntSize(vLevel) > 0 ) + Vec_IntPush( vFirsts, Vec_IntEntry(vLevel, 0) ); + return vFirsts; +} + + +/**Function************************************************************* + + Synopsis [Temporary vector marking.] + + Description [The vector should be static when the marking is used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecIntHasMark( Vec_Int_t * vVec ) { return (vVec->nCap >> 30) & 1; } +static inline void Vec_WecIntSetMark( Vec_Int_t * vVec ) { vVec->nCap |= (1<<30); } +static inline void Vec_WecIntXorMark( Vec_Int_t * vVec ) { vVec->nCap ^= (1<<30); } +static inline void Vec_WecMarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( !Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} +static inline void Vec_WecUnmarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} + +/**Function************************************************************* + + Synopsis [Removes 0-size vectors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecRemoveEmpty( Vec_Wec_t * vCubes ) +{ + Vec_Int_t * vCube; + int i, k = 0; + Vec_WecForEachLevel( vCubes, vCube, i ) + if ( Vec_IntSize(vCube) > 0 ) + vCubes->pArray[k++] = *vCube; + else + ABC_FREE( vCube->pArray ); + for ( i = k; i < Vec_WecSize(vCubes); i++ ) + Vec_IntZero( Vec_WecEntry(vCubes, i) ); + Vec_WecShrink( vCubes, k ); +// Vec_WecSortByFirstInt( vCubes, 0 ); +} + + +ABC_NAMESPACE_HEADER_END + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/abcsat/satSolver.cpp b/lib/abcsat/satSolver.cpp new file mode 100644 index 0000000..f9a2898 --- /dev/null +++ b/lib/abcsat/satSolver.cpp @@ -0,0 +1,2498 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#include +#include +#include +#include + +#include +#include + +ABC_NAMESPACE_IMPL_START + +#define SAT_USE_ANALYZE_FINAL + +//================================================================================================= +// Debug: + +//#define VERBOSEDEBUG + + +/**Function************************************************************* + + Synopsis [Merging two lists of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MergeSortCostMerge( int * p1Beg, int * p1End, int * p2Beg, int * p2End, int * pOut ) +{ + int nEntries = (p1End - p1Beg) + (p2End - p2Beg); + int * pOutBeg = pOut; + while ( p1Beg < p1End && p2Beg < p2End ) + { + if ( p1Beg[1] == p2Beg[1] ) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++, *pOut++ = *p2Beg++, *pOut++ = *p2Beg++; + else if ( p1Beg[1] < p2Beg[1] ) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++; + else // if ( p1Beg[1] > p2Beg[1] ) + *pOut++ = *p2Beg++, *pOut++ = *p2Beg++; + } + while ( p1Beg < p1End ) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++; + while ( p2Beg < p2End ) + *pOut++ = *p2Beg++, *pOut++ = *p2Beg++; + assert( pOut - pOutBeg == nEntries ); +} + +/**Function************************************************************* + + Synopsis [Recursive sorting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Abc_MergeSortCost_rec( int * pInBeg, int * pInEnd, int * pOutBeg ) +{ + int nSize = (pInEnd - pInBeg)/2; + assert( nSize > 0 ); + if ( nSize == 1 ) + return; + if ( nSize == 2 ) + { + if ( pInBeg[1] > pInBeg[3] ) + { + pInBeg[1] ^= pInBeg[3]; + pInBeg[3] ^= pInBeg[1]; + pInBeg[1] ^= pInBeg[3]; + pInBeg[0] ^= pInBeg[2]; + pInBeg[2] ^= pInBeg[0]; + pInBeg[0] ^= pInBeg[2]; + } + } + else if ( nSize < 8 ) + { + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pInBeg[2*j+1] < pInBeg[2*best_i+1] ) + best_i = j; + temp = pInBeg[2*i]; + pInBeg[2*i] = pInBeg[2*best_i]; + pInBeg[2*best_i] = temp; + temp = pInBeg[2*i+1]; + pInBeg[2*i+1] = pInBeg[2*best_i+1]; + pInBeg[2*best_i+1] = temp; + } + } + else + { + Abc_MergeSortCost_rec( pInBeg, pInBeg + 2*(nSize/2), pOutBeg ); + Abc_MergeSortCost_rec( pInBeg + 2*(nSize/2), pInEnd, pOutBeg + 2*(nSize/2) ); + Abc_MergeSortCostMerge( pInBeg, pInBeg + 2*(nSize/2), pInBeg + 2*(nSize/2), pInEnd, pOutBeg ); + memcpy( pInBeg, pOutBeg, sizeof(int) * 2 * nSize ); + } +} + +/**Function************************************************************* + + Synopsis [Sorting procedure.] + + Description [Returns permutation for the non-decreasing order of costs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int * Abc_MergeSortCost( int * pCosts, int nSize ) +{ + int i, * pResult, * pInput, * pOutput; + pResult = (int *) calloc( sizeof(int), nSize ); + if ( nSize < 2 ) + return pResult; + pInput = (int *) malloc( sizeof(int) * 2 * nSize ); + pOutput = (int *) malloc( sizeof(int) * 2 * nSize ); + for ( i = 0; i < nSize; i++ ) + pInput[2*i] = i, pInput[2*i+1] = pCosts[i]; + Abc_MergeSortCost_rec( pInput, pInput + 2*nSize, pOutput ); + for ( i = 0; i < nSize; i++ ) + pResult[i] = pInput[2*i]; + free( pOutput ); + free( pInput ); + return pResult; +} + + + +// For derivation output (verbosity level 2) +#define L_IND "%-*d" +#define L_ind sat_solver_dl(s)*2+2,sat_solver_dl(s) +#define L_LIT "%sx%d" +#define L_lit(p) lit_sign(p)?"~":"", (lit_var(p)) + +// Just like 'assert()' but expression will be evaluated in the release version as well. +static inline void check(int expr) { assert(expr); } + +//static void printlits(lit* begin, lit* end) +//{ +// int i; +// for (i = 0; i < end - begin; i++) +// printf(L_LIT" ",L_lit(begin[i])); +//} + +//================================================================================================= +// Random numbers: + + +// Returns a random float 0 <= x < 1. Seed must never be 0. +static inline double drand(double* seed) { + int q; + *seed *= 1389796; + q = (int)(*seed / 2147483647); + *seed -= (double)q * 2147483647; + return *seed / 2147483647; } + + +// Returns a random integer 0 <= x < size. Seed must never be 0. +static inline int irand(double* seed, int size) { + return (int)(drand(seed) * size); } + + +//================================================================================================= +// Variable datatype + minor functions: + +static const int var0 = 1; +static const int var1 = 0; +static const int varX = 3; + +struct varinfo_t +{ + unsigned val : 2; // variable value + unsigned pol : 1; // last polarity + unsigned tag : 1; // conflict analysis tag + unsigned lev : 28; // variable level +}; + +static inline int var_level (sat_solver* s, int v) { return s->levels[v]; } +static inline int var_value (sat_solver* s, int v) { return s->assigns[v]; } +static inline int var_polar (sat_solver* s, int v) { return s->polarity[v]; } + +static inline void var_set_level (sat_solver* s, int v, int lev) { s->levels[v] = lev; } +static inline void var_set_value (sat_solver* s, int v, int val) { s->assigns[v] = val; } +static inline void var_set_polar (sat_solver* s, int v, int pol) { s->polarity[v] = pol; } + +// variable tags +static inline int var_tag (sat_solver* s, int v) { return s->tags[v]; } +static inline void var_set_tag (sat_solver* s, int v, int tag) { + assert( tag > 0 && tag < 16 ); + if ( s->tags[v] == 0 ) + veci_push( &s->tagged, v ); + s->tags[v] = tag; +} +static inline void var_add_tag (sat_solver* s, int v, int tag) { + assert( tag > 0 && tag < 16 ); + if ( s->tags[v] == 0 ) + veci_push( &s->tagged, v ); + s->tags[v] |= tag; +} +static inline void solver2_clear_tags(sat_solver* s, int start) { + int i, * tagged = veci_begin(&s->tagged); + for (i = start; i < veci_size(&s->tagged); i++) + s->tags[tagged[i]] = 0; + veci_resize(&s->tagged,start); +} + +int sat_solver_get_var_value(sat_solver* s, int v) +{ + if ( var_value(s, v) == var0 ) + return l_False; + if ( var_value(s, v) == var1 ) + return l_True; + if ( var_value(s, v) == varX ) + return l_Undef; + assert( 0 ); + return 0; +} + +//================================================================================================= +// Simple helpers: + +static inline int sat_solver_dl(sat_solver* s) { return veci_size(&s->trail_lim); } +static inline veci* sat_solver_read_wlist(sat_solver* s, lit l) { return &s->wlists[l]; } + +//================================================================================================= +// Variable order functions: + +static inline void order_update(sat_solver* s, int v) // updateorder +{ + int* orderpos = s->orderpos; + int* heap = veci_begin(&s->order); + int i = orderpos[v]; + int x = heap[i]; + int parent = (i - 1) / 2; + + assert(s->orderpos[v] != -1); + + while (i != 0 && s->activity[x] > s->activity[heap[parent]]){ + heap[i] = heap[parent]; + orderpos[heap[i]] = i; + i = parent; + parent = (i - 1) / 2; + } + + heap[i] = x; + orderpos[x] = i; +} + +static inline void order_assigned(sat_solver* s, int v) +{ +} + +static inline void order_unassigned(sat_solver* s, int v) // undoorder +{ + int* orderpos = s->orderpos; + if (orderpos[v] == -1){ + orderpos[v] = veci_size(&s->order); + veci_push(&s->order,v); + order_update(s,v); +//printf( "+%d ", v ); + } +} + +static inline int order_select(sat_solver* s, float random_var_freq) // selectvar +{ + int* heap = veci_begin(&s->order); + int* orderpos = s->orderpos; + // Random decision: + if (drand(&s->random_seed) < random_var_freq){ + int next = irand(&s->random_seed,s->size); + assert(next >= 0 && next < s->size); + if (var_value(s, next) == varX) + return next; + } + // Activity based decision: + while (veci_size(&s->order) > 0){ + int next = heap[0]; + int size = veci_size(&s->order)-1; + int x = heap[size]; + veci_resize(&s->order,size); + orderpos[next] = -1; + if (size > 0){ + int i = 0; + int child = 1; + while (child < size){ + + if (child+1 < size && s->activity[heap[child]] < s->activity[heap[child+1]]) + child++; + assert(child < size); + if (s->activity[x] >= s->activity[heap[child]]) + break; + + heap[i] = heap[child]; + orderpos[heap[i]] = i; + i = child; + child = 2 * child + 1; + } + heap[i] = x; + orderpos[heap[i]] = i; + } + if (var_value(s, next) == varX) + return next; + } + return var_Undef; +} + +void sat_solver_set_var_activity(sat_solver* s, int * pVars, int nVars) +{ + int i; + assert( s->VarActType == 1 ); + for (i = 0; i < s->size; i++) + s->activity[i] = 0; + s->var_inc = Abc_Dbl2Word(1); + for ( i = 0; i < nVars; i++ ) + { + int iVar = pVars ? pVars[i] : i; + s->activity[iVar] = Abc_Dbl2Word(nVars-i); + order_update( s, iVar ); + } +} + +//================================================================================================= +// variable activities + +void solver_init_activities(sat_solver* s) +{ + // variable activities + if ( s->VarActType == 0 ) + { + s->var_inc = (1 << 5); + s->var_decay = -1; + } + else if ( s->VarActType == 1 ) + { + s->var_inc = Abc_Dbl2Word(1.0); + s->var_decay = Abc_Dbl2Word(1.0 / 0.95); + } + else if ( s->VarActType == 2 ) + { + s->var_inc = Xdbl_FromDouble(1.0); + s->var_decay = Xdbl_FromDouble(1.0 / 0.950); + } + else assert(0); + + // clause activities + if ( s->ClaActType == 0 ) + { + s->cla_inc = (1 << 11); + s->cla_decay = -1; + } + else + { + s->cla_inc = 1; + s->cla_decay = (float)(1 / 0.999); + } +} + +static inline void act_var_rescale(sat_solver* s) +{ + if ( s->VarActType == 0 ) + { + word* activity = s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] >>= 19; + s->var_inc >>= 19; + s->var_inc = Abc_MaxInt( (unsigned)s->var_inc, (1<<4) ); + } + else if ( s->VarActType == 1 ) + { + double* activity = (double*)s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] *= 1e-100; + s->var_inc = Abc_Dbl2Word( Abc_Word2Dbl(s->var_inc) * 1e-100 ); + //printf( "Rescaling var activity...\n" ); + } + else if ( s->VarActType == 2 ) + { + xdbl * activity = s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] = Xdbl_Div( activity[i], 200 ); // activity[i] / 2^200 + s->var_inc = Xdbl_Div( s->var_inc, 200 ); + } + else assert(0); +} +static inline void act_var_bump(sat_solver* s, int v) +{ + if ( s->VarActType == 0 ) + { + s->activity[v] += s->var_inc; + if ((unsigned)s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 1 ) + { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc); + s->activity[v] = Abc_Dbl2Word(act); + if (act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 2 ) + { + s->activity[v] = Xdbl_Add( s->activity[v], s->var_inc ); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else assert(0); +} +static inline void act_var_bump_global(sat_solver* s, int v) +{ + if ( !s->pGlobalVars || !s->pGlobalVars[v] ) + return; + if ( s->VarActType == 0 ) + { + s->activity[v] += (int)((unsigned)s->var_inc * 3); + if (s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 1 ) + { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc) * 3.0; + s->activity[v] = Abc_Dbl2Word(act); + if ( act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 2 ) + { + s->activity[v] = Xdbl_Add( s->activity[v], Xdbl_Mul(s->var_inc, Xdbl_FromDouble(3.0)) ); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else assert( 0 ); +} +static inline void act_var_bump_factor(sat_solver* s, int v) +{ + if ( !s->factors ) + return; + if ( s->VarActType == 0 ) + { + s->activity[v] += (int)((unsigned)s->var_inc * (float)s->factors[v]); + if (s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 1 ) + { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc) * s->factors[v]; + s->activity[v] = Abc_Dbl2Word(act); + if ( act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else if ( s->VarActType == 2 ) + { + s->activity[v] = Xdbl_Add( s->activity[v], Xdbl_Mul(s->var_inc, Xdbl_FromDouble(s->factors[v])) ); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s,v); + } + else assert( 0 ); +} + +static inline void act_var_decay(sat_solver* s) +{ + if ( s->VarActType == 0 ) + s->var_inc += (s->var_inc >> 4); + else if ( s->VarActType == 1 ) + s->var_inc = Abc_Dbl2Word( Abc_Word2Dbl(s->var_inc) * Abc_Word2Dbl(s->var_decay) ); + else if ( s->VarActType == 2 ) + s->var_inc = Xdbl_Mul(s->var_inc, s->var_decay); + else assert(0); +} + +// clause activities +static inline void act_clause_rescale(sat_solver* s) +{ + if ( s->ClaActType == 0 ) + { + unsigned* activity = (unsigned *)veci_begin(&s->act_clas); + int i; + for (i = 0; i < veci_size(&s->act_clas); i++) + activity[i] >>= 14; + s->cla_inc >>= 14; + s->cla_inc = Abc_MaxInt( s->cla_inc, (1<<10) ); + } + else + { + float* activity = (float *)veci_begin(&s->act_clas); + int i; + for (i = 0; i < veci_size(&s->act_clas); i++) + activity[i] *= (float)1e-20; + s->cla_inc *= (float)1e-20; + } +} +static inline void act_clause_bump(sat_solver* s, clause *c) +{ + if ( s->ClaActType == 0 ) + { + unsigned* act = (unsigned *)veci_begin(&s->act_clas) + c->lits[c->size]; + *act += s->cla_inc; + if ( *act & 0x80000000 ) + act_clause_rescale(s); + } + else + { + float* act = (float *)veci_begin(&s->act_clas) + c->lits[c->size]; + *act += s->cla_inc; + if (*act > 1e20) + act_clause_rescale(s); + } +} +static inline void act_clause_decay(sat_solver* s) +{ + if ( s->ClaActType == 0 ) + s->cla_inc += (s->cla_inc >> 10); + else + s->cla_inc *= s->cla_decay; +} + + +//================================================================================================= +// Sorting functions (sigh): + +static inline void selectionsort(void** array, int size, int(*comp)(const void *, const void *)) +{ + int i, j, best_i; + void* tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (comp(array[j], array[best_i]) < 0) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} + +//================================================================================================= +// Clause functions: + +static inline int sat_clause_compute_lbd( sat_solver* s, clause* c ) +{ + int i, lev, minl = 0, lbd = 0; + for (i = 0; i < (int)c->size; i++) + { + lev = var_level(s, lit_var(c->lits[i])); + if ( !(minl & (1 << (lev & 31))) ) + { + minl |= 1 << (lev & 31); + lbd++; +// printf( "%d ", lev ); + } + } +// printf( " -> %d\n", lbd ); + return lbd; +} + +/* pre: size > 1 && no variable occurs twice + */ +int sat_solver_clause_new(sat_solver* s, lit* begin, lit* end, int learnt) +{ + int fUseBinaryClauses = 1; + int size; + clause* c; + int h; + + assert(end - begin > 1); + assert(learnt >= 0 && learnt < 2); + size = end - begin; + + // do not allocate memory for the two-literal problem clause + if ( fUseBinaryClauses && size == 2 && !learnt ) + { + veci_push(sat_solver_read_wlist(s,lit_neg(begin[0])),(clause_from_lit(begin[1]))); + veci_push(sat_solver_read_wlist(s,lit_neg(begin[1])),(clause_from_lit(begin[0]))); + s->stats.clauses++; + s->stats.clauses_literals += size; + return 0; + } + + // create new clause +// h = Vec_SetAppend( &s->Mem, NULL, size + learnt + 1 + 1 ) << 1; + h = Sat_MemAppend( &s->Mem, begin, size, learnt, 0 ); + assert( !(h & 1) ); + if ( s->hLearnts == -1 && learnt ) + s->hLearnts = h; + if (learnt) + { + c = clause_read( s, h ); + c->lbd = sat_clause_compute_lbd( s, c ); + assert( clause_id(c) == veci_size(&s->act_clas) ); +// veci_push(&s->learned, h); +// act_clause_bump(s,clause_read(s, h)); + if ( s->ClaActType == 0 ) + veci_push(&s->act_clas, (1<<10)); + else + veci_push(&s->act_clas, s->cla_inc); + s->stats.learnts++; + s->stats.learnts_literals += size; + } + else + { + s->stats.clauses++; + s->stats.clauses_literals += size; + } + + assert(begin[0] >= 0); + assert(begin[0] < s->size*2); + assert(begin[1] >= 0); + assert(begin[1] < s->size*2); + + assert(lit_neg(begin[0]) < s->size*2); + assert(lit_neg(begin[1]) < s->size*2); + + //veci_push(sat_solver_read_wlist(s,lit_neg(begin[0])),c); + //veci_push(sat_solver_read_wlist(s,lit_neg(begin[1])),c); + veci_push(sat_solver_read_wlist(s,lit_neg(begin[0])),(size > 2 ? h : clause_from_lit(begin[1]))); + veci_push(sat_solver_read_wlist(s,lit_neg(begin[1])),(size > 2 ? h : clause_from_lit(begin[0]))); + + return h; +} + + +//================================================================================================= +// Minor (solver) functions: + +static inline int sat_solver_enqueue(sat_solver* s, lit l, int from) +{ + int v = lit_var(l); + if ( s->pFreqs[v] == 0 ) +// { + s->pFreqs[v] = 1; +// s->nVarUsed++; +// } + +#ifdef VERBOSEDEBUG + printf(L_IND"enqueue("L_LIT")\n", L_ind, L_lit(l)); +#endif + if (var_value(s, v) != varX) + return var_value(s, v) == lit_sign(l); + else{ +/* + if ( s->pCnfFunc ) + { + if ( lit_sign(l) ) + { + if ( (s->loads[v] & 1) == 0 ) + { + s->loads[v] ^= 1; + s->pCnfFunc( s->pCnfMan, l ); + } + } + else + { + if ( (s->loads[v] & 2) == 0 ) + { + s->loads[v] ^= 2; + s->pCnfFunc( s->pCnfMan, l ); + } + } + } +*/ + // New fact -- store it. +#ifdef VERBOSEDEBUG + printf(L_IND"bind("L_LIT")\n", L_ind, L_lit(l)); +#endif + var_set_value(s, v, lit_sign(l)); + var_set_level(s, v, sat_solver_dl(s)); + s->reasons[v] = from; + s->trail[s->qtail++] = l; + order_assigned(s, v); + return true; + } +} + + +static inline int sat_solver_decision(sat_solver* s, lit l){ + assert(s->qtail == s->qhead); + assert(var_value(s, lit_var(l)) == varX); +#ifdef VERBOSEDEBUG + printf(L_IND"assume("L_LIT") ", L_ind, L_lit(l)); + printf( "act = %.20f\n", s->activity[lit_var(l)] ); +#endif + veci_push(&s->trail_lim,s->qtail); + return sat_solver_enqueue(s,l,0); +} + + +static void sat_solver_canceluntil(sat_solver* s, int level) { + int bound; + int lastLev; + int c; + + if (sat_solver_dl(s) <= level) + return; + + assert( veci_size(&s->trail_lim) > 0 ); + bound = (veci_begin(&s->trail_lim))[level]; + lastLev = (veci_begin(&s->trail_lim))[veci_size(&s->trail_lim)-1]; + + //////////////////////////////////////// + // added to cancel all assignments +// if ( level == -1 ) +// bound = 0; + //////////////////////////////////////// + + for (c = s->qtail-1; c >= bound; c--) { + int x = lit_var(s->trail[c]); + var_set_value(s, x, varX); + s->reasons[x] = 0; + if ( c < lastLev ) + var_set_polar( s, x, !lit_sign(s->trail[c]) ); + } + //printf( "\n" ); + + for (c = s->qhead-1; c >= bound; c--) + order_unassigned(s,lit_var(s->trail[c])); + + s->qhead = s->qtail = bound; + veci_resize(&s->trail_lim,level); +} + +static void sat_solver_canceluntil_rollback(sat_solver* s, int NewBound) { + int c, x; + + assert( sat_solver_dl(s) == 0 ); + assert( s->qtail == s->qhead ); + assert( s->qtail >= NewBound ); + + for (c = s->qtail-1; c >= NewBound; c--) + { + x = lit_var(s->trail[c]); + var_set_value(s, x, varX); + s->reasons[x] = 0; + } + + for (c = s->qhead-1; c >= NewBound; c--) + order_unassigned(s,lit_var(s->trail[c])); + + s->qhead = s->qtail = NewBound; +} + +static void sat_solver_record(sat_solver* s, veci* cls) +{ + lit* begin = veci_begin(cls); + lit* end = begin + veci_size(cls); + int h = (veci_size(cls) > 1) ? sat_solver_clause_new(s,begin,end,1) : 0; + sat_solver_enqueue(s,*begin,h); + assert(veci_size(cls) > 0); + if ( h == 0 ) + veci_push( &s->unit_lits, *begin ); + + /////////////////////////////////// + // add clause to internal storage + if ( s->pStore ) + { + int RetValue = Sto_ManAddClause( (Sto_Man_t *)s->pStore, begin, end ); + assert( RetValue ); + (void) RetValue; + } + /////////////////////////////////// +/* + if (h != 0) { + act_clause_bump(s,clause_read(s, h)); + s->stats.learnts++; + s->stats.learnts_literals += veci_size(cls); + } +*/ +} + +int sat_solver_count_assigned(sat_solver* s) +{ + // count top-level assignments + int i, Count = 0; + assert(sat_solver_dl(s) == 0); + for ( i = 0; i < s->size; i++ ) + if (var_value(s, i) != varX) + Count++; + return Count; +} + +static double sat_solver_progress(sat_solver* s) +{ + int i; + double progress = 0; + double F = 1.0 / s->size; + for (i = 0; i < s->size; i++) + if (var_value(s, i) != varX) + progress += pow(F, var_level(s, i)); + return progress / s->size; +} + +//================================================================================================= +// Major methods: + +static int sat_solver_lit_removable(sat_solver* s, int x, int minl) +{ + int top = veci_size(&s->tagged); + + assert(s->reasons[x] != 0); + veci_resize(&s->stack,0); + veci_push(&s->stack,x); + + while (veci_size(&s->stack)){ + int v = veci_pop(&s->stack); + assert(s->reasons[v] != 0); + if (clause_is_lit(s->reasons[v])){ + v = lit_var(clause_read_lit(s->reasons[v])); + if (!var_tag(s,v) && var_level(s, v)){ + if (s->reasons[v] != 0 && ((1 << (var_level(s, v) & 31)) & minl)){ + veci_push(&s->stack,v); + var_set_tag(s, v, 1); + }else{ + solver2_clear_tags(s, top); + return 0; + } + } + }else{ + clause* c = clause_read(s, s->reasons[v]); + lit* lits = clause_begin(c); + int i; + for (i = 1; i < clause_size(c); i++){ + int v = lit_var(lits[i]); + if (!var_tag(s,v) && var_level(s, v)){ + if (s->reasons[v] != 0 && ((1 << (var_level(s, v) & 31)) & minl)){ + veci_push(&s->stack,lit_var(lits[i])); + var_set_tag(s, v, 1); + }else{ + solver2_clear_tags(s, top); + return 0; + } + } + } + } + } + return 1; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +/* +void Solver::analyzeFinal(Clause* confl, bool skip_first) +{ + // -- NOTE! This code is relatively untested. Please report bugs! + conflict.clear(); + if (root_level == 0) return; + + vec& seen = analyze_seen; + for (int i = skip_first ? 1 : 0; i < confl->size(); i++){ + Var x = var((*confl)[i]); + if (level[x] > 0) + seen[x] = 1; + } + + int start = (root_level >= trail_lim.size()) ? trail.size()-1 : trail_lim[root_level]; + for (int i = start; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + GClause r = reason[x]; + if (r == GClause_NULL){ + assert(level[x] > 0); + conflict.push(~trail[i]); + }else{ + if (r.isLit()){ + Lit p = r.lit(); + if (level[var(p)] > 0) + seen[var(p)] = 1; + }else{ + Clause& c = *r.clause(); + for (int j = 1; j < c.size(); j++) + if (level[var(c[j])] > 0) + seen[var(c[j])] = 1; + } + } + seen[x] = 0; + } + } +} +*/ + +#ifdef SAT_USE_ANALYZE_FINAL + +static void sat_solver_analyze_final(sat_solver* s, int hConf, int skip_first) +{ + clause* conf = clause_read(s, hConf); + int i, j, start; + veci_resize(&s->conf_final,0); + if ( s->root_level == 0 ) + return; + assert( veci_size(&s->tagged) == 0 ); +// assert( s->tags[lit_var(p)] == l_Undef ); +// s->tags[lit_var(p)] = l_True; + for (i = skip_first ? 1 : 0; i < clause_size(conf); i++) + { + int x = lit_var(clause_begin(conf)[i]); + if (var_level(s, x) > 0) + var_set_tag(s, x, 1); + } + + start = (s->root_level >= veci_size(&s->trail_lim))? s->qtail-1 : (veci_begin(&s->trail_lim))[s->root_level]; + for (i = start; i >= (veci_begin(&s->trail_lim))[0]; i--){ + int x = lit_var(s->trail[i]); + if (var_tag(s,x)){ + if (s->reasons[x] == 0){ + assert(var_level(s, x) > 0); + veci_push(&s->conf_final,lit_neg(s->trail[i])); + }else{ + if (clause_is_lit(s->reasons[x])){ + lit q = clause_read_lit(s->reasons[x]); + assert(lit_var(q) >= 0 && lit_var(q) < s->size); + if (var_level(s, lit_var(q)) > 0) + var_set_tag(s, lit_var(q), 1); + } + else{ + clause* c = clause_read(s, s->reasons[x]); + int* lits = clause_begin(c); + for (j = 1; j < clause_size(c); j++) + if (var_level(s, lit_var(lits[j])) > 0) + var_set_tag(s, lit_var(lits[j]), 1); + } + } + } + } + solver2_clear_tags(s,0); +} + +#endif + +static void sat_solver_analyze(sat_solver* s, int h, veci* learnt) +{ + lit* trail = s->trail; + int cnt = 0; + lit p = lit_Undef; + int ind = s->qtail-1; + lit* lits; + int i, j, minl; + veci_push(learnt,lit_Undef); + do{ + assert(h != 0); + if (clause_is_lit(h)){ + int x = lit_var(clause_read_lit(h)); + if (var_tag(s, x) == 0 && var_level(s, x) > 0){ + var_set_tag(s, x, 1); + act_var_bump(s,x); + if (var_level(s, x) == sat_solver_dl(s)) + cnt++; + else + veci_push(learnt,clause_read_lit(h)); + } + }else{ + clause* c = clause_read(s, h); + + if (clause_learnt(c)) + act_clause_bump(s,c); + lits = clause_begin(c); + //printlits(lits,lits+clause_size(c)); printf("\n"); + for (j = (p == lit_Undef ? 0 : 1); j < clause_size(c); j++){ + int x = lit_var(lits[j]); + if (var_tag(s, x) == 0 && var_level(s, x) > 0){ + var_set_tag(s, x, 1); + act_var_bump(s,x); + // bump variables propaged by the LBD=2 clause +// if ( s->reasons[x] && clause_read(s, s->reasons[x])->lbd <= 2 ) +// act_var_bump(s,x); + if (var_level(s,x) == sat_solver_dl(s)) + cnt++; + else + veci_push(learnt,lits[j]); + } + } + } + + while ( !var_tag(s, lit_var(trail[ind--])) ); + + p = trail[ind+1]; + h = s->reasons[lit_var(p)]; + cnt--; + + }while (cnt > 0); + + *veci_begin(learnt) = lit_neg(p); + + lits = veci_begin(learnt); + minl = 0; + for (i = 1; i < veci_size(learnt); i++){ + int lev = var_level(s, lit_var(lits[i])); + minl |= 1 << (lev & 31); + } + + // simplify (full) + for (i = j = 1; i < veci_size(learnt); i++){ + if (s->reasons[lit_var(lits[i])] == 0 || !sat_solver_lit_removable(s,lit_var(lits[i]),minl)) + lits[j++] = lits[i]; + } + + // update size of learnt + statistics + veci_resize(learnt,j); + s->stats.tot_literals += j; + + + // clear tags + solver2_clear_tags(s,0); + +#ifdef DEBUG + for (i = 0; i < s->size; i++) + assert(!var_tag(s, i)); +#endif + +#ifdef VERBOSEDEBUG + printf(L_IND"Learnt {", L_ind); + for (i = 0; i < veci_size(learnt); i++) printf(" "L_LIT, L_lit(lits[i])); +#endif + if (veci_size(learnt) > 1){ + int max_i = 1; + int max = var_level(s, lit_var(lits[1])); + lit tmp; + + for (i = 2; i < veci_size(learnt); i++) + if (var_level(s, lit_var(lits[i])) > max){ + max = var_level(s, lit_var(lits[i])); + max_i = i; + } + + tmp = lits[1]; + lits[1] = lits[max_i]; + lits[max_i] = tmp; + } +#ifdef VERBOSEDEBUG + { + int lev = veci_size(learnt) > 1 ? var_level(s, lit_var(lits[1])) : 0; + printf(" } at level %d\n", lev); + } +#endif +} + +//#define TEST_CNF_LOAD + +int sat_solver_propagate(sat_solver* s) +{ + int hConfl = 0; + lit* lits; + lit false_lit; + + //printf("sat_solver_propagate\n"); + while (hConfl == 0 && s->qtail - s->qhead > 0){ + lit p = s->trail[s->qhead++]; + +#ifdef TEST_CNF_LOAD + int v = lit_var(p); + if ( s->pCnfFunc ) + { + if ( lit_sign(p) ) + { + if ( (s->loads[v] & 1) == 0 ) + { + s->loads[v] ^= 1; + s->pCnfFunc( s->pCnfMan, p ); + } + } + else + { + if ( (s->loads[v] & 2) == 0 ) + { + s->loads[v] ^= 2; + s->pCnfFunc( s->pCnfMan, p ); + } + } + } + { +#endif + + veci* ws = sat_solver_read_wlist(s,p); + int* begin = veci_begin(ws); + int* end = begin + veci_size(ws); + int*i, *j; + + s->stats.propagations++; +// s->simpdb_props--; + + //printf("checking lit %d: "L_LIT"\n", veci_size(ws), L_lit(p)); + for (i = j = begin; i < end; ){ + if (clause_is_lit(*i)){ + + int Lit = clause_read_lit(*i); + if (var_value(s, lit_var(Lit)) == lit_sign(Lit)){ + *j++ = *i++; + continue; + } + + *j++ = *i; + if (!sat_solver_enqueue(s,clause_read_lit(*i),clause_from_lit(p))){ + hConfl = s->hBinary; + (clause_begin(s->binary))[1] = lit_neg(p); + (clause_begin(s->binary))[0] = clause_read_lit(*i++); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + }else{ + + clause* c = clause_read(s,*i); + lits = clause_begin(c); + + // Make sure the false literal is data[1]: + false_lit = lit_neg(p); + if (lits[0] == false_lit){ + lits[0] = lits[1]; + lits[1] = false_lit; + } + assert(lits[1] == false_lit); + + // If 0th watch is true, then clause is already satisfied. + if (var_value(s, lit_var(lits[0])) == lit_sign(lits[0])) + *j++ = *i; + else{ + // Look for new watch: + lit* stop = lits + clause_size(c); + lit* k; + for (k = lits + 2; k < stop; k++){ + if (var_value(s, lit_var(*k)) != !lit_sign(*k)){ + lits[1] = *k; + *k = false_lit; + veci_push(sat_solver_read_wlist(s,lit_neg(lits[1])),*i); + goto next; } + } + + *j++ = *i; + // Clause is unit under assignment: + if ( c->lrn ) + c->lbd = sat_clause_compute_lbd(s, c); + if (!sat_solver_enqueue(s,lits[0], *i)){ + hConfl = *i++; + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + } + } + next: + i++; + } + + s->stats.inspects += j - veci_begin(ws); + veci_resize(ws,j - veci_begin(ws)); +#ifdef TEST_CNF_LOAD + } +#endif + } + + return hConfl; +} + +//================================================================================================= +// External solver functions: + +sat_solver* sat_solver_new(void) +{ + sat_solver* s = (sat_solver*)ABC_CALLOC( char, sizeof(sat_solver)); + +// Vec_SetAlloc_(&s->Mem, 15); + Sat_MemAlloc_(&s->Mem, 17); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend( &s->Mem, NULL, 2, 0, 0 ); + s->binary = clause_read( s, s->hBinary ); + + s->nLearntStart = LEARNT_MAX_START_DEFAULT; // starting learned clause limit + s->nLearntDelta = LEARNT_MAX_INCRE_DEFAULT; // delta of learned clause limit + s->nLearntRatio = LEARNT_MAX_RATIO_DEFAULT; // ratio of learned clause limit + s->nLearntMax = s->nLearntStart; + + // initialize vectors + veci_new(&s->order); + veci_new(&s->trail_lim); + veci_new(&s->tagged); +// veci_new(&s->learned); + veci_new(&s->act_clas); + veci_new(&s->stack); +// veci_new(&s->model); + veci_new(&s->unit_lits); + veci_new(&s->temp_clause); + veci_new(&s->conf_final); + + // initialize arrays + s->wlists = 0; + s->activity = 0; + s->orderpos = 0; + s->reasons = 0; + s->trail = 0; + + // initialize other vars + s->size = 0; + s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_new(&s->act_vars); + + s->root_level = 0; +// s->simpdb_assigns = 0; +// s->simpdb_props = 0; + s->progress_estimate = 0; +// s->binary = (clause*)ABC_ALLOC( char, sizeof(clause) + sizeof(lit)*2); +// s->binary->size_learnt = (2 << 1); + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + return s; +} + +sat_solver* zsat_solver_new_seed(double seed) +{ + sat_solver* s = (sat_solver*)ABC_CALLOC( char, sizeof(sat_solver)); + +// Vec_SetAlloc_(&s->Mem, 15); + Sat_MemAlloc_(&s->Mem, 15); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend( &s->Mem, NULL, 2, 0, 0 ); + s->binary = clause_read( s, s->hBinary ); + + s->nLearntStart = LEARNT_MAX_START_DEFAULT; // starting learned clause limit + s->nLearntDelta = LEARNT_MAX_INCRE_DEFAULT; // delta of learned clause limit + s->nLearntRatio = LEARNT_MAX_RATIO_DEFAULT; // ratio of learned clause limit + s->nLearntMax = s->nLearntStart; + + // initialize vectors + veci_new(&s->order); + veci_new(&s->trail_lim); + veci_new(&s->tagged); +// veci_new(&s->learned); + veci_new(&s->act_clas); + veci_new(&s->stack); +// veci_new(&s->model); + veci_new(&s->unit_lits); + veci_new(&s->temp_clause); + veci_new(&s->conf_final); + + // initialize arrays + s->wlists = 0; + s->activity = 0; + s->orderpos = 0; + s->reasons = 0; + s->trail = 0; + + // initialize other vars + s->size = 0; + s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_new(&s->act_vars); + + s->root_level = 0; +// s->simpdb_assigns = 0; +// s->simpdb_props = 0; + s->random_seed = seed; + s->progress_estimate = 0; +// s->binary = (clause*)ABC_ALLOC( char, sizeof(clause) + sizeof(lit)*2); +// s->binary->size_learnt = (2 << 1); + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + return s; +} + +int sat_solver_addvar(sat_solver* s) +{ + sat_solver_setnvars(s, s->size+1); + return s->size-1; +} +void sat_solver_setnvars(sat_solver* s,int n) +{ + int var; + + if (s->cap < n){ + int old_cap = s->cap; + while (s->cap < n) s->cap = s->cap*2+1; + if ( s->cap < 50000 ) + s->cap = 50000; + + s->wlists = ABC_REALLOC(veci, s->wlists, s->cap*2); +// s->vi = ABC_REALLOC(varinfo,s->vi, s->cap); + s->levels = ABC_REALLOC(int, s->levels, s->cap); + s->assigns = ABC_REALLOC(char, s->assigns, s->cap); + s->polarity = ABC_REALLOC(char, s->polarity, s->cap); + s->tags = ABC_REALLOC(char, s->tags, s->cap); + s->loads = ABC_REALLOC(char, s->loads, s->cap); + s->activity = ABC_REALLOC(word, s->activity, s->cap); + s->activity2 = ABC_REALLOC(word, s->activity2,s->cap); + s->pFreqs = ABC_REALLOC(char, s->pFreqs, s->cap); + + if ( s->factors ) + s->factors = ABC_REALLOC(double, s->factors, s->cap); + s->orderpos = ABC_REALLOC(int, s->orderpos, s->cap); + s->reasons = ABC_REALLOC(int, s->reasons, s->cap); + s->trail = ABC_REALLOC(lit, s->trail, s->cap); + s->model = ABC_REALLOC(int, s->model, s->cap); + memset( s->wlists + 2*old_cap, 0, 2*(s->cap-old_cap)*sizeof(veci) ); + } + + for (var = s->size; var < n; var++){ + assert(!s->wlists[2*var].size); + assert(!s->wlists[2*var+1].size); + if ( s->wlists[2*var].ptr == NULL ) + veci_new(&s->wlists[2*var]); + if ( s->wlists[2*var+1].ptr == NULL ) + veci_new(&s->wlists[2*var+1]); + + if ( s->VarActType == 0 ) + s->activity[var] = (1<<10); + else if ( s->VarActType == 1 ) + s->activity[var] = 0; + else if ( s->VarActType == 2 ) + s->activity[var] = 0; + else assert(0); + + s->pFreqs[var] = 0; + if ( s->factors ) + s->factors [var] = 0; +// *((int*)s->vi + var) = 0; s->vi[var].val = varX; + s->levels [var] = 0; + s->assigns [var] = varX; + s->polarity[var] = 0; + s->tags [var] = 0; + s->loads [var] = 0; + s->orderpos[var] = veci_size(&s->order); + s->reasons [var] = 0; + s->model [var] = 0; + + /* does not hold because variables enqueued at top level will not be reinserted in the heap + assert(veci_size(&s->order) == var); + */ + veci_push(&s->order,var); + order_update(s, var); + } + + s->size = n > s->size ? n : s->size; +} + +void sat_solver_delete(sat_solver* s) +{ +// Vec_SetFree_( &s->Mem ); + Sat_MemFree_( &s->Mem ); + + // delete vectors + veci_delete(&s->order); + veci_delete(&s->trail_lim); + veci_delete(&s->tagged); +// veci_delete(&s->learned); + veci_delete(&s->act_clas); + veci_delete(&s->stack); +// veci_delete(&s->model); + veci_delete(&s->act_vars); + veci_delete(&s->unit_lits); + veci_delete(&s->pivot_vars); + veci_delete(&s->temp_clause); + veci_delete(&s->conf_final); + + veci_delete(&s->user_vars); + veci_delete(&s->user_values); + + // delete arrays + if (s->reasons != 0){ + int i; + for (i = 0; i < s->cap*2; i++) + veci_delete(&s->wlists[i]); + ABC_FREE(s->wlists ); +// ABC_FREE(s->vi ); + ABC_FREE(s->levels ); + ABC_FREE(s->assigns ); + ABC_FREE(s->polarity ); + ABC_FREE(s->tags ); + ABC_FREE(s->loads ); + ABC_FREE(s->activity ); + ABC_FREE(s->activity2); + ABC_FREE(s->pFreqs ); + ABC_FREE(s->factors ); + ABC_FREE(s->orderpos ); + ABC_FREE(s->reasons ); + ABC_FREE(s->trail ); + ABC_FREE(s->model ); + } + + sat_solver_store_free(s); + ABC_FREE(s); +} + +void sat_solver_restart( sat_solver* s ) +{ + int i; + Sat_MemRestart( &s->Mem ); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend( &s->Mem, NULL, 2, 0, 0 ); + s->binary = clause_read( s, s->hBinary ); + + veci_resize(&s->trail_lim, 0); + veci_resize(&s->order, 0); + for ( i = 0; i < s->size*2; i++ ) + s->wlists[i].size = 0; + + s->nDBreduces = 0; + + // initialize other vars + s->size = 0; +// s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + + // variable activities + solver_init_activities(s); + veci_resize(&s->act_clas, 0); + + + s->root_level = 0; +// s->simpdb_assigns = 0; +// s->simpdb_props = 0; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; +} + +void zsat_solver_restart_seed( sat_solver* s, double seed ) +{ + int i; + Sat_MemRestart( &s->Mem ); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend( &s->Mem, NULL, 2, 0, 0 ); + s->binary = clause_read( s, s->hBinary ); + + veci_resize(&s->trail_lim, 0); + veci_resize(&s->order, 0); + for ( i = 0; i < s->size*2; i++ ) + s->wlists[i].size = 0; + + s->nDBreduces = 0; + + // initialize other vars + s->size = 0; +// s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_resize(&s->act_clas, 0); + + s->root_level = 0; +// s->simpdb_assigns = 0; +// s->simpdb_props = 0; + s->random_seed = seed; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; +} + +// returns memory in bytes used by the SAT solver +double sat_solver_memory( sat_solver* s ) +{ + int i; + double Mem = sizeof(sat_solver); + for (i = 0; i < s->cap*2; i++) + Mem += s->wlists[i].cap * sizeof(int); + Mem += s->cap * sizeof(veci); // ABC_FREE(s->wlists ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->levels ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->assigns ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->polarity ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->tags ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->loads ); + Mem += s->cap * sizeof(word); // ABC_FREE(s->activity ); + if ( s->activity2 ) + Mem += s->cap * sizeof(word); // ABC_FREE(s->activity ); + if ( s->factors ) + Mem += s->cap * sizeof(double); // ABC_FREE(s->factors ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->orderpos ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->reasons ); + Mem += s->cap * sizeof(lit); // ABC_FREE(s->trail ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->model ); + + Mem += s->order.cap * sizeof(int); + Mem += s->trail_lim.cap * sizeof(int); + Mem += s->tagged.cap * sizeof(int); +// Mem += s->learned.cap * sizeof(int); + Mem += s->stack.cap * sizeof(int); + Mem += s->act_vars.cap * sizeof(int); + Mem += s->unit_lits.cap * sizeof(int); + Mem += s->act_clas.cap * sizeof(int); + Mem += s->temp_clause.cap * sizeof(int); + Mem += s->conf_final.cap * sizeof(int); + Mem += Sat_MemMemoryAll( &s->Mem ); + return Mem; +} + +int sat_solver_simplify(sat_solver* s) +{ + assert(sat_solver_dl(s) == 0); + if (sat_solver_propagate(s) != 0) + return false; + return true; +} + +void sat_solver_reducedb(sat_solver* s) +{ + static abctime TimeTotal = 0; + abctime clk = Abc_Clock(); + Sat_Mem_t * pMem = &s->Mem; + int nLearnedOld = veci_size(&s->act_clas); + int * act_clas = veci_begin(&s->act_clas); + int * pPerm, * pArray, * pSortValues, nCutoffValue; + int i, k, j, Id, Counter, CounterStart, nSelected; + clause * c; + + assert( s->nLearntMax > 0 ); + assert( nLearnedOld == Sat_MemEntryNum(pMem, 1) ); + assert( nLearnedOld == (int)s->stats.learnts ); + + s->nDBreduces++; + + //printf( "Calling reduceDB with %d learned clause limit.\n", s->nLearntMax ); + s->nLearntMax = s->nLearntStart + s->nLearntDelta * s->nDBreduces; +// return; + + // create sorting values + pSortValues = ABC_ALLOC( int, nLearnedOld ); + Sat_MemForEachLearned( pMem, c, i, k ) + { + Id = clause_id(c); +// pSortValues[Id] = act[Id]; + if ( s->ClaActType == 0 ) + pSortValues[Id] = ((7 - Abc_MinInt(c->lbd, 7)) << 28) | (act_clas[Id] >> 4); + else + pSortValues[Id] = ((7 - Abc_MinInt(c->lbd, 7)) << 28);// | (act_clas[Id] >> 4); + assert( pSortValues[Id] >= 0 ); + } + + // preserve 1/20 of last clauses + CounterStart = nLearnedOld - (s->nLearntMax / 20); + + // preserve 3/4 of most active clauses + nSelected = nLearnedOld*s->nLearntRatio/100; + + // find non-decreasing permutation + pPerm = Abc_MergeSortCost( pSortValues, nLearnedOld ); + assert( pSortValues[pPerm[0]] <= pSortValues[pPerm[nLearnedOld-1]] ); + nCutoffValue = pSortValues[pPerm[nLearnedOld-nSelected]]; + ABC_FREE( pPerm ); +// ActCutOff = ABC_INFINITY; + + // mark learned clauses to remove + Counter = j = 0; + Sat_MemForEachLearned( pMem, c, i, k ) + { + assert( c->mark == 0 ); + if ( Counter++ > CounterStart || clause_size(c) < 3 || pSortValues[clause_id(c)] > nCutoffValue || s->reasons[lit_var(c->lits[0])] == Sat_MemHand(pMem, i, k) ) + act_clas[j++] = act_clas[clause_id(c)]; + else // delete + { + c->mark = 1; + s->stats.learnts_literals -= clause_size(c); + s->stats.learnts--; + } + } + assert( s->stats.learnts == (unsigned)j ); + assert( Counter == nLearnedOld ); + veci_resize(&s->act_clas,j); + ABC_FREE( pSortValues ); + + // update ID of each clause to be its new handle + Counter = Sat_MemCompactLearned( pMem, 0 ); + assert( Counter == (int)s->stats.learnts ); + + // update reasons + for ( i = 0; i < s->size; i++ ) + { + if ( !s->reasons[i] ) // no reason + continue; + if ( clause_is_lit(s->reasons[i]) ) // 2-lit clause + continue; + if ( !clause_learnt_h(pMem, s->reasons[i]) ) // problem clause + continue; + c = clause_read( s, s->reasons[i] ); + assert( c->mark == 0 ); + s->reasons[i] = clause_id(c); // updating handle here!!! + } + + // update watches + for ( i = 0; i < s->size*2; i++ ) + { + pArray = veci_begin(&s->wlists[i]); + for ( j = k = 0; k < veci_size(&s->wlists[i]); k++ ) + { + if ( clause_is_lit(pArray[k]) ) // 2-lit clause + pArray[j++] = pArray[k]; + else if ( !clause_learnt_h(pMem, pArray[k]) ) // problem clause + pArray[j++] = pArray[k]; + else + { + c = clause_read(s, pArray[k]); + if ( !c->mark ) // useful learned clause + pArray[j++] = clause_id(c); // updating handle here!!! + } + } + veci_resize(&s->wlists[i],j); + } + + // perform final move of the clauses + Counter = Sat_MemCompactLearned( pMem, 1 ); + assert( Counter == (int)s->stats.learnts ); + + // report the results + TimeTotal += Abc_Clock() - clk; +} + + +// reverses to the previously bookmarked point +void sat_solver_rollback( sat_solver* s ) +{ + Sat_Mem_t * pMem = &s->Mem; + int i, k, j; + static int Count = 0; + Count++; + assert( s->iVarPivot >= 0 && s->iVarPivot <= s->size ); + assert( s->iTrailPivot >= 0 && s->iTrailPivot <= s->qtail ); + // reset implication queue + sat_solver_canceluntil_rollback( s, s->iTrailPivot ); + // update order + if ( s->iVarPivot < s->size ) + { + if ( s->activity2 ) + { + s->var_inc = s->var_inc2; + memcpy( s->activity, s->activity2, sizeof(word) * s->iVarPivot ); + } + veci_resize(&s->order, 0); + for ( i = 0; i < s->iVarPivot; i++ ) + { + if ( var_value(s, i) != varX ) + continue; + s->orderpos[i] = veci_size(&s->order); + veci_push(&s->order,i); + order_update(s, i); + } + } + // compact watches + for ( i = 0; i < s->iVarPivot*2; i++ ) + { + cla* pArray = veci_begin(&s->wlists[i]); + for ( j = k = 0; k < veci_size(&s->wlists[i]); k++ ) + { + if ( clause_is_lit(pArray[k]) ) + { + if ( clause_read_lit(pArray[k]) < s->iVarPivot*2 ) + pArray[j++] = pArray[k]; + } + else if ( Sat_MemClauseUsed(pMem, pArray[k]) ) + pArray[j++] = pArray[k]; + } + veci_resize(&s->wlists[i],j); + } + // reset watcher lists + for ( i = 2*s->iVarPivot; i < 2*s->size; i++ ) + s->wlists[i].size = 0; + + // reset clause counts + s->stats.clauses = pMem->BookMarkE[0]; + s->stats.learnts = pMem->BookMarkE[1]; + // rollback clauses + Sat_MemRollBack( pMem ); + + // resize learned arrays + veci_resize(&s->act_clas, s->stats.learnts); + + // initialize other vars + s->size = s->iVarPivot; + if ( s->size == 0 ) + { + // s->size = 0; + // s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + + s->root_level = 0; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + + // initialize rollback + s->iVarPivot = 0; // the pivot for variables + s->iTrailPivot = 0; // the pivot for trail + s->hProofPivot = 1; // the pivot for proof records + } +} + + +int sat_solver_addclause(sat_solver* s, lit* begin, lit* end) +{ + lit *i,*j; + int maxvar; + lit last; + assert( begin < end ); + if ( s->fPrintClause ) + { + for ( i = begin; i < end; i++ ) + printf( "%s%d ", (*i)&1 ? "!":"", (*i)>>1 ); + printf( "\n" ); + } + + veci_resize( &s->temp_clause, 0 ); + for ( i = begin; i < end; i++ ) + veci_push( &s->temp_clause, *i ); + begin = veci_begin( &s->temp_clause ); + end = begin + veci_size( &s->temp_clause ); + + // insertion sort + maxvar = lit_var(*begin); + for (i = begin + 1; i < end; i++){ + lit l = *i; + maxvar = lit_var(l) > maxvar ? lit_var(l) : maxvar; + for (j = i; j > begin && *(j-1) > l; j--) + *j = *(j-1); + *j = l; + } + sat_solver_setnvars(s,maxvar+1); + + /////////////////////////////////// + // add clause to internal storage + if ( s->pStore ) + { + int RetValue = Sto_ManAddClause( (Sto_Man_t *)s->pStore, begin, end ); + assert( RetValue ); + (void) RetValue; + } + /////////////////////////////////// + + // delete duplicates + last = lit_Undef; + for (i = j = begin; i < end; i++){ + //printf("lit: "L_LIT", value = %d\n", L_lit(*i), (lit_sign(*i) ? -s->assignss[lit_var(*i)] : s->assignss[lit_var(*i)])); + if (*i == lit_neg(last) || var_value(s, lit_var(*i)) == lit_sign(*i)) + return true; // tautology + else if (*i != last && var_value(s, lit_var(*i)) == varX) + last = *j++ = *i; + } +// j = i; + + if (j == begin) // empty clause + return false; + + if (j - begin == 1) // unit clause + return sat_solver_enqueue(s,*begin,0); + + // create new clause + sat_solver_clause_new(s,begin,j,0); + return true; +} + +double luby(double y, int x) +{ + int size, seq; + for (size = 1, seq = 0; size < x+1; seq++, size = 2*size + 1); + while (size-1 != x){ + size = (size-1) >> 1; + seq--; + x = x % size; + } + return pow(y, (double)seq); +} + +void luby_test() +{ + int i; + for ( i = 0; i < 20; i++ ) + printf( "%d ", (int)luby(2,i) ); + printf( "\n" ); +} + +static lbool sat_solver_search(sat_solver* s, ABC_INT64_T nof_conflicts) +{ +// double var_decay = 0.95; +// double clause_decay = 0.999; + double random_var_freq = s->fNotUseRandom ? 0.0 : 0.02; + ABC_INT64_T conflictC = 0; + veci learnt_clause; + int i; + + assert(s->root_level == sat_solver_dl(s)); + + s->nRestarts++; + s->stats.starts++; +// s->var_decay = (float)(1 / var_decay ); // move this to sat_solver_new() +// s->cla_decay = (float)(1 / clause_decay); // move this to sat_solver_new() +// veci_resize(&s->model,0); + veci_new(&learnt_clause); + + // use activity factors in every even restart + if ( (s->nRestarts & 1) && veci_size(&s->act_vars) > 0 ) +// if ( veci_size(&s->act_vars) > 0 ) + for ( i = 0; i < s->act_vars.size; i++ ) + act_var_bump_factor(s, s->act_vars.ptr[i]); + + // use activity factors in every restart + if ( s->pGlobalVars && veci_size(&s->act_vars) > 0 ) + for ( i = 0; i < s->act_vars.size; i++ ) + act_var_bump_global(s, s->act_vars.ptr[i]); + + for (;;){ + int hConfl = sat_solver_propagate(s); + if (hConfl != 0){ + // CONFLICT + int blevel; + +#ifdef VERBOSEDEBUG + printf(L_IND"**CONFLICT**\n", L_ind); +#endif + s->stats.conflicts++; conflictC++; + if (sat_solver_dl(s) == s->root_level){ +#ifdef SAT_USE_ANALYZE_FINAL + sat_solver_analyze_final(s, hConfl, 0); +#endif + veci_delete(&learnt_clause); + return l_False; + } + + veci_resize(&learnt_clause,0); + sat_solver_analyze(s, hConfl, &learnt_clause); + blevel = veci_size(&learnt_clause) > 1 ? var_level(s, lit_var(veci_begin(&learnt_clause)[1])) : s->root_level; + blevel = s->root_level > blevel ? s->root_level : blevel; + sat_solver_canceluntil(s,blevel); + sat_solver_record(s,&learnt_clause); +#ifdef SAT_USE_ANALYZE_FINAL +// if (learnt_clause.size() == 1) level[var(learnt_clause[0])] = 0; // (this is ugly (but needed for 'analyzeFinal()') -- in future versions, we will backtrack past the 'root_level' and redo the assumptions) + if ( learnt_clause.size == 1 ) + var_set_level(s, lit_var(learnt_clause.ptr[0]), 0); +#endif + act_var_decay(s); + act_clause_decay(s); + + }else{ + // NO CONFLICT + int next; + + // Reached bound on number of conflicts: + if ( (!s->fNoRestarts && nof_conflicts >= 0 && conflictC >= nof_conflicts) || (s->nRuntimeLimit && (s->stats.conflicts & 63) == 0 && Abc_Clock() > s->nRuntimeLimit)){ + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + return l_Undef; } + + // Reached bound on number of conflicts: + if ( (s->nConfLimit && s->stats.conflicts > s->nConfLimit) || + (s->nInsLimit && s->stats.propagations > s->nInsLimit) ) + { + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + return l_Undef; + } + + // Simplify the set of problem clauses: + if (sat_solver_dl(s) == 0 && !s->fSkipSimplify) + sat_solver_simplify(s); + + // Reduce the set of learnt clauses: +// if (s->nLearntMax && veci_size(&s->learned) - s->qtail >= s->nLearntMax) + if (s->nLearntMax && veci_size(&s->act_clas) >= s->nLearntMax) + sat_solver_reducedb(s); + + // New variable decision: + s->stats.decisions++; + next = order_select(s,(float)random_var_freq); + + if (next == var_Undef){ + // Model found: + int i; + for (i = 0; i < s->size; i++) + s->model[i] = (var_value(s,i)==var1 ? l_True : l_False); + sat_solver_canceluntil(s,s->root_level); + veci_delete(&learnt_clause); + + /* + veci apa; veci_new(&apa); + for (i = 0; i < s->size; i++) + veci_push(&apa,(int)(s->model.ptr[i] == l_True ? toLit(i) : lit_neg(toLit(i)))); + printf("model: "); printlits((lit*)apa.ptr, (lit*)apa.ptr + veci_size(&apa)); printf("\n"); + veci_delete(&apa); + */ + + return l_True; + } + + if ( var_polar(s, next) ) // positive polarity + sat_solver_decision(s,toLit(next)); + else + sat_solver_decision(s,lit_neg(toLit(next))); + } + } + + return l_Undef; // cannot happen +} + +// internal call to the SAT solver +int sat_solver_solve_internal(sat_solver* s) +{ + lbool status = l_Undef; + int restart_iter = 0; + veci_resize(&s->unit_lits, 0); + s->nCalls++; + + if (s->verbosity >= 1){ + printf("==================================[MINISAT]===================================\n"); + printf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); + printf("| | Clauses Literals | Limit Clauses Literals Lit/Cl | |\n"); + printf("==============================================================================\n"); + } + + while (status == l_Undef){ + ABC_INT64_T nof_conflicts; + double Ratio = (s->stats.learnts == 0)? 0.0 : + s->stats.learnts_literals / (double)s->stats.learnts; + if ( s->nRuntimeLimit && Abc_Clock() > s->nRuntimeLimit ) + break; + if (s->verbosity >= 1) + { + printf("| %9.0f | %7.0f %8.0f | %7.0f %7.0f %8.0f %7.1f | %6.3f %% |\n", + (double)s->stats.conflicts, + (double)s->stats.clauses, + (double)s->stats.clauses_literals, + (double)0, + (double)s->stats.learnts, + (double)s->stats.learnts_literals, + Ratio, + s->progress_estimate*100); + fflush(stdout); + } + nof_conflicts = (ABC_INT64_T)( 100 * luby(2, restart_iter++) ); + status = sat_solver_search(s, nof_conflicts); + // quit the loop if reached an external limit + if ( s->nConfLimit && s->stats.conflicts > s->nConfLimit ) + break; + if ( s->nInsLimit && s->stats.propagations > s->nInsLimit ) + break; + if ( s->nRuntimeLimit && Abc_Clock() > s->nRuntimeLimit ) + break; + if ( s->pFuncStop && s->pFuncStop(s->RunId) ) + break; + } + if (s->verbosity >= 1) + printf("==============================================================================\n"); + + sat_solver_canceluntil(s,s->root_level); + // save variable values + if ( status == l_True && s->user_vars.size ) + { + int v; + for ( v = 0; v < s->user_vars.size; v++ ) + veci_push(&s->user_values, sat_solver_var_value(s, s->user_vars.ptr[v])); + } + return status; +} + +// pushing one assumption to the stack of assumptions +int sat_solver_push(sat_solver* s, int p) +{ + assert(lit_var(p) < s->size); + veci_push(&s->trail_lim,s->qtail); + s->root_level++; + if (!sat_solver_enqueue(s,p,0)) + { + int h = s->reasons[lit_var(p)]; + if (h) + { + if (clause_is_lit(h)) + { + (clause_begin(s->binary))[1] = lit_neg(p); + (clause_begin(s->binary))[0] = clause_read_lit(h); + h = s->hBinary; + } + sat_solver_analyze_final(s, h, 1); + veci_push(&s->conf_final, lit_neg(p)); + } + else + { + veci_resize(&s->conf_final,0); + veci_push(&s->conf_final, lit_neg(p)); + // the two lines below are a bug fix by Siert Wieringa + if (var_level(s, lit_var(p)) > 0) + veci_push(&s->conf_final, p); + } + //sat_solver_canceluntil(s, 0); + return false; + } + else + { + int fConfl = sat_solver_propagate(s); + if (fConfl){ + sat_solver_analyze_final(s, fConfl, 0); + //assert(s->conf_final.size > 0); + //sat_solver_canceluntil(s, 0); + return false; } + } + return true; +} + +// removing one assumption from the stack of assumptions +void sat_solver_pop(sat_solver* s) +{ + assert( sat_solver_dl(s) > 0 ); + sat_solver_canceluntil(s, --s->root_level); +} + +void sat_solver_set_resource_limits(sat_solver* s, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal) +{ + // set the external limits + s->nRestarts = 0; + s->nConfLimit = 0; + s->nInsLimit = 0; + if ( nConfLimit ) + s->nConfLimit = s->stats.conflicts + nConfLimit; + if ( nInsLimit ) +// s->nInsLimit = s->stats.inspects + nInsLimit; + s->nInsLimit = s->stats.propagations + nInsLimit; + if ( nConfLimitGlobal && (s->nConfLimit == 0 || s->nConfLimit > nConfLimitGlobal) ) + s->nConfLimit = nConfLimitGlobal; + if ( nInsLimitGlobal && (s->nInsLimit == 0 || s->nInsLimit > nInsLimitGlobal) ) + s->nInsLimit = nInsLimitGlobal; +} + +int sat_solver_solve(sat_solver* s, lit* begin, lit* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal) +{ + lbool status; + lit * i; + //////////////////////////////////////////////// + if ( s->fSolved ) + { + if ( s->pStore ) + { + int RetValue = Sto_ManAddClause( (Sto_Man_t *)s->pStore, NULL, NULL ); + assert( RetValue ); + (void) RetValue; + } + return l_False; + } + //////////////////////////////////////////////// + + if ( s->fVerbose ) + printf( "Running SAT solver with parameters %d and %d and %d.\n", s->nLearntStart, s->nLearntDelta, s->nLearntRatio ); + + sat_solver_set_resource_limits( s, nConfLimit, nInsLimit, nConfLimitGlobal, nInsLimitGlobal ); + +#ifdef SAT_USE_ANALYZE_FINAL + // Perform assumptions: + s->root_level = 0; + for ( i = begin; i < end; i++ ) + if ( !sat_solver_push(s, *i) ) + { + sat_solver_canceluntil(s,0); + s->root_level = 0; + return l_False; + } + assert(s->root_level == sat_solver_dl(s)); +#else + //printf("solve: "); printlits(begin, end); printf("\n"); + for (i = begin; i < end; i++){ +// switch (lit_sign(*i) ? -s->assignss[lit_var(*i)] : s->assignss[lit_var(*i)]){ + switch (var_value(s, *i)) { + case var1: // l_True: + break; + case varX: // l_Undef + sat_solver_decision(s, *i); + if (sat_solver_propagate(s) == 0) + break; + // fallthrough + case var0: // l_False + sat_solver_canceluntil(s, 0); + return l_False; + } + } + s->root_level = sat_solver_dl(s); +#endif + + status = sat_solver_solve_internal(s); + + sat_solver_canceluntil(s,0); + s->root_level = 0; + + //////////////////////////////////////////////// + if ( status == l_False && s->pStore ) + { + int RetValue = Sto_ManAddClause( (Sto_Man_t *)s->pStore, NULL, NULL ); + assert( RetValue ); + (void) RetValue; + } + //////////////////////////////////////////////// + return status; +} + +// This LEXSAT procedure should be called with a set of literals (pLits, nLits), +// which defines both (1) variable order, and (2) assignment to begin search from. +// It retuns the LEXSAT assigment that is the same or larger than the given one. +// (It assumes that there is no smaller assignment than the one given!) +// The resulting assignment is returned in the same set of literals (pLits, nLits). +// It pushes/pops assumptions internally and will undo them before terminating. +int sat_solver_solve_lexsat( sat_solver* s, int * pLits, int nLits ) +{ + int i, iLitFail = -1; + lbool status; + assert( nLits > 0 ); + // help the SAT solver by setting desirable polarity + sat_solver_set_literal_polarity( s, pLits, nLits ); + // check if there exists a satisfying assignment + status = sat_solver_solve_internal( s ); + if ( status != l_True ) // no assignment + return status; + // there is at least one satisfying assignment + assert( status == l_True ); + // find the first mismatching literal + for ( i = 0; i < nLits; i++ ) + if ( pLits[i] != sat_solver_var_literal(s, Abc_Lit2Var(pLits[i])) ) + break; + if ( i == nLits ) // no mismatch - the current assignment is the minimum one! + return l_True; + // mismatch happens in literal i + iLitFail = i; + // create assumptions up to this literal (as in pLits) - including this literal! + for ( i = 0; i <= iLitFail; i++ ) + if ( !sat_solver_push(s, pLits[i]) ) // can become UNSAT while adding the last assumption + break; + if ( i < iLitFail + 1 ) // the solver became UNSAT while adding assumptions + status = l_False; + else // solve under the assumptions + status = sat_solver_solve_internal( s ); + if ( status == l_True ) + { + // we proved that there is a sat assignment with literal (iLitFail) having polarity as in pLits + // continue solving recursively + if ( iLitFail + 1 < nLits ) + status = sat_solver_solve_lexsat( s, pLits + iLitFail + 1, nLits - iLitFail - 1 ); + } + else if ( status == l_False ) + { + // we proved that there is no assignment with iLitFail having polarity as in pLits + assert( Abc_LitIsCompl(pLits[iLitFail]) ); // literal is 0 + // (this assert may fail only if there is a sat assignment smaller than one originally given in pLits) + // now we flip this literal (make it 1), change the last assumption + // and contiue looking for the 000...0-assignment of other literals + sat_solver_pop( s ); + pLits[iLitFail] = Abc_LitNot(pLits[iLitFail]); + if ( !sat_solver_push(s, pLits[iLitFail]) ) + printf( "sat_solver_solve_lexsat(): A satisfying assignment should exist.\n" ); // because we know that the problem is satisfiable + // update other literals to be 000...0 + for ( i = iLitFail + 1; i < nLits; i++ ) + pLits[i] = Abc_LitNot( Abc_LitRegular(pLits[i]) ); + // continue solving recursively + if ( iLitFail + 1 < nLits ) + status = sat_solver_solve_lexsat( s, pLits + iLitFail + 1, nLits - iLitFail - 1 ); + else + status = l_True; + } + // undo the assumptions + for ( i = iLitFail; i >= 0; i-- ) + sat_solver_pop( s ); + return status; +} + +// This procedure is called on a set of assumptions to minimize their number. +// The procedure relies on the fact that the current set of assumptions is UNSAT. +// It receives and returns SAT solver without assumptions. It returns the number +// of assumptions after minimization. The set of assumptions is returned in pLits. +int sat_solver_minimize_assumptions( sat_solver* s, int * pLits, int nLits, int nConfLimit ) +{ + int i, k, nLitsL, nLitsR, nResL, nResR, status; + if ( nLits == 1 ) + { + // since the problem is UNSAT, we will try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + if ( nConfLimit ) s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal( s ); + //printf( "%c", status == l_False ? 'u' : 's' ); + return (int)(status != l_False); // return 1 if the problem is not UNSAT + } + assert( nLits >= 2 ); + nLitsL = nLits / 2; + nLitsR = nLits - nLitsL; + // assume the left lits + for ( i = 0; i < nLitsL; i++ ) + if ( !sat_solver_push(s, pLits[i]) ) + { + for ( k = i; k >= 0; k-- ) + sat_solver_pop(s); + return sat_solver_minimize_assumptions( s, pLits, i+1, nConfLimit ); + } + // solve with these assumptions + if ( nConfLimit ) s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal( s ); + if ( status == l_False ) // these are enough + { + for ( i = 0; i < nLitsL; i++ ) + sat_solver_pop(s); + return sat_solver_minimize_assumptions( s, pLits, nLitsL, nConfLimit ); + } + // solve for the right lits + nResL = nLitsR == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nLitsL, nLitsR, nConfLimit ); + for ( i = 0; i < nLitsL; i++ ) + sat_solver_pop(s); + // swap literals +// assert( nResL <= nLitsL ); +// for ( i = 0; i < nResL; i++ ) +// ABC_SWAP( int, pLits[i], pLits[nLitsL+i] ); + veci_resize( &s->temp_clause, 0 ); + for ( i = 0; i < nLitsL; i++ ) + veci_push( &s->temp_clause, pLits[i] ); + for ( i = 0; i < nResL; i++ ) + pLits[i] = pLits[nLitsL+i]; + for ( i = 0; i < nLitsL; i++ ) + pLits[nResL+i] = veci_begin(&s->temp_clause)[i]; + // assume the right lits + for ( i = 0; i < nResL; i++ ) + if ( !sat_solver_push(s, pLits[i]) ) + { + for ( k = i; k >= 0; k-- ) + sat_solver_pop(s); + return sat_solver_minimize_assumptions( s, pLits, i+1, nConfLimit ); + } + // solve with these assumptions + if ( nConfLimit ) s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal( s ); + if ( status == l_False ) // these are enough + { + for ( i = 0; i < nResL; i++ ) + sat_solver_pop(s); + return nResL; + } + // solve for the left lits + nResR = nLitsL == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nResL, nLitsL, nConfLimit ); + for ( i = 0; i < nResL; i++ ) + sat_solver_pop(s); + return nResL + nResR; +} + +// This is a specialized version of the above procedure with several custom changes: +// - makes sure that at least one of the marked literals is preserved in the clause +// - sets literals to zero when they do not have to be used +// - sets literals to zero for disproved variables +int sat_solver_minimize_assumptions2( sat_solver* s, int * pLits, int nLits, int nConfLimit ) +{ + int i, k, nLitsL, nLitsR, nResL, nResR; + if ( nLits == 1 ) + { + // since the problem is UNSAT, we will try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + int RetValue = 1, LitNot = Abc_LitNot(pLits[0]); + int status = l_False; + int Temp = s->nConfLimit; + s->nConfLimit = nConfLimit; + + RetValue = sat_solver_push( s, LitNot ); assert( RetValue ); + status = sat_solver_solve_internal( s ); + sat_solver_pop( s ); + + // if the problem is UNSAT, add clause + if ( status == l_False ) + { + RetValue = sat_solver_addclause( s, &LitNot, &LitNot+1 ); + assert( RetValue ); + } + + s->nConfLimit = Temp; + return (int)(status != l_False); // return 1 if the problem is not UNSAT + } + assert( nLits >= 2 ); + nLitsL = nLits / 2; + nLitsR = nLits - nLitsL; + // assume the left lits + for ( i = 0; i < nLitsL; i++ ) + if ( !sat_solver_push(s, pLits[i]) ) + { + for ( k = i; k >= 0; k-- ) + sat_solver_pop(s); + + // add clauses for these literal + for ( k = i+1; k > nLitsL; k++ ) + { + int LitNot = Abc_LitNot(pLits[i]); + int RetValue = sat_solver_addclause( s, &LitNot, &LitNot+1 ); + assert( RetValue ); + } + + return sat_solver_minimize_assumptions2( s, pLits, i+1, nConfLimit ); + } + // solve for the right lits + nResL = sat_solver_minimize_assumptions2( s, pLits + nLitsL, nLitsR, nConfLimit ); + for ( i = 0; i < nLitsL; i++ ) + sat_solver_pop(s); + // swap literals +// assert( nResL <= nLitsL ); + veci_resize( &s->temp_clause, 0 ); + for ( i = 0; i < nLitsL; i++ ) + veci_push( &s->temp_clause, pLits[i] ); + for ( i = 0; i < nResL; i++ ) + pLits[i] = pLits[nLitsL+i]; + for ( i = 0; i < nLitsL; i++ ) + pLits[nResL+i] = veci_begin(&s->temp_clause)[i]; + // assume the right lits + for ( i = 0; i < nResL; i++ ) + if ( !sat_solver_push(s, pLits[i]) ) + { + for ( k = i; k >= 0; k-- ) + sat_solver_pop(s); + + // add clauses for these literal + for ( k = i+1; k > nResL; k++ ) + { + int LitNot = Abc_LitNot(pLits[i]); + int RetValue = sat_solver_addclause( s, &LitNot, &LitNot+1 ); + assert( RetValue ); + } + + return sat_solver_minimize_assumptions2( s, pLits, i+1, nConfLimit ); + } + // solve for the left lits + nResR = sat_solver_minimize_assumptions2( s, pLits + nResL, nLitsL, nConfLimit ); + for ( i = 0; i < nResL; i++ ) + sat_solver_pop(s); + return nResL + nResR; +} + + + +int sat_solver_nvars(sat_solver* s) +{ + return s->size; +} + + +int sat_solver_nclauses(sat_solver* s) +{ + return s->stats.clauses; +} + + +int sat_solver_nconflicts(sat_solver* s) +{ + return (int)s->stats.conflicts; +} + +//================================================================================================= +// Clause storage functions: + +void sat_solver_store_alloc( sat_solver * s ) +{ + assert( s->pStore == NULL ); + s->pStore = Sto_ManAlloc(); +} + +void sat_solver_store_write( sat_solver * s, char * pFileName ) +{ + if ( s->pStore ) Sto_ManDumpClauses( (Sto_Man_t *)s->pStore, pFileName ); +} + +void sat_solver_store_free( sat_solver * s ) +{ + if ( s->pStore ) Sto_ManFree( (Sto_Man_t *)s->pStore ); + s->pStore = NULL; +} + +int sat_solver_store_change_last( sat_solver * s ) +{ + if ( s->pStore ) return Sto_ManChangeLastClause( (Sto_Man_t *)s->pStore ); + return -1; +} + +void sat_solver_store_mark_roots( sat_solver * s ) +{ + if ( s->pStore ) Sto_ManMarkRoots( (Sto_Man_t *)s->pStore ); +} + +void sat_solver_store_mark_clauses_a( sat_solver * s ) +{ + if ( s->pStore ) Sto_ManMarkClausesA( (Sto_Man_t *)s->pStore ); +} + +void * sat_solver_store_release( sat_solver * s ) +{ + void * pTemp; + if ( s->pStore == NULL ) + return NULL; + pTemp = s->pStore; + s->pStore = NULL; + return pTemp; +} + + +ABC_NAMESPACE_IMPL_END + diff --git a/lib/abcsat/satStore.cpp b/lib/abcsat/satStore.cpp new file mode 100644 index 0000000..1b382c0 --- /dev/null +++ b/lib/abcsat/satStore.cpp @@ -0,0 +1,469 @@ +/**CFile**************************************************************** + + FileName [satStore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver.] + + Synopsis [Records the trace of SAT solving in the CNF form.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satStore.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include +#include +#include +#include + +#include + +ABC_NAMESPACE_IMPL_START + + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Fetches memory.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +char * Sto_ManMemoryFetch( Sto_Man_t * p, int nBytes ) +{ + char * pMem; + if ( p->pChunkLast == NULL || nBytes > p->nChunkSize - p->nChunkUsed ) + { + pMem = (char *)ABC_ALLOC( char, p->nChunkSize ); + *(char **)pMem = p->pChunkLast; + p->pChunkLast = pMem; + p->nChunkUsed = sizeof(char *); + } + pMem = p->pChunkLast + p->nChunkUsed; + p->nChunkUsed += nBytes; + return pMem; +} + +/**Function************************************************************* + + Synopsis [Frees memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMemoryStop( Sto_Man_t * p ) +{ + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return; + for ( pMem = p->pChunkLast; (pNext = *(char **)pMem); pMem = pNext ) + ABC_FREE( pMem ); + ABC_FREE( pMem ); +} + +/**Function************************************************************* + + Synopsis [Reports memory usage in bytes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManMemoryReport( Sto_Man_t * p ) +{ + int Total; + char * pMem, * pNext; + if ( p->pChunkLast == NULL ) + return 0; + Total = p->nChunkUsed; + for ( pMem = p->pChunkLast; (pNext = *(char **)pMem); pMem = pNext ) + Total += p->nChunkSize; + return Total; +} + + +/**Function************************************************************* + + Synopsis [Allocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sto_Man_t * Sto_ManAlloc() +{ + Sto_Man_t * p; + // allocate the manager + p = (Sto_Man_t *)ABC_ALLOC( char, sizeof(Sto_Man_t) ); + memset( p, 0, sizeof(Sto_Man_t) ); + // memory management + p->nChunkSize = (1<<16); // use 64K chunks + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManFree( Sto_Man_t * p ) +{ + Sto_ManMemoryStop( p ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManAddClause( Sto_Man_t * p, lit * pBeg, lit * pEnd ) +{ + Sto_Cls_t * pClause; + lit Lit, * i, * j; + int nSize; + + // process the literals + if ( pBeg < pEnd ) + { + // insertion sort + for ( i = pBeg + 1; i < pEnd; i++ ) + { + Lit = *i; + for ( j = i; j > pBeg && *(j-1) > Lit; j-- ) + *j = *(j-1); + *j = Lit; + } + // make sure there is no duplicated variables + for ( i = pBeg + 1; i < pEnd; i++ ) + if ( lit_var(*(i-1)) == lit_var(*i) ) + { + printf( "The clause contains two literals of the same variable: %d and %d.\n", *(i-1), *i ); + return 0; + } + // check the largest var size + p->nVars = STO_MAX( p->nVars, lit_var(*(pEnd-1)) + 1 ); + } + + // get memory for the clause + nSize = sizeof(Sto_Cls_t) + sizeof(lit) * (pEnd - pBeg); + nSize = (nSize / sizeof(char*) + ((nSize % sizeof(char*)) > 0)) * sizeof(char*); // added by Saurabh on Sep 3, 2009 + pClause = (Sto_Cls_t *)Sto_ManMemoryFetch( p, nSize ); + memset( pClause, 0, sizeof(Sto_Cls_t) ); + + // assign the clause + pClause->Id = p->nClauses++; + pClause->nLits = pEnd - pBeg; + memcpy( pClause->pLits, pBeg, sizeof(lit) * (pEnd - pBeg) ); +// assert( pClause->pLits[0] >= 0 ); + + // add the clause to the list + if ( p->pHead == NULL ) + p->pHead = pClause; + if ( p->pTail == NULL ) + p->pTail = pClause; + else + { + p->pTail->pNext = pClause; + p->pTail = pClause; + } + + // add the empty clause + if ( pClause->nLits == 0 ) + { + if ( p->pEmpty ) + { + printf( "More than one empty clause!\n" ); + return 0; + } + p->pEmpty = pClause; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as root clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMarkRoots( Sto_Man_t * p ) +{ + Sto_Cls_t * pClause; + p->nRoots = 0; + Sto_ManForEachClause( p, pClause ) + { + pClause->fRoot = 1; + p->nRoots++; + } +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as clause of A.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManMarkClausesA( Sto_Man_t * p ) +{ + Sto_Cls_t * pClause; + p->nClausesA = 0; + Sto_ManForEachClause( p, pClause ) + { + pClause->fA = 1; + p->nClausesA++; + } +} + +/**Function************************************************************* + + Synopsis [Returns the literal of the last clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManChangeLastClause( Sto_Man_t * p ) +{ + Sto_Cls_t * pClause, * pPrev; + pPrev = NULL; + Sto_ManForEachClause( p, pClause ) + pPrev = pClause; + assert( pPrev != NULL ); + assert( pPrev->fA == 1 ); + assert( pPrev->nLits == 1 ); + p->nClausesA--; + pPrev->fA = 0; + return pPrev->pLits[0] >> 1; +} + + +/**Function************************************************************* + + Synopsis [Writes the stored clauses into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Sto_ManDumpClauses( Sto_Man_t * p, char * pFileName ) +{ + FILE * pFile; + Sto_Cls_t * pClause; + int i; + // start the file + pFile = fopen( pFileName, "w" ); + if ( pFile == NULL ) + { + printf( "Error: Cannot open output file (%s).\n", pFileName ); + return; + } + // write the data + fprintf( pFile, "p %d %d %d %d\n", p->nVars, p->nClauses, p->nRoots, p->nClausesA ); + Sto_ManForEachClause( p, pClause ) + { + for ( i = 0; i < (int)pClause->nLits; i++ ) + fprintf( pFile, " %d", lit_print(pClause->pLits[i]) ); + fprintf( pFile, " 0\n" ); + } +// fprintf( pFile, " 0\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [Reads one literal from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Sto_ManLoadNumber( FILE * pFile, int * pNumber ) +{ + int Char, Number = 0, Sign = 0; + // skip space-like chars + do { + Char = fgetc( pFile ); + if ( Char == EOF ) + return 0; + } while ( Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n' ); + // read the literal + while ( 1 ) + { + // get the next character + Char = fgetc( pFile ); + if ( Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n' ) + break; + // check that the char is a digit + if ( (Char < '0' || Char > '9') && Char != '-' ) + { + printf( "Error: Wrong char (%c) in the input file.\n", Char ); + return 0; + } + // check if this is a minus + if ( Char == '-' ) + Sign = 1; + else + Number = 10 * Number + Char; + } + // return the number + *pNumber = Sign? -Number : Number; + return 1; +} + +/**Function************************************************************* + + Synopsis [Reads CNF from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Sto_Man_t * Sto_ManLoadClauses( char * pFileName ) +{ + FILE * pFile; + Sto_Man_t * p; + Sto_Cls_t * pClause; + char pBuffer[1024]; + int nLits, nLitsAlloc, Counter, Number; + lit * pLits; + + // start the file + pFile = fopen( pFileName, "r" ); + if ( pFile == NULL ) + { + printf( "Error: Cannot open input file (%s).\n", pFileName ); + return NULL; + } + + // create the manager + p = Sto_ManAlloc(); + + // alloc the array of literals + nLitsAlloc = 1024; + pLits = (lit *)ABC_ALLOC( char, sizeof(lit) * nLitsAlloc ); + + // read file header + p->nVars = p->nClauses = p->nRoots = p->nClausesA = 0; + while ( fgets( pBuffer, 1024, pFile ) ) + { + if ( pBuffer[0] == 'c' ) + continue; + if ( pBuffer[0] == 'p' ) + { + sscanf( pBuffer + 1, "%d %d %d %d", &p->nVars, &p->nClauses, &p->nRoots, &p->nClausesA ); + break; + } + printf( "Warning: Skipping line: \"%s\"\n", pBuffer ); + } + + // read the clauses + nLits = 0; + while ( Sto_ManLoadNumber(pFile, &Number) ) + { + if ( Number == 0 ) + { + int RetValue; + RetValue = Sto_ManAddClause( p, pLits, pLits + nLits ); + assert( RetValue ); + nLits = 0; + continue; + } + if ( nLits == nLitsAlloc ) + { + nLitsAlloc *= 2; + pLits = ABC_REALLOC( lit, pLits, nLitsAlloc ); + } + pLits[ nLits++ ] = lit_read(Number); + } + if ( nLits > 0 ) + printf( "Error: The last clause was not saved.\n" ); + + // count clauses + Counter = 0; + Sto_ManForEachClause( p, pClause ) + Counter++; + + // check the number of clauses + if ( p->nClauses != Counter ) + { + printf( "Error: The actual number of clauses (%d) is different than declared (%d).\n", Counter, p->nClauses ); + Sto_ManFree( p ); + return NULL; + } + + ABC_FREE( pLits ); + fclose( pFile ); + return p; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + +ABC_NAMESPACE_IMPL_END + diff --git a/lib/bill/bill/bill.hpp b/lib/bill/bill/bill.hpp new file mode 100644 index 0000000..9d0012b --- /dev/null +++ b/lib/bill/bill/bill.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(BILL_WINDOWS_PLATFORM) +#include +#endif +#include +#include +#include +#include +#include diff --git a/lib/bill/bill/dd/cudd_zdd.hpp b/lib/bill/bill/dd/cudd_zdd.hpp new file mode 100644 index 0000000..e30ce9a --- /dev/null +++ b/lib/bill/bill/dd/cudd_zdd.hpp @@ -0,0 +1,493 @@ +#include "cplusplus/cuddObj.hh" +#include "cudd/cuddInt.h" +#include +#include +#include +#include +#include +#include + +namespace cudd { + +class cudd_zdd +{ +public: + cudd_zdd( uint32_t num_variables ) + : num_variables( num_variables ), empty( cudd.zddZero() ), base( cudd.zddOne( INT_MAX ) ) + { + elementaries.reserve( num_variables ); + for ( auto i = 0u; i < num_variables; ++i ) + { + elementaries.emplace_back( base.Change( i ) ); + } + tautologies.reserve( num_variables ); + for ( auto i = 0u; i < num_variables; ++i ) + { + tautologies.emplace_back( cudd.zddOne( i ) ); + } + assert( cudd.ReadZddSize() == int( num_variables ) ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + } + + ~cudd_zdd() + { + } + + void print( ZDD const& node, std::string const& name = "", int verbosity = 4 ) + { + std::cout << name << " (" << node.getNode() << ", level = " << Cudd_NodeReadIndex( node.getNode() ) << ")"; + node.print( num_variables, verbosity ); + } + + ZDD ref( ZDD const& node ) + { + Cudd_Ref( node.getNode() ); + return node; + } + + void deref_rec( ZDD const& node ) + { + Cudd_RecursiveDerefZdd( cudd.getManager(), node.getNode() ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + } + + /* only decrease ref count of `node` but not recursively on its children */ + void deref( ZDD const& node ) + { + Cudd_Deref( node.getNode() ); + } + + /* Create a node at level `var`, whose "then" child points to `T` and "else" child points to `E`, + * or return the node if it already exists. + * The result ZDD object is pass by copy, hence ref count is incresed by the copy constructor. + * + * `T` and `E` should be lower than `var` (larger indices are lower) + * Note the order of `T` and `E` is the same as in `bill`, but different from CUDD. + */ + ZDD unique( uint32_t var, ZDD const& E, ZDD const& T ) + { + assert( T.NodeReadIndex() > var && E.NodeReadIndex() > var ); + return ZDD( cudd, cuddZddGetNode( cudd.getManager(), var, T.getNode(), E.getNode() ) ); + } + +public: /* basic getters; does NOT increase ref count */ + ZDD& bottom() { return empty; } + ZDD& top() { return base; } + + ZDD& elementary( uint32_t var ) + { + assert( var < num_variables ); + return elementaries[var]; + } + + ZDD& tautology( uint32_t var = 0 ) + { + assert( var < num_variables ); + return tautologies[var]; + } + +public: /* operations provided by CUDD, wrapped with `bill` function names */ + ZDD union_( ZDD const& f, ZDD const& g ) + { + return f.Union( g ); + } + + ZDD intersection( ZDD const& f, ZDD const& g ) + { + return f.Intersect( g ); + } + + ZDD difference( ZDD const& f, ZDD const& g ) + { + return f.Diff( g ); + } + +public: /* operations not provided by CUDD */ + /* union every pair of subsets in f and g */ + ZDD join( ZDD const& f, ZDD const& g ) + { + assert( ( f == empty || f == base || f.NodeReadIndex() < num_variables) && ( g == empty || g == base || g.NodeReadIndex() < num_variables ) ); + auto r = ZDD( cudd, join( f.getNode(), g.getNode() ) ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + return r; + } + + /* resulting sets are elements in f, but not superset of any element in g */ + /* \forall A \in result, A \in f and \forall B \in g, A \notsuperset B */ + ZDD nonsupersets( ZDD const& f, ZDD const& g ) + { + assert( ( f == empty || f == base || f.NodeReadIndex() < num_variables) && ( g == empty || g == base || g.NodeReadIndex() < num_variables ) ); + auto r = ZDD( cudd, nonsupersets( f.getNode(), g.getNode() ) ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + return r; + } + + ZDD choose( ZDD const& f, uint32_t k ) + { + assert( f == empty || f == base || f.NodeReadIndex() < num_variables ); + auto r = ZDD( cudd, choose( f.getNode(), k ) ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + return r; + } + + /* builds an ZDD with one minterm, having '-' at variables in `vec`, and '0' at other variables + * `vec` is assumed to be sorted decreasingly + */ + ZDD care( std::vector const& vec ) + { + DdNode* n1 = base.getNode(); + DdNode* n2 = 0; + + for ( auto i : vec ) + { + assert( i < num_variables ); + n2 = unique( i, n1, n1 ); ref( n2 ); + if ( n1 != base.getNode() ) { Cudd_Deref( n1 ); } + n1 = n2; + } + + Cudd_Deref( n2 ); + auto r = ZDD( cudd, n2 ); + assert( Cudd_DebugCheck( cudd.getManager() ) == 0 ); + return r; + } + + /* inverse of care */ + ZDD dont_care( std::vector const& vec ) + { + std::vector vec2; + for ( int i = num_variables - 1; i >= 0; --i ) + { + bool found = false; + for ( auto j : vec ) + { + if ( j == (unsigned)i ) + { + found = true; + break; + } + } + if ( !found ) + { + vec2.emplace_back( i ); + } + } + + return care( vec2 ); + } + + //edivide + //maximal + //meet + //nonsubsets + +private: /* implementation details */ + DdNode* lo( DdNode* f ) { return cuddE( f ); } + DdNode* hi( DdNode* f ) { return cuddT( f ); } + DdNode* ref( DdNode* node ) { Cudd_Ref( node ); return node; } + DdNode* deref( DdNode* node ) + { + cuddSatDec( node->ref ); + if ( node->ref == 0 ) + { + DdManager * table = cudd.getManager(); + table->deadZ++; + #ifdef DD_STATS + table->nodesDropped++; + #endif + assert( !cuddIsConstant( node ) ); + int ord = table->permZ[node->index]; + table->subtableZ[ord].dead++; + if ( !cuddIsConstant( cuddT( node ) ) ) { assert( cuddT( node )->ref != 0 ); Cudd_RecursiveDerefZdd( table, cuddT( node ) ); } + if ( !cuddIsConstant( cuddE( node ) ) ) { assert( cuddE( node )->ref != 0 ); Cudd_RecursiveDerefZdd( table, cuddE( node ) ); } + } + return node; + } + + DdNode* unique( uint32_t var, DdNode* lo, DdNode* hi ) + { return cuddZddGetNode( cudd.getManager(), var, hi, lo ); } + + DdNode* union_( DdNode* f, DdNode* g ) + { return Cudd_zddUnion( cudd.getManager(), f, g ); } + + DdNode* intersection( DdNode* f, DdNode* g ) + { return Cudd_zddIntersect( cudd.getManager(), f, g ); } + + DdNode* difference( DdNode* f, DdNode* g ) + { return Cudd_zddDiff( cudd.getManager(), f, g ); } + + /* replacement of function pointers to be used in the operation cache */ + uint64_t join_ = 2; + uint64_t nonsupersets_ = 4; + uint64_t choose_ = 6; + + DdNode* join( DdNode* f, DdNode* g ) + { + /* terminal cases */ + DdNode* e = empty.getNode(); + DdNode* b = base.getNode(); + if ( f == e || g == e ) { return e; } + if ( f == b ) { return g; } + if ( g == b ) { return f; } + + if ( Cudd_NodeReadIndex( f ) < Cudd_NodeReadIndex( g ) ) { return join( g, f ); } + + /* cache lookup */ + auto res = cache_lookup( cudd.getManager(), join_, f, g ); + if ( res != NULL ) { return res; } + + uint32_t var = Cudd_NodeReadIndex( g ); + DdNode* lo = 0; + DdNode* hi = 0; + + if ( Cudd_NodeReadIndex( f ) > Cudd_NodeReadIndex( g ) ) + { + lo = join( f, cuddE( g ) ); ref( lo ); + hi = join( f, cuddT( g ) ); ref( hi ); + } + else /* f_var == g_var */ + { + #if 1 /* Knuth's book */ + DdNode* tmp0 = join( cuddE( f ), cuddT( g ) ); ref( tmp0 ); + DdNode* tmp1 = join( cuddT( f ), cuddE( g ) ); ref( tmp1 ); + DdNode* tmp2 = join( cuddT( f ), cuddT( g ) ); ref( tmp2 ); + DdNode* tmp3 = union_( tmp0, tmp1 ); ref( tmp3 ); + deref( tmp0 ); deref( tmp1 ); + hi = union_( tmp2, tmp3 ); ref( hi ); + deref( tmp2 ); deref( tmp3 ); + #else /* Bruno's implementation */ + DdNode* tmp0 = union_( cuddE( g ), cuddT( g ) ); ref( tmp0 ); + DdNode* tmp1 = join( cuddT( f ), tmp0 ); ref( tmp1 ); + deref( tmp0 ); + DdNode* tmp2 = join( cuddE( f ), cuddT( g ) ); ref( tmp2 ); + hi = union_( tmp1, tmp2 ); ref( hi ); + deref( tmp1 ); deref( tmp2 ); + #endif + lo = join( cuddE( f ), cuddE( g ) ); ref( lo ); + } + auto r = unique( var, lo, hi ); + deref( lo ); deref( hi ); + + cache_insert( cudd.getManager(), join_, f, g, r ); + return r; + } + + DdNode* nonsupersets( DdNode* f, DdNode* g ) + { + /* terminal cases */ + DdNode* e = empty.getNode(); + DdNode* b = base.getNode(); + if ( g == e ) { return f; } + if ( f == e || g == b || f == g ) { return e; } + + if ( Cudd_NodeReadIndex( f ) > Cudd_NodeReadIndex( g ) ) + { return nonsupersets( f, cuddE( g ) ); } + + /* cache lookup */ + auto res = cache_lookup( cudd.getManager(), nonsupersets_, f, g ); + if ( res != NULL ) { return res; } + + /* recursive computation */ + DdNode* lo = 0; + DdNode* hi = 0; + if ( Cudd_NodeReadIndex( f ) < Cudd_NodeReadIndex( g ) ) + { + lo = nonsupersets( cuddE( f ), g ); ref( lo ); + hi = nonsupersets( cuddT( f ), g ); ref( hi ); + } + else /* f_var == g_var */ + { + DdNode* tmp0 = nonsupersets( cuddT( f ), cuddT( g ) ); ref( tmp0 ); + DdNode* tmp1 = nonsupersets( cuddT( f ), cuddE( g ) ); ref( tmp1 ); + hi = intersection( tmp0, tmp1 ); ref( hi ); + deref( tmp0 ); deref( tmp1 ); + lo = nonsupersets( cuddE( f ), cuddE( g ) ); ref( lo ); + } + auto r = unique( Cudd_NodeReadIndex( f ), lo, hi ); + //deref( lo ); deref( hi ); + + cache_insert( cudd.getManager(), nonsupersets_, f, g, r ); + return r; + } + + DdNode* choose( DdNode* f, uint64_t k ) + { + if ( Cudd_NodeReadIndex( f ) >= num_variables ) + { + return k > 0 ? empty.getNode() : base.getNode(); + } + if ( k == 1 ) { return f; } + if ( k == 0 ) { return choose( cuddE( f ), k ); } + + /* cache lookup */ + auto res = cache_lookup( cudd.getManager(), choose_ + k * 2, f, f ); + if ( res != NULL ) { return res; } + + /* k > 0 */ + DdNode* n = choose( cuddE( f ), k ); ref( n ); /* don't take this var */ + DdNode* tmp = choose( cuddE( f ), k - 1 ); ref( tmp ); /* take this var */ + auto r = unique( Cudd_NodeReadIndex( f ), n, tmp ); + deref( n ); deref( tmp ); + + cache_insert( cudd.getManager(), choose_ + k * 2, f, f, r ); + return r; + } + +private: /* iterator, counting, etc */ + template + bool foreach_set_rec( DdNode* f, std::vector& set, Fn&& fn ) const + { + if ( f == base.getNode() ) + { + return fn(set); + } + if ( f != empty.getNode() ) + { + if ( !foreach_set_rec( cuddE( f ), set, fn ) ) + { + return false; + } + auto new_set = set; + new_set.push_back( Cudd_NodeReadIndex( f ) ); + if ( !foreach_set_rec( cuddT( f ), new_set, fn ) ) + { + return false; + } + } + return true; + } + + uint64_t count_sets_rec( DdNode* f, std::unordered_map& visited ) const + { + if ( f == base.getNode() ) + { + return 1; + } + if ( f == empty.getNode() ) + { + return 0; + } + + const auto it = visited.find( f ); + if ( it != visited.end() ) + { + return it->second; + } + return visited[f] = count_sets_rec( cuddE( f ), visited ) + count_sets_rec( cuddT( f ), visited ); + } + +public: + /* a set is represented with a `std::vector` of variable indices */ + template + void foreach_set( ZDD const& f, Fn&& fn ) const + { + std::vector set; + foreach_set_rec( f.getNode(), set, fn ); + } + + void print_sets( ZDD const& f, std::ostream& os = std::cout ) const + { + foreach_set( f, [&]( auto const& set ){ + os << fmt::format("{{ {} }}\n", fmt::join( set, ", " ) ); + return true; + }); + } + + /* \!brief Return the number of nodes in a ZDD. */ + uint64_t count_nodes( DdNode* f ) const + { + return Cudd_zddDagSize(f); + } + + /* \!brief Return the number of sets in a ZDD. */ + uint64_t count_sets( ZDD const& f ) const + { + if ( f.getNode() == base.getNode() ) + { + return 1; + } + if ( f.getNode() == empty.getNode() ) + { + return 0; + } + std::unordered_map visited; + return count_sets_rec( f.getNode(), visited ); + } + + std::vector> sets_as_vectors( ZDD const& f ) const + { + std::vector> sets_vectors; + foreach_set( f, [&]( auto const& set ){ + sets_vectors.emplace_back( set ); + return true; + }); + return sets_vectors; + } + +private: /* operation cache */ + DdNode * cache_lookup( DdManager * table, uint64_t op, DdNode * f, DdNode * g ) + { + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; + #ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } + #endif + + posn = ddCHash2(op,f,g,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + } + + void cache_insert( DdManager * table, uint64_t op, DdNode * f, DdNode * g, DdNode * data ) + { + int posn; + DdCache *entry; + + posn = ddCHash2(op,f,g,table->cacheShift); + entry = &table->cache[posn]; + + if (entry->data != NULL) { + table->cachecollisions++; + } + table->cacheinserts++; + + entry->f = f; + entry->g = g; + entry->h = (ptruint) op; + entry->data = data; + #ifdef DD_CACHE_PROFILE + entry->count++; + #endif + } + +private: + Cudd cudd; /* the CUDD manager */ + uint32_t num_variables; + ZDD empty; /* the empty family {} (minterms: none; constant 0) */ + ZDD base; /* the unit family {{}} (minterms: the all-zero cube) */ + std::vector elementaries; /* the single-set family of the single-element set {{i}} (minterms: 0...010...0) */ + std::vector tautologies; /* every combinations of variables >= var (minterms: 0...0-...-) */ +}; + +} // namespace cudd \ No newline at end of file diff --git a/lib/bill/bill/dd/zdd.hpp b/lib/bill/bill/dd/zdd.hpp new file mode 100644 index 0000000..763f73f --- /dev/null +++ b/lib/bill/bill/dd/zdd.hpp @@ -0,0 +1,928 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "../utils/hash.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bill { + +/*! \brief A zero-suppressed decision diagram (ZDD). + * + * NOTE: This is a simple implementation. I would advise against its use when high-performance + * is a requirement. + * + * Limitations: + * - The number of variables `N` must be known at instantiation time. + * + * Variables are numbered from `0` to `N - 1`. + */ + +// TODO: Implement complemented edges +// TODO: Implement variable reordering +// TODO: Implement Variable order heuristics +// TODO: Implement Chain reduction +// TODO: Implement subsets operator +// TODO: Implement supersets operator +class zdd_base { +#pragma region Types and constructors +private: + struct node_type { + node_type(uint32_t var, uint32_t lo, uint32_t hi) + : marked(0) + , var(var) + , refs(0) + , lo(lo) + , hi(hi) + {} + + uint32_t marked : 1; + uint32_t var : 31; + int32_t refs; // Number of references - 1 + uint32_t lo; + uint32_t hi; + }; + + enum operations : uint32_t { + zdd_choose, + zdd_difference, + zdd_edivide, + zdd_intersection, + zdd_join, + zdd_maximal, + zdd_meet, + zdd_nonsubsets, + zdd_nonsupersets, + zdd_union, + num_operations + }; + +public: + using node_index = uint32_t; + + /* \!brief Creates a new ZDD base. + * + * \param num_vars Number of variables + * \param log_num_objs Log number of nodes to pre-allocate (default: 16) + */ + explicit zdd_base(uint32_t num_vars, uint32_t log_num_objs = 16) + : unique_tables_(num_vars) + , num_dead_nodes_(0u) + , num_cache_lookups_(0u) + , num_cache_misses_(0u) + { + assert(num_variables() <= 4095); + nodes_.reserve(1u << log_num_objs); + nodes_.emplace_back(num_vars, 0, 0); + nodes_.emplace_back(num_vars, 1, 1); + build_elementary(); + build_tautologies(); + } +#pragma endregion + +#pragma region ZDD base properties +public: + /*! \brief Return the number of active nodes. */ + uint32_t num_nodes() const + { + return nodes_.size() - 2 - num_dead_nodes_ - free_nodes_.size(); + } + + /*! \brief Return the number of active nodes. */ + uint32_t num_variables() const + { + return unique_tables_.size(); + } +#pragma endregion + +#pragma region ZDD base operations +private: + + /* \!brief Returns an unique node for the tuple (var, lo, hi) + * + * Given a variable `var` and node indexes lo and hi, we want to see if the ZDD base + * contains a node (var, lo, hi). If no such node exists, we create it. This function + * returns a index to this _unique_ node. One crucial technicality should be noted: + * + * /!\ This operation can potentially invalidate pointers, iterators and references /!\ + * + * Indexes are not invalidated. + */ + node_index unique(uint32_t var, node_index lo, node_index hi) + { + assert(var < num_variables()); + /* ZDD reduction rule */ + if (hi == bottom()) { + --nodes_.at(hi).refs; + return lo; + } + assert(nodes_.at(lo).var > var); + assert(nodes_.at(hi).var > var); + + /* Unique table lookup */ + const auto it = unique_tables_.at(var).find({lo, hi}); + if (it != unique_tables_.at(var).end()) { + if (nodes_.at(it->second).refs < 0) { + --num_dead_nodes_; + nodes_.at(it->second).refs = 0; + return it->second; + } else { + --nodes_.at(lo).refs; + --nodes_.at(hi).refs; + } + return ref(it->second); + } + + /* Create new node */ + node_index new_node_index; + restart: + if (!free_nodes_.empty()) { + new_node_index = free_nodes_.top(); + free_nodes_.pop(); + nodes_.at(new_node_index).marked = 0; + nodes_.at(new_node_index).var = var; + nodes_.at(new_node_index).refs = 0; + nodes_.at(new_node_index).lo = lo; + nodes_.at(new_node_index).hi = hi; + } else { + if (num_dead_nodes_ > num_nodes() / 8) { + collect_garbage(); + goto restart; + } + new_node_index = nodes_.size(); + nodes_.emplace_back(var, lo, hi); + } + unique_tables_.at(var)[{lo, hi}] = new_node_index; + return new_node_index; + } + + /* \! brief Recursively revives a dead, but unrecycled node + * + * When we discover that a node exists, but it is dead, i.e. all links to it have gone + * away, but we haven’t recycled it yet. We bring it back to life! + * + * It increases the reference counts of the node's children, and resuscitates them if they + * were dead. + */ + void revive_node(node_index index) + { + assert(nodes_.at(index).refs < 0); + restart: + node_type& node = nodes_.at(index); + node.refs = 0; + --num_dead_nodes_; + if (nodes_.at(node.lo).refs < 0) { + revive_node(node.lo); + } else { + ref(node.lo); + } + if (nodes_.at(node.hi).refs < 0) { + index = node.hi; + goto restart; + } + ref(node.hi); + } + + /* \!brief Recursively kills a node + * + * When the reference count of a node reeaches -1, we kill it. It decreases the reference + * counts of the node's children, and kill them too(!) if necessary, i.e. their reference + * count reaches -1. + */ + void kill_node(node_index index) + { + assert(nodes_.at(index).refs == 0); + restart: + node_type& node = nodes_.at(index); + node.refs = -1; + ++num_dead_nodes_; + if (nodes_.at(node.lo).refs == 0) { + kill_node(node.lo); + } else { + --nodes_.at(node.lo).refs; + } + if (nodes_.at(node.hi).refs == 0) { + index = node.hi; + goto restart; + } + --nodes_.at(node.hi).refs; + } + + /* \!brief Return the tautology function */ + node_index tautology(uint32_t var) + { + if (var == num_variables()) { + return top(); + } + return (2 * num_variables()) + 1u - var; + } + + void cache_cleanup() + { + auto check_nodes = [&](node_index a, node_index b, node_index c) -> bool { + if (nodes_.at(a).refs < 0) { + return true; + } + if (nodes_.at(b).refs < 0) { + return true; + } + return nodes_.at(c).refs < 0; + }; + for (auto& table : computed_tables_) { + for (auto it = table.begin(); it != table.end();) { + auto [index_f, index_g] = it->first; + if (check_nodes(it->second, index_f, index_g)) { + it = table.erase(it); + continue; + } else { + ++it; + } + } + } + } + + void tables_cleanup() + { + /* Skip terminals and elementary nodes */ + uint32_t const begin = (2 * num_variables()) + 2; + for (uint32_t index = begin; index < nodes_.size(); ++index) { + node_type& node = nodes_.at(index); + if (node.refs >= 0) { + continue; + } + auto const it = unique_tables_.at(node.var).find({node.lo, node.hi}); + assert(it != unique_tables_.at(node.var).end()); + unique_tables_.at(node.var).erase(it); + free_nodes_.push(index); + node.refs = 0; + } + } + + /*! \brief Creates a node at each level that means "tautology from here on" */ + void build_tautologies() + { + assert(nodes_.size() == num_variables() + 2u); + node_index last = top(); + for (int var = num_variables() - 1; var >= 0; --var) { + ref(last, 2); + last = unique(var, last, last); + assert(last == (2 * num_variables()) + 1u - var); + if (var != 0) { + --nodes_.at(last).refs; + } + } + } + + /*! \brief Create nodes corresponding to the elementary families */ + void build_elementary() + { + for (auto var = 0u; var < num_variables(); ++var) { + unique(var, bottom(), top()); + ref(bottom()); + ref(top()); + }; + --nodes_.at(bottom()).refs; + --nodes_.at(top()).refs; + } + +public: + /*! \brief Returns the node index corresponding to the `empty family` */ + node_index bottom() const + { + return 0u; + } + + /*! \brief Returns the node index corresponding to the `unit family` */ + node_index top() const + { + return 1u; + } + + /*! \brief Returns the node-id corresponding to the elementary family `{{var}}` */ + node_index elementary(uint32_t var) + { + assert(var < num_variables()); + return var + 2u; + } + + /*! \brief Increase the reference count of a node. */ + node_index ref(node_index index, int32_t i = 1) + { + assert(index < nodes_.size()); + nodes_.at(index).refs += i; + return index; + } + + /*! \brief Decrease the reference count of a node. */ + void deref(node_index index) + { + assert(index < nodes_.size()); + assert(nodes_.at(index).refs >= 0); + if (nodes_.at(index).refs == 0) { + kill_node(index); + return; + } + --nodes_.at(index).refs; + } + + /*! \brief Recycle all the dead nodes */ + void collect_garbage() + { + cache_cleanup(); + tables_cleanup(); + num_dead_nodes_ = 0; + } +#pragma endregion + +#pragma region ZDD Operations +public: + /* \!brief Computes the family of all ``k``-combinations of a ZDD. */ + node_index choose(node_index index_f, uint32_t k) + { + constexpr operations op = operations::zdd_choose; + if (index_f <= top()) { + return k > 0 ? bottom() : top(); + } + if (k == 1) { + return ref(index_f); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, k}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_index index_new = choose(node_f.lo, k); + if (k > 0) { + node_index temp = choose(node_f.lo, k - 1); + ref(index_new); + index_new = unique(node_f.var, index_new, temp); + } else { + deref(index_new); + } + computed_tables_.at(op)[{index_f, k}] = index_new; + return index_new; + } + + /* \!brief Computes the difference of two ZDDs (`f - g`) + * Keep in mind that `f - g` is different from `g - f` ! + */ + node_index difference(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_difference; + if (index_f == bottom()) { + return ref(bottom()); + } + node_type& node_f = nodes_.at(index_f); + + restart: + if (index_f == index_g) { + return ref(bottom()); + } + if (index_g == bottom()) { + return ref(index_f); + } + node_type& node_g = nodes_.at(index_g); + if (node_g.var < node_f.var) { + index_g = node_g.lo; + goto restart; + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_index r_lo; + node_index r_hi; + if (node_f.var == node_g.var) { + r_lo = difference(node_f.lo, node_g.lo); + r_hi = difference(node_f.hi, node_g.hi); + } else { + r_lo = difference(node_f.lo, index_g); + r_hi = ref(node_f.hi); + } + node_index index_new = unique(node_f.var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Computes the intersection of two ZDDs */ + node_index intersection(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_intersection; + if (index_f == tautology()) { + return ref(index_g); + } + if (index_g == tautology()) { + return ref(index_f); + } + restart: + if (index_f > index_g) { + std::swap(index_f, index_g); + } + if (index_f == bottom()) { + return ref(bottom()); + } + if (index_f == index_g) { + return ref(index_f); + } + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + if (node_f.var node_g.var) { + index_g = node_g.lo; + goto restart; + } + if (index_f == tautology(node_f.var)) { + return ref(index_g); + } + if (index_g == tautology(node_g.var)) { + return ref(index_f); + } + assert(node_f.var == node_g.var); + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_index r_lo = intersection(node_f.lo, node_g.lo); + node_index r_hi = intersection(node_f.hi, node_g.hi); + node_index index_new = unique(node_f.var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Computes the join of two ZDDs */ + node_index join(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_join; + if (index_f > index_g) { + std::swap(index_f, index_g); + } + if (index_f == bottom()) { + return ref(bottom()); + } + if (index_f == top()) { + return ref(index_g); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + node_index r_lo; + node_index r_hi; + uint32_t var = node_f.var; + if (node_f.var < node_g.var) { + r_lo = join(node_f.lo, index_g); + r_hi = join(node_f.hi, index_g); + } else if (node_f.var > node_g.var) { + r_lo = join(node_g.lo, index_f); + r_hi = join(node_g.hi, index_f); + var = node_g.var; + } else { + // In this case node_f.var == node_g.var + r_lo = union_(node_g.lo, node_g.hi); + node_index const r_hl = join(node_f.hi, r_lo); + deref(r_lo); + node_index const r_lh = join(node_f.lo, node_g.hi); + r_hi = union_(r_hl, r_lh); + deref(r_hl); + deref(r_lh); + r_lo = join(node_f.lo, node_g.lo); + } + node_index index_new = unique(var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Computes the maximal of a ZDD */ + node_index maximal(node_index index_f) + { + constexpr operations op = operations::zdd_maximal; + if (index_f <= top()) { + return ref(index_f); + } + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, 0}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_index r_hi = maximal(node_f.hi); + node_index temp = maximal(node_f.lo); + node_index r_lo = nonsubsets(temp, r_hi); + deref(temp); + node_index index_new = unique(node_f.var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, 0}] = index_new; + return index_new;; + } + + /* \!brief Computes the meet of two ZDDs */ + node_index meet(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_meet; + if (index_f > index_g) { + std::swap(index_f, index_g); + } + if (index_f <= top()) { + return ref(index_f); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + node_index r_lo; + node_index r_hi; + if (node_f.var < node_g.var) { + r_lo = union_(node_f.lo, node_f.hi); + r_hi = meet(r_lo, index_g); + deref(r_lo); + return r_hi; + } else if (node_f.var > node_g.var) { + r_lo = union_(node_g.lo, node_g.hi); + r_hi = meet(r_lo, index_f); + deref(r_lo); + return r_hi; + } else { + // In this case node_f.var == node_g.var + r_hi = union_(node_f.lo, node_f.hi); + node_index r_hl = meet(r_hi, node_g.lo); + deref(r_hi); + node_index r_lh = meet(node_f.lo, node_g.hi); + r_lo = union_(r_hl, r_lh); + deref(r_hl); + deref(r_lh); + r_hi = meet(node_f.hi, node_g.hi); + } + node_index index_new = unique(node_f.var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Computes the nonsubsets of two ZDDs */ + node_index nonsubsets(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_nonsubsets; + if (index_g == bottom()) { + return ref(index_f); + } + if (index_f <= top()) { + return ref(bottom()); + } + if (index_f == index_g) { + return ref(bottom()); + } + + if (nodes_.at(index_f).var > nodes_.at(index_g).var) { + return nonsubsets(index_f, nodes_.at(index_g).lo); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + node_index r_lo; + node_index r_hi; + if (node_f.var < node_g.var) { + r_lo = nonsubsets(node_f.lo, index_g); + r_hi = ref(node_f.hi); + } else { + node_index const temp = nonsubsets(node_f.lo, node_g.hi); + r_hi = nonsubsets(node_f.lo, node_g.lo); + r_lo = intersection(temp, r_hi); + deref(temp); + deref(r_hi); + r_hi = nonsubsets(node_f.hi, node_g.hi); + } + node_index index_new = unique(node_f.var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Computes the nonsupersets of two ZDDs */ + node_index nonsupersets(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_nonsupersets; + if (index_g == bottom()) { + return ref(index_f); + } + if (index_f == bottom()) { + return ref(bottom()); + } + // This operation can be potentially faster if instead I check for top \in g + // TODO: experiment! + if (index_g == top()) { + return ref(bottom()); + } + if (index_f == index_g) { + return ref(bottom()); + } + + if (nodes_.at(index_f).var > nodes_.at(index_g).var) { + return nonsupersets(index_f, nodes_.at(index_g).lo); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_; + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + node_index r_lo; + node_index r_hi; + uint32_t var = node_f.var; + if (node_f.var < node_g.var) { + r_lo = nonsupersets(node_f.lo, index_g); + r_hi = nonsupersets(node_f.hi, index_g); + } else { + r_lo = nonsupersets(node_f.hi, node_g.hi); + node_index temp = nonsupersets(node_f.hi, node_g.lo); + r_hi = intersection(temp, r_lo); + deref(temp); + deref(r_lo); + r_lo = nonsupersets(node_f.lo, node_g.lo); + } + node_index index_new = unique(var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } + + /* \!brief Return the tautology function */ + node_index tautology() + { + return (2 * num_variables()) + 1u; + } + + /* \!brief Computes the union of two ZDDs */ + node_index union_(node_index index_f, node_index index_g) + { + constexpr operations op = operations::zdd_union; + if (index_f == index_g) { + return ref(index_f); + } + if (index_f > index_g) { + std::swap(index_f, index_g); + } + if (index_f == bottom()) { + return ref(index_g); + } + + // Cache lookup + ++num_cache_lookups_; + const auto it = computed_tables_.at(op).find({index_f, index_g}); + if (it != computed_tables_.at(op).end()) { + if (nodes_.at(it->second).refs < 0) { + revive_node(it->second); + return it->second; + } + return ref(it->second); + } + ++num_cache_misses_;; + + node_type node_f = nodes_.at(index_f); + node_type node_g = nodes_.at(index_g); + node_index r_lo; + node_index r_hi; + uint32_t var = node_f.var; + if (node_f.var < node_g.var) { + if (index_f == tautology(node_f.var)) { + return ref(index_f); + } + r_lo = union_(node_f.lo, index_g); + r_hi = ref(node_f.hi); + } else if (node_f.var > node_g.var) { + if (index_g == tautology(node_g.var)) { + return ref(index_g); + } + r_lo = union_(index_f, node_g.lo); + r_hi = ref(node_g.hi); + var = node_g.var; + } else { + // In this case node_f.var == node_g.var + if (index_g == tautology(node_g.var)) { + return ref(index_g); + } + r_lo = union_(node_f.lo, node_g.lo); + r_hi = union_(node_f.hi, node_g.hi); + } + node_index index_new = unique(var, r_lo, r_hi); + computed_tables_.at(op)[{index_f, index_g}] = index_new; + return index_new; + } +#pragma endregion + +#pragma region ZDD iterators +private: + template + bool foreach_set_rec(node_index index, std::vector& set, Fn&& fn) const + { + if (index == 1u) { + return fn(set); + } + if (index != 0u) { + if (!foreach_set_rec(nodes_.at(index).lo, set, fn)) { + return false; + } + auto new_set = set; + new_set.push_back(nodes_.at(index).var); + if (!foreach_set_rec(nodes_.at(index).hi, new_set, fn)) { + return false; + } + } + return true; + } + +public: + template + void foreach_set(node_index index, Fn&& fn) const + { + std::vector set; + foreach_set_rec(index, set, fn); + } +#pragma endregion + +#pragma region ZDD properties +private: + void count_nodes_rec(node_index index, std::unordered_set& visited) const + { + if (index <= 1 || visited.count(index)) { + return; + } + visited.insert(index); + node_type const& node = nodes_.at(index); + count_nodes_rec(node.lo, visited); + count_nodes_rec(node.hi, visited); + } + + uint64_t count_sets_rec(node_index index, std::unordered_map& visited) const + { + if (index <= 1) { + return index; + } + const auto it = visited.find(index); + if (it != visited.end()) { + return it->second; + } + node_type const& node = nodes_.at(index); + return visited[index] = count_sets_rec(node.lo, visited) + + count_sets_rec(node.hi, visited); + } + +public: + /* \!brief Return the number of nodes in a ZDD. */ + uint64_t count_nodes(node_index index_root) const + { + if (index_root <= 1) { + return 0; + } + std::unordered_set visited; + count_nodes_rec(index_root, visited); + return visited.size(); + } + + /* \!brief Return the number of sets in a ZDD. */ + uint64_t count_sets(node_index index_root) const + { + if (index_root <= 1) { + return index_root; + } + std::unordered_map visited; + return count_sets_rec(index_root, visited); + } + + std::vector> sets_as_vectors(node_index index) const + { + std::vector> sets_vectors; + foreach_set(index, [&](auto const& set){ + sets_vectors.emplace_back(set); + return true; + }); + return sets_vectors; + } +#pragma endregion + +#pragma region Debug +public: + void print_debug(std::ostream& os = std::cout) const + { + os << "ZDD nodes:\n"; + os << " i VAR LO HI REF\n"; + uint32_t i = 0u; + for (node_type const& node : nodes_) { + os << fmt::format("{:5} : {:5} {:5} {:5} {:5}\n", i++, node.var, node.lo, + node.hi, node.refs); + } + } + + void print_sets(node_index index, std::ostream& os = std::cout) const + { + foreach_set(index, [&](auto const& set){ + os << fmt::format("{{ {} }}\n", fmt::join(set, ", ")); + return true; + }); + } +#pragma endregion + +private: + using children_type = std::pair; + using unique_table_type = std::unordered_map; + + std::vector nodes_; + std::stack free_nodes_; + std::vector unique_tables_; + std::array computed_tables_; + + // Stats + uint32_t num_dead_nodes_; + uint32_t num_cache_lookups_; + uint32_t num_cache_misses_; +}; + +} // namespace bill diff --git a/lib/bill/bill/sat/cardinality.hpp b/lib/bill/bill/sat/cardinality.hpp new file mode 100644 index 0000000..93eb294 --- /dev/null +++ b/lib/bill/bill/sat/cardinality.hpp @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "interface/types.hpp" + +#include + +namespace bill { + +template +inline void at_least_one(std::vector const& variables, Cnf& cnf_builder) +{ + std::vector clause; + for (auto var : variables) { + clause.emplace_back(var, positive_polarity); + } + cnf_builder.add_clause(clause); +} + +template +inline void at_most_one_pairwise(std::vector const& variables, Cnf& cnf_builder) +{ + std::vector clause; + for (auto i = 0u; i < variables.size() - 1; ++i) { + for (auto j = i + 1u; j < variables.size(); ++j) { + clause.emplace_back(variables[i], negative_polarity); + clause.emplace_back(variables[j], negative_polarity); + cnf_builder.add_clause(clause); + clause.clear(); + } + } +} + +} // namespace bill \ No newline at end of file diff --git a/lib/bill/bill/sat/incremental_totalizer_cardinality.hpp b/lib/bill/bill/sat/incremental_totalizer_cardinality.hpp new file mode 100644 index 0000000..d0e742b --- /dev/null +++ b/lib/bill/bill/sat/incremental_totalizer_cardinality.hpp @@ -0,0 +1,181 @@ +/* + * This implementation is based on the code of Antonio Morgado and + * Alexey S. Ignatiev [1]. See [2] for a seminal reference. + * + * [1] https://github.com/pysathq/pysat/blob/master/cardenc/itot.hh. + * [2] Ruben Martins, Saurabh Joshi, Vasco M. Manquinho, Inês Lynce: + * Incremental Cardinality Constraints for MaxSAT. CP 2014: 531-548 + */ +#pragma once + +#include "interface/types.hpp" + +#include + +namespace bill { + +struct totalizer_tree { + std::vector vars; + uint32_t num_inputs; + std::shared_ptr left; + std::shared_ptr right; +}; /* totalizer_tree */ + +namespace detail { + +template +inline void create_totalizer_internal(Solver& solver, std::vector>& dest, + std::vector const& ov, uint32_t rhs, + std::vector const& av, + std::vector const& bv) +{ + (void) solver; + + /* i = 0 */ + uint32_t kmin = std::min(rhs, uint32_t(bv.size())); + for (auto j = 0u; j < kmin; ++j) { + dest.emplace_back(std::vector{~bv[j], ov[j]}); + } + + /* j = 0 */ + kmin = std::min(rhs, uint32_t(av.size())); + for (auto i = 0u; i < kmin; ++i) { + dest.emplace_back(std::vector{~av[i], ov[i]}); + } + + /* i, j > 0 */ + for (auto i = 1u; i <= kmin; ++i) { + auto const min_j = std::min(rhs - i, uint32_t(bv.size())); + for (auto j = 1u; j <= min_j; ++j) { + dest.emplace_back(std::vector{~av[i - 1], ~bv[j - 1], ov[i + j - 1]}); + } + } +} + +template +inline void increase_totalizer_internal(Solver& solver, std::vector>& dest, + std::vector& ov, uint32_t rhs, + std::vector& av, std::vector& bv) +{ + uint32_t last = ov.size(); + for (auto i = last; i < rhs; ++i) { + ov.emplace_back(lit_type(solver.add_variable(), positive_polarity)); + } + + // add the constraints + /* i = 0 */ + uint32_t const max_j = std::min(rhs, uint32_t(bv.size())); + for (auto j = last; j < max_j; ++j) { + dest.emplace_back(std::vector{~bv[j], ov[j]}); + } + + /* j = 0 */ + uint32_t const max_i = std::min(rhs, uint32_t(av.size())); + for (auto i = last; i < max_i; ++i) { + dest.emplace_back(std::vector{~av[i], ov[i]}); + } + + /* i, j > 0 */ + for (auto i = 1u; i <= max_i; ++i) { + auto const max_j = std::min(rhs - i, uint32_t(bv.size())); + auto const min_j = uint32_t(std::max(int(last) - int(i) + 1, 1)); + for (auto j = min_j; j <= max_j; ++j) { + dest.emplace_back(std::vector{~av[i - 1], ~bv[j - 1], ov[i + j - 1]}); + } + } +} + +} // namespace detail + +template +inline std::shared_ptr create_totalizer(Solver& solver, + std::vector>& dest, + std::vector const& lhs, + uint32_t rhs) +{ + auto const n = lhs.size(); + + std::deque> queue; + for (auto i = 0u; i < n; ++i) { + auto t = std::make_shared(); + t->vars.resize(1, lit_type(lhs[i].variable(), lhs[i].polarity())); + t->num_inputs = 1; + queue.push_back(t); + } + + while (queue.size() > 1) { + auto const le = queue.front(); + queue.pop_front(); + + auto const ri = queue.front(); + queue.pop_front(); + + auto t = std::make_shared(); + t->num_inputs = le->num_inputs + ri->num_inputs; + t->left = le; + t->right = ri; + + uint32_t kmin = std::min(rhs + 1, t->num_inputs); + t->vars.resize(kmin, lit_type(var_type(0), negative_polarity)); + for (auto i = 0u; i < kmin; ++i) { + t->vars[i] = lit_type(solver.add_variable(), positive_polarity); + } + detail::create_totalizer_internal(solver, dest, t->vars, kmin, le->vars, ri->vars); + queue.push_back(t); + } + return queue.front(); +} + +template +inline void increase_totalizer(Solver& solver, std::vector>& dest, + std::shared_ptr& t, uint32_t rhs) +{ + uint32_t const kmin = std::min(rhs + 1, t->num_inputs); + if (kmin <= t->vars.size()) + return; + + increase_totalizer(solver, dest, t->left, rhs); + increase_totalizer(solver, dest, t->right, rhs); + detail::increase_totalizer_internal(solver, dest, t->vars, kmin, t->left->vars, + t->right->vars); +} + +template +inline std::shared_ptr merge_totalizer(Solver& solver, + std::vector>& dest, + std::shared_ptr& ta, + std::shared_ptr& tb, + uint32_t rhs) +{ + increase_totalizer(solver, dest, ta, rhs); + increase_totalizer(solver, dest, tb, rhs); + + uint32_t n = ta->num_inputs + tb->num_inputs; + uint32_t kmin = std::min(rhs, n); + + auto t = std::make_shared(); + t->num_inputs = n; + t->left = ta; + t->right = tb; + + t->vars.resize(kmin, lit_type(var_type(0), negative_polarity)); + for (auto i = 0u; i < kmin; ++i) { + t->vars[i] = lit_type(solver.add_variable(), positive_polarity); + } + + detail::create_totalizer_internal(solver, dest, t->vars, kmin, ta->vars, tb->vars); + return t; +} + +template +inline std::shared_ptr extend_totalizer(Solver& solver, + std::vector>& dest, + std::shared_ptr& ta, + std::vector const& lhs, + uint32_t rhs) +{ + auto tb = create_totalizer(solver, dest, lhs, rhs); + return merge_totalizer(solver, dest, ta, tb, rhs); +} + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/abc_bmcg.hpp b/lib/bill/bill/sat/interface/abc_bmcg.hpp new file mode 100644 index 0000000..73bbcf4 --- /dev/null +++ b/lib/bill/bill/sat/interface/abc_bmcg.hpp @@ -0,0 +1,175 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include + +namespace bill { + +#if !defined(BILL_WINDOWS_PLATFORM) +template<> +class solver { + using solver_type = pabc::bmcg_sat_solver; + +public: +#pragma region Constructors + solver() + { + solver_ = pabc::bmcg_sat_solver_start(); + } + + ~solver() + { + pabc::bmcg_sat_solver_stop(solver_); + solver_ = nullptr; + } + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + pabc::bmcg_sat_solver_reset(solver_); + state_ = result::states::undefined; + variable_counter_ = 0u; + } + + var_type add_variable() + { + variable_counter_++; + return pabc::bmcg_sat_solver_addvar(solver_); + } + + void add_variables(uint32_t num_variables = 1) + { + for (auto i = 0u; i < num_variables; ++i) { + pabc::bmcg_sat_solver_addvar(solver_); + } + variable_counter_ += num_variables; + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + auto counter = 0u; + while (it != ie) { + literals[counter++] = pabc::Abc_Var2Lit(it->variable(), + it->is_complemented()); + ++it; + } + auto const result = pabc::bmcg_sat_solver_addclause(solver_, literals, counter); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(clause.begin(), clause.end()); + } + + auto add_clause(lit_type lit) + { + return add_clause(std::vector{lit}); + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + for (auto i = 0u; i < num_variables(); ++i) { + auto const value = pabc::bmcg_sat_solver_read_cex_varvalue(solver_, i); + if (value == 1) { + model.emplace_back(lbool_type::true_); + } else { + model.emplace_back(lbool_type::false_); + } + } + return result(model); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0) + { + /* special case: empty solver state */ + if (num_variables() == 0u) + return result::states::undefined; + + if (conflict_limit > 0) + pabc::bmcg_sat_solver_set_conflict_budget(solver_, conflict_limit); + + int result; + if (assumptions.size() > 0u) { + /* solve with assumptions */ + uint32_t counter = 0u; + auto it = assumptions.begin(); + while (it != assumptions.end()) { + literals[counter++] = pabc::Abc_Var2Lit(it->variable(), + it->is_complemented()); + ++it; + } + result = pabc::bmcg_sat_solver_solve(solver_, literals, counter); + } else { + /* solve without assumptions */ + result = pabc::bmcg_sat_solver_solve(solver_, 0, 0); + } + + if (result == 1) { + state_ = result::states::satisfiable; + } else if (result == -1) { + state_ = result::states::unsatisfiable; + } else { + state_ = result::states::undefined; + } + + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return variable_counter_; + } + + uint32_t num_clauses() const + { + return pabc::bmcg_sat_solver_clausenum(solver_); + } +#pragma endregion + +private: + /*! \brief Backend solver */ + solver_type* solver_ = nullptr; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::undefined; + + /*! \brief Temporary storage for one clause */ + pabc::lit literals[2048]; + + /*! \brief Count the number of variables */ + uint32_t variable_counter_ = 0u; +}; +#endif + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/abc_bsat2.hpp b/lib/bill/bill/sat/interface/abc_bsat2.hpp new file mode 100644 index 0000000..0a8fe76 --- /dev/null +++ b/lib/bill/bill/sat/interface/abc_bsat2.hpp @@ -0,0 +1,227 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include +#include + +namespace bill { + +template<> +class solver { + using solver_type = pabc::sat_solver; + +public: +#pragma region Constructors + solver() + : variable_counter(1, 0u) + , clause_counter(1, 0) + { + solver_ = pabc::sat_solver_new(); + } + + ~solver() + { + pabc::sat_solver_delete(solver_); + solver_ = nullptr; + } + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + pabc::sat_solver_restart(solver_); + state_ = result::states::undefined; + randomize = false; + variable_counter.clear(); + variable_counter.emplace_back(0u); + clause_counter.clear(); + clause_counter.emplace_back(0); + } + + var_type add_variable() + { + ++variable_counter.back(); + return pabc::sat_solver_addvar(solver_); + } + + void add_variables(uint32_t num_variables = 1) + { + variable_counter.back() += num_variables; + for (auto i = 0u; i < num_variables; ++i) { + pabc::sat_solver_addvar(solver_); + } + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + literals.resize(ie - it); + ++clause_counter.back(); + auto counter = 0u; + while (it != ie) { + literals[counter++] = pabc::Abc_Var2Lit(it->variable(), + it->is_complemented()); + ++it; + } + auto const result = pabc::sat_solver_addclause(solver_, literals.data(), literals.data() + counter); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(clause.begin(), clause.end()); + } + + auto add_clause(lit_type lit) + { + --clause_counter.back(); /* do not count unit clauses */ + return add_clause(std::vector{lit}); + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + for (auto i = 0u; i < num_variables(); ++i) { + auto const value = pabc::sat_solver_var_value(solver_, i); + if (value == 1) { + model.emplace_back(lbool_type::true_); + } else { + model.emplace_back(lbool_type::false_); + } + } + return result(model); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0) + { + /* special case: empty solver state */ + if (num_variables() == 0u) + return result::states::undefined; + + if (randomize) { + std::vector vars; + for (auto i = 0u; i < num_variables(); ++i) { + if (random() % 2) { + vars.push_back(i); + } + } + pabc::sat_solver_set_polarity(solver_, + (int*) (const_cast(vars.data())), + vars.size()); + } + + int result; + if (assumptions.size() > 0u) { + /* solve with assumptions */ + uint32_t counter = 0u; + literals.resize(assumptions.size()); + auto it = assumptions.begin(); + while (it != assumptions.end()) { + literals[counter++] = pabc::Abc_Var2Lit(it->variable(), + it->is_complemented()); + ++it; + } + result = pabc::sat_solver_solve(solver_, literals.data(), literals.data() + counter, + conflict_limit, 0, 0, 0); + } else { + /* solve without assumptions */ + result = pabc::sat_solver_solve(solver_, 0, 0, conflict_limit, 0, 0, 0); + } + + if (result == 1) { + state_ = result::states::satisfiable; + } else if (result == -1) { + state_ = result::states::unsatisfiable; + } else { + state_ = result::states::undefined; + } + + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return variable_counter.back(); + /* Note: `pabc::sat_solver_nvars(solver_)` is not correct when bookmark/rollback is used */ + } + + uint32_t num_clauses() const + { + return clause_counter.back(); + /* Note: `pabc::sat_solver_nclauses(solver_)` is not correct when bookmark/rollback is used */ + } +#pragma endregion + + void push() + { + pabc::sat_solver_bookmark(solver_); + variable_counter.emplace_back(variable_counter.back()); + clause_counter.emplace_back(clause_counter.back()); + } + + void pop(uint32_t num_levels = 1u) + { + assert(num_levels == 1u && "bsat does not support multiple step pop"); + assert(variable_counter.size() >= num_levels); + assert(clause_counter.size() >= num_levels); + pabc::sat_solver_rollback(solver_); + variable_counter.resize(uint32_t(variable_counter.size() - num_levels)); + clause_counter.resize(uint32_t(clause_counter.size() - num_levels)); + } + + void set_random_phase(uint32_t seed = 0u) + { + randomize = true; + pabc::sat_solver_set_random(solver_, 1); + random.seed(seed); + } + +private: + /*! \brief Backend solver */ + solver_type* solver_ = nullptr; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::undefined; + + /*! \brief Temporary storage for one clause */ + std::vector literals; + + /*! \brief Whether to randomize initial variable values */ + bool randomize = false; + std::default_random_engine random; + + /*! \brief Stacked counter for number of variables */ + std::vector variable_counter; + + /*! \brief Stacked counter for number of clauses */ + std::vector clause_counter; +}; + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/common.hpp b/lib/bill/bill/sat/interface/common.hpp new file mode 100644 index 0000000..7f17d15 --- /dev/null +++ b/lib/bill/bill/sat/interface/common.hpp @@ -0,0 +1,160 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "../../utils/platforms.hpp" + +#if defined(BILL_WINDOWS_PLATFORM) +#pragma warning(push) +#pragma warning( \ + disable : 4018 4127 4189 4200 4242 4244 4245 4305 4365 4388 4389 4456 4457 4459 4514 4552 4571 4583 4619 4623 4625 4626 4706 4710 4711 4774 4820 4820 4996 5026 5027 5039) +#include "../solver/ghack.hpp" +#include "../solver/glucose.hpp" +#define ABC_USE_NAMESPACE pabc +#define ABC_NAMESPACE pabc +#define ABC_USE_NO_READLINE +#include "../solver/abc.hpp" +#pragma warning(pop) +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdangling-else" +#pragma GCC diagnostic ignored "-Wreorder" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wunused-comparison" +#pragma GCC diagnostic ignored "-Wunused-label" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-value" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wzero-length-array" +#include "../solver/ghack.hpp" +#include "../solver/glucose.hpp" +#include "../solver/maple.hpp" +#ifndef LIN64 +#define LIN64 +#endif +#define ABC_USE_NAMESPACE pabc +#define ABC_NAMESPACE pabc +#define ABC_USE_NO_READLINE +#include "../solver/abc.hpp" +#pragma GCC diagnostic pop +#endif + +#include "types.hpp" + +#include +#include +#include + +namespace bill { + +class result { +public: + using model_type = std::vector; + using clause_type = std::vector; + + enum class states : uint8_t { + satisfiable, + unsatisfiable, + undefined, + timeout, + dirty, + }; + + static std::string to_string(states const& state) + { + switch (state) { + case states::satisfiable: + return "satisfiable"; + case states::unsatisfiable: + return "unsatisfiable"; + case states::timeout: + return "timeout"; + case states::dirty: + return "dirty"; + case states::undefined: + default: + return "undefined"; + } + } + +#pragma region Constructors + result(states state = states::undefined) + : state_(state) + {} + + result(model_type const& model) + : state_(states::satisfiable) + , data_(model) + {} + + result(clause_type const& unsat_core) + : state_(states::unsatisfiable) + , data_(unsat_core) + {} +#pragma endregion + +#pragma region Properties + inline bool is_satisfiable() const + { + return (state_ == states::satisfiable); + } + + inline bool is_unsatisfiable() const + { + return (state_ == states::unsatisfiable); + } + + inline bool is_undefined() const + { + return (state_ == states::undefined); + } + + inline model_type model() const + { + return std::get(data_); + } + + inline clause_type core() const + { + return std::get(data_); + } +#pragma endregion + +#pragma region Overloads + inline operator bool() const + { + return (state_ == states::satisfiable); + } + + inline explicit operator std::string() const + { + return result::to_string(state_); + } +#pragma endregion + +private: + states state_; + std::variant data_; +}; + +enum class solvers { + glucose_41, + ghack, + bsat2, +#if !defined(BILL_WINDOWS_PLATFORM) + maple, + bmcg, +#endif +#if defined(BILL_HAS_Z3) + z3, +#endif +}; + +/*! \brief Solver interface + */ +template +class solver; + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/ghack.hpp b/lib/bill/bill/sat/interface/ghack.hpp new file mode 100644 index 0000000..0d5b3b7 --- /dev/null +++ b/lib/bill/bill/sat/interface/ghack.hpp @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include + +namespace bill { + +template<> +class solver { + using solver_type = GHack::Solver; + +public: +#pragma region Constructors + solver() + : solver_(std::make_unique()) + {} + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + solver_.reset(); + solver_ = std::make_unique(); + state_ = result::states::undefined; + } + + var_type add_variable() + { + return solver_->newVar(); + } + + void add_variables(uint32_t num_variables = 1) + { + for (auto i = 0u; i < num_variables; ++i) { + solver_->newVar(); + } + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + GHack::vec literals; + while (it != ie) { + literals.push(GHack::mkLit(it->variable(), it->is_complemented())); + ++it; + } + auto const result = solver_->addClause_(literals); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(clause.begin(), clause.end()); + } + + auto add_clause(lit_type lit) + { + auto const result = solver_->addClause( + GHack::mkLit(lit.variable(), lit.is_complemented())); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + for (auto i = 0; i < solver_->model.size(); ++i) { + if (solver_->model[i] == GHack::l_False) { + model.emplace_back(lbool_type::false_); + } else if (solver_->model[i] == GHack::l_True) { + model.emplace_back(lbool_type::true_); + } else { + model.emplace_back(lbool_type::undefined); + } + } + return result(model); + } + + result get_core() const + { + assert(state_ == result::states::unsatisfiable); + result::clause_type unsat_core; + for (auto i = 0; i < solver_->conflict.size(); ++i) { + unsat_core.emplace_back(GHack::var(solver_->conflict[i]), + GHack::sign(solver_->conflict[i]) ? + negative_polarity : + positive_polarity); + } + return result(unsat_core); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else if (state_ == result::states::unsatisfiable) { + return get_core(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0) + { + if (state_ != result::states::dirty && assumptions.empty()) { + return state_; + } + + assert(solver_->okay() == true); + if (conflict_limit) { + solver_->setConfBudget(conflict_limit); + } + + GHack::vec literals; + for (auto lit : assumptions) { + literals.push(GHack::mkLit(lit.variable(), lit.is_complemented())); + } + + GHack::lbool state = solver_->solveLimited(literals); + if (state == GHack::l_True) { + state_ = result::states::satisfiable; + } else if (state == GHack::l_False) { + state_ = result::states::unsatisfiable; + } else { + state_ = result::states::undefined; + } + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return solver_->nVars(); + } + + uint32_t num_clauses() const + { + return solver_->nClauses(); + } +#pragma endregion + +private: + /*! \brief Backend solver */ + std::unique_ptr solver_; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::undefined; +}; + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/glucose.hpp b/lib/bill/bill/sat/interface/glucose.hpp new file mode 100644 index 0000000..0712dd4 --- /dev/null +++ b/lib/bill/bill/sat/interface/glucose.hpp @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include + +namespace bill { + +template<> +class solver { + using solver_type = Glucose::Solver; + +public: +#pragma region Constructors + solver() + : solver_(std::make_unique()) + {} + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + solver_.reset(); + solver_ = std::make_unique(); + state_ = result::states::undefined; + } + + var_type add_variable() + { + return solver_->newVar(); + } + + void add_variables(uint32_t num_variables = 1) + { + for (auto i = 0u; i < num_variables; ++i) { + solver_->newVar(); + } + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + Glucose::vec literals; + while (it != ie) { + literals.push(Glucose::mkLit(it->variable(), it->is_complemented())); + ++it; + } + auto const result = solver_->addClause_(literals); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(clause.begin(), clause.end()); + } + + auto add_clause(lit_type lit) + { + auto const result = solver_->addClause( + Glucose::mkLit(lit.variable(), lit.is_complemented())); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + for (auto i = 0; i < solver_->model.size(); ++i) { + if (solver_->model[i] == Glucose::l_False) { + model.emplace_back(lbool_type::false_); + } else if (solver_->model[i] == Glucose::l_True) { + model.emplace_back(lbool_type::true_); + } else { + model.emplace_back(lbool_type::undefined); + } + } + return result(model); + } + + result get_core() const + { + assert(state_ == result::states::unsatisfiable); + result::clause_type unsat_core; + for (auto i = 0; i < solver_->conflict.size(); ++i) { + unsat_core.emplace_back(Glucose::var(solver_->conflict[i]), + Glucose::sign(solver_->conflict[i]) ? + negative_polarity : + positive_polarity); + } + return result(unsat_core); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else if (state_ == result::states::unsatisfiable) { + return get_core(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0) + { + if (state_ != result::states::dirty && assumptions.empty()) { + return state_; + } + + assert(solver_->okay() == true); + if (conflict_limit) { + solver_->setConfBudget(conflict_limit); + } + + Glucose::vec literals; + for (auto lit : assumptions) { + literals.push(Glucose::mkLit(lit.variable(), lit.is_complemented())); + } + + Glucose::lbool state = solver_->solveLimited(literals); + if (state == Glucose::l_True) { + state_ = result::states::satisfiable; + } else if (state == Glucose::l_False) { + state_ = result::states::unsatisfiable; + } else { + state_ = result::states::undefined; + } + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return solver_->nVars(); + } + + uint32_t num_clauses() const + { + return solver_->nClauses(); + } +#pragma endregion + +private: + /*! \brief Backend solver */ + std::unique_ptr solver_; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::undefined; +}; + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/maple.hpp b/lib/bill/bill/sat/interface/maple.hpp new file mode 100644 index 0000000..08d807f --- /dev/null +++ b/lib/bill/bill/sat/interface/maple.hpp @@ -0,0 +1,169 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include + +namespace bill { + +#if !defined(BILL_WINDOWS_PLATFORM) +template<> +class solver { + using solver_type = Maple::Solver; + +public: +#pragma region Constructors + solver() + : solver_(std::make_unique()) + {} + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + solver_.reset(); + solver_ = std::make_unique(); + state_ = result::states::undefined; + } + + var_type add_variable() + { + return solver_->newVar(); + } + + void add_variables(uint32_t num_variables = 1) + { + for (auto i = 0u; i < num_variables; ++i) { + solver_->newVar(); + } + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + Maple::vec literals; + while (it != ie) { + literals.push(Maple::mkLit(it->variable(), it->is_complemented())); + ++it; + } + auto const result = solver_->addClause_(literals); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(clause.begin(), clause.end()); + } + + auto add_clause(lit_type lit) + { + auto const result = solver_->addClause( + Maple::mkLit(lit.variable(), lit.is_complemented())); + state_ = result ? result::states::dirty : result::states::unsatisfiable; + return result; + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + for (auto i = 0; i < solver_->model.size(); ++i) { + if (solver_->model[i] == Maple::l_False) { + model.emplace_back(lbool_type::false_); + } else if (solver_->model[i] == Maple::l_True) { + model.emplace_back(lbool_type::true_); + } else { + model.emplace_back(lbool_type::undefined); + } + } + return result(model); + } + + result get_core() const + { + assert(state_ == result::states::unsatisfiable); + result::clause_type unsat_core; + for (auto i = 0; i < solver_->conflict.size(); ++i) { + unsat_core.emplace_back(Maple::var(solver_->conflict[i]), + Maple::sign(solver_->conflict[i]) ? + negative_polarity : + positive_polarity); + } + return result(unsat_core); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else if (state_ == result::states::unsatisfiable) { + return get_core(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0) + { + if (state_ != result::states::dirty && assumptions.empty()) { + return state_; + } + + assert(solver_->okay() == true); + if (conflict_limit) { + solver_->setConfBudget(conflict_limit); + } + + Maple::vec literals; + for (auto lit : assumptions) { + literals.push(Maple::mkLit(lit.variable(), lit.is_complemented())); + } + + Maple::lbool state = solver_->solveLimited(literals); + if (state == Maple::l_True) { + state_ = result::states::satisfiable; + } else if (state == Maple::l_False) { + state_ = result::states::unsatisfiable; + } else { + state_ = result::states::undefined; + } + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return solver_->nVars(); + } + + uint32_t num_clauses() const + { + return solver_->nClauses(); + } +#pragma endregion + +private: + /*! \brief Backend solver */ + std::unique_ptr solver_; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::dirty; +}; +#endif + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/types.hpp b/lib/bill/bill/sat/interface/types.hpp new file mode 100644 index 0000000..23ae084 --- /dev/null +++ b/lib/bill/bill/sat/interface/types.hpp @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include +#include +#include + +namespace bill { + +/*! \brief Wrapper class to represent variables. + * + * A variable is an element of a convenient set. They are often identified by symbols such as + * x1, x2, ..., xn; of course, any other symbol can also be used, e.g., a, b, c. In code, however, + * we use unsigned numerals 1, 2, 3, ..., n that stand for variables. + * + * Because of its relation to literals (see below), using `uint32_t` to hold variable identifiers + * limits the number of possibles variables to 2^31 - 1 = 2,147,483,647. + */ +class var_type { + constexpr static uint32_t max_value = (std::numeric_limits::max() >> 1); + +public: + constexpr var_type(uint32_t var = 0) + : data_(var) + { + assert(var < max_value); + } + +#pragma region Overloads + constexpr operator uint32_t() const + { + return data_; + } + + bool operator<(var_type other) const + { + return data_ < other.data_; + } + + bool operator==(var_type other) const + { + return data_ == other.data_; + } + + bool operator!=(var_type other) const + { + return data_ != other.data_; + } +#pragma endregion + +private: + uint32_t data_; +}; + +/*! \brief Wrapper class to represent literals. + * + * A literal is either a variable or the complement of a variable. In other words, if x1 is a + * variable, both x1 and ~x1 are literals. If there are n possible variables in some problem, there + * are 2n possible literals. We call x1 and ~x1 the positive polarity literal and negative polarity + * literal of x1, respectively. + * + * We also use unsigned numerals to represent literals (though we could have used singed numerals + * and use the sign to represent each polarity). When using unsigned numerals, even numerals + * represent positive polarity and odd numerals represent negative polarity. + * + * Using `uint32_t` to hold literals identifiers limits the number of possible literals to + * 2^32 - 1 = 4,294,967,295. + */ +class lit_type { +public: + enum class polarities : bool { + positive = 0, + negative = 1, + }; + + constexpr lit_type(var_type var = {}, polarities polarity = polarities::positive) + : data_((var << 1) | ((polarity == polarities::positive) ? 0 : 1)) + {} + +#pragma region Properties + var_type variable() const + { + return (data_ >> 1); + } + + polarities polarity() const + { + return polarities((data_ & 1) == 1); + } + + bool is_complemented() const + { + return (data_ & 1) == 1; + } +#pragma endregion + +#pragma region Modifiers + void complement() + { + data_ ^= 1; + } +#pragma endregion + +#pragma region Overloads + lit_type operator~() const + { + lit_type complemented(*this); + complemented.data_ ^= 1; + return complemented; + } + + bool operator<(lit_type other) const + { + return data_ < other.data_; + } + + bool operator==(lit_type other) const + { + return data_ == other.data_; + } + + bool operator!=(lit_type other) const + { + return data_ != other.data_; + } +#pragma endregion + +private: + uint32_t data_; +}; + +constexpr auto positive_polarity = lit_type::polarities::positive; +constexpr auto negative_polarity = lit_type::polarities::negative; + +/*! \brief Lifted Boolean wrapper class. + */ +enum class lbool_type : uint8_t { + true_, + false_, + undefined, +}; + +} // namespace bill diff --git a/lib/bill/bill/sat/interface/z3.hpp b/lib/bill/bill/sat/interface/z3.hpp new file mode 100644 index 0000000..15e3061 --- /dev/null +++ b/lib/bill/bill/sat/interface/z3.hpp @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#if defined(BILL_HAS_Z3) + +#include "common.hpp" +#include "types.hpp" + +#include +#include +#include +#include + +namespace bill { + +template<> +class solver { +public: +#pragma region Constructors + solver() + : solver_(ctx_) + , variable_counter_(1, 0u) + , clause_counter_(1, 0u) + {} + + ~solver() + {} + + /* disallow copying */ + solver(solver const&) = delete; + solver& operator=(const solver&) = delete; +#pragma endregion + +#pragma region Modifiers + void restart() + { + solver_.reset(); + vars_.clear(); + variable_counter_.clear(); + variable_counter_.emplace_back(0u); + clause_counter_.clear(); + clause_counter_.emplace_back(0u); + state_ = result::states::undefined; + } + + var_type add_variable() + { + vars_.push_back(ctx_.bool_const(fmt::format("x{}", variable_counter_.back()).c_str())); + return variable_counter_.back()++; + } + + void add_variables(uint32_t num_variables = 1) + { + for (auto i = 0u; i < num_variables; ++i) { + add_variable(); + } + } + + auto add_clause(std::vector::const_iterator it, + std::vector::const_iterator ie) + { + z3::expr_vector vec(ctx_); + while (it != ie) { + vec.push_back(it->is_complemented() ? !vars_[it->variable()] : + vars_[it->variable()]); + ++it; + } + solver_.add(mk_or(vec)); + ++clause_counter_.back(); + return result::states::dirty; + } + + auto add_clause(std::vector const& clause) + { + return add_clause(std::begin(clause), std::end(clause)); + } + + auto add_clause(lit_type lit) + { + solver_.add(lit.is_complemented() ? !vars_[lit.variable()] : vars_[lit.variable()]); + return result::states::dirty; + } + + result get_model() const + { + assert(state_ == result::states::satisfiable); + result::model_type model; + const auto m = solver_.get_model(); + for (auto const& v : vars_) { + model.emplace_back(m.eval(v).is_true() ? lbool_type::true_ : + lbool_type::false_); + } + return result(model); + } + + result get_result() const + { + assert(state_ != result::states::dirty); + if (state_ == result::states::satisfiable) { + return get_model(); + } else { + return result(); + } + } + + result::states solve(std::vector const& assumptions = {}, + uint32_t conflict_limit = 0u) + { + z3::expr_vector vec(ctx_); + for (auto const& lit : assumptions) + vec.push_back(lit.is_complemented() ? !vars_[lit.variable()] : + vars_[lit.variable()]); + solver_.set("sat.max_conflicts", conflict_limit == 0u ? + std::numeric_limits::max() : + conflict_limit); + switch (solver_.check(vec)) { + case z3::sat: + state_ = result::states::satisfiable; + break; + case z3::unsat: + state_ = result::states::unsatisfiable; + break; + case z3::unknown: + default: + state_ = result::states::undefined; + break; + }; + z3::reset_params(); + return state_; + } +#pragma endregion + +#pragma region Properties + uint32_t num_variables() const + { + return variable_counter_.back(); + } + + uint32_t num_clauses() const + { + return clause_counter_.back(); + } +#pragma endregion + + void push() + { + solver_.push(); + variable_counter_.emplace_back(variable_counter_.back()); + clause_counter_.emplace_back(clause_counter_.back()); + } + + void pop(uint32_t num_levels = 1u) + { + assert(num_levels < variable_counter_.size()); + assert(variable_counter_.size() >= num_levels); + assert(clause_counter_.size() >= num_levels); + solver_.pop(num_levels); + variable_counter_.resize(uint32_t(variable_counter_.size() - num_levels)); + clause_counter_.resize(uint32_t(clause_counter_.size() - num_levels)); + if (vars_.size() > variable_counter_.back()) { + vars_.erase(vars_.begin() + variable_counter_.back(), vars_.end()); + } + } + + void set_random_phase(uint32_t seed = 0u) + { + solver_.set("sat.random_seed", seed); + solver_.set("phase_selection", 5u); + } + +private: + /*! \brief Backend solver context object */ + z3::context ctx_; + + /*! \brief Backend solver */ + z3::solver solver_; + + /*! \brief Current state of the solver */ + result::states state_ = result::states::undefined; + + /*! \brief Variables */ + std::vector vars_; + + /*! \brief Stacked counter for number of variables */ + std::vector variable_counter_; + + /*! \brief Stacked counter for number of clauses */ + std::vector clause_counter_; +}; + +} // namespace bill + +#endif diff --git a/lib/bill/bill/sat/solver.hpp b/lib/bill/bill/sat/solver.hpp new file mode 100644 index 0000000..65afa0d --- /dev/null +++ b/lib/bill/bill/sat/solver.hpp @@ -0,0 +1,12 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "interface/abc_bmcg.hpp" +#include "interface/abc_bsat2.hpp" +#include "interface/ghack.hpp" +#include "interface/glucose.hpp" +#include "interface/maple.hpp" +#include "interface/z3.hpp" diff --git a/lib/bill/bill/sat/solver/abc.hpp b/lib/bill/bill/sat/solver/abc.hpp new file mode 100644 index 0000000..f1e9b83 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc.hpp @@ -0,0 +1,5927 @@ +#pragma once + +/*** satStore.cpp ***/ + +/**CFile**************************************************************** + + FileName [satStore.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver.] + + Synopsis [Records the trace of SAT solving in the CNF form.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: satStore.c,v 1.4 2005/09/16 22:55:03 casem Exp $] + +***********************************************************************/ + +#include "abc/satStore.h" +#include +#include +#include +#include + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Fetches memory.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline char* Sto_ManMemoryFetch(Sto_Man_t* p, int nBytes) +{ + char* pMem; + if (p->pChunkLast == NULL || nBytes > p->nChunkSize - p->nChunkUsed) { + pMem = (char*) ABC_ALLOC(char, p->nChunkSize); + *(char**) pMem = p->pChunkLast; + p->pChunkLast = pMem; + p->nChunkUsed = sizeof(char*); + } + pMem = p->pChunkLast + p->nChunkUsed; + p->nChunkUsed += nBytes; + return pMem; +} + +/**Function************************************************************* + + Synopsis [Frees memory manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Sto_ManMemoryStop(Sto_Man_t* p) +{ + char *pMem, *pNext; + if (p->pChunkLast == NULL) + return; + for (pMem = p->pChunkLast; (pNext = *(char**) pMem); pMem = pNext) + ABC_FREE(pMem); + ABC_FREE(pMem); +} + +/**Function************************************************************* + + Synopsis [Reports memory usage in bytes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline int Sto_ManMemoryReport(Sto_Man_t* p) +{ + int Total; + char *pMem, *pNext; + if (p->pChunkLast == NULL) + return 0; + Total = p->nChunkUsed; + for (pMem = p->pChunkLast; (pNext = *(char**) pMem); pMem = pNext) + Total += p->nChunkSize; + return Total; +} + +/**Function************************************************************* + + Synopsis [Allocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline Sto_Man_t* Sto_ManAlloc() +{ + Sto_Man_t* p; + // allocate the manager + p = (Sto_Man_t*) ABC_ALLOC(char, sizeof(Sto_Man_t)); + memset(p, 0, sizeof(Sto_Man_t)); + // memory management + p->nChunkSize = (1 << 16); // use 64K chunks + return p; +} + +/**Function************************************************************* + + Synopsis [Deallocate proof manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Sto_ManFree(Sto_Man_t* p) +{ + Sto_ManMemoryStop(p); + ABC_FREE(p); +} + +/**Function************************************************************* + + Synopsis [Adds one clause to the manager.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline int Sto_ManAddClause(Sto_Man_t* p, lit* pBeg, lit* pEnd) +{ + Sto_Cls_t* pClause; + lit Lit, *i, *j; + int nSize; + + // process the literals + if (pBeg < pEnd) { + // insertion sort + for (i = pBeg + 1; i < pEnd; i++) { + Lit = *i; + for (j = i; j > pBeg && *(j - 1) > Lit; j--) + *j = *(j - 1); + *j = Lit; + } + // make sure there is no duplicated variables + for (i = pBeg + 1; i < pEnd; i++) + if (lit_var(*(i - 1)) == lit_var(*i)) { + printf("The clause contains two literals of the same variable: %d " + "and %d.\n", + *(i - 1), *i); + return 0; + } + // check the largest var size + p->nVars = STO_MAX(p->nVars, lit_var(*(pEnd - 1)) + 1); + } + + // get memory for the clause + nSize = sizeof(Sto_Cls_t) + sizeof(lit) * (pEnd - pBeg); + nSize = (nSize / sizeof(char*) + ((nSize % sizeof(char*)) > 0)) + * sizeof(char*); // added by Saurabh on Sep 3, 2009 + pClause = (Sto_Cls_t*) Sto_ManMemoryFetch(p, nSize); + memset(pClause, 0, sizeof(Sto_Cls_t)); + + // assign the clause + pClause->Id = p->nClauses++; + pClause->nLits = pEnd - pBeg; + memcpy(pClause->pLits, pBeg, sizeof(lit) * (pEnd - pBeg)); + // assert( pClause->pLits[0] >= 0 ); + + // add the clause to the list + if (p->pHead == NULL) + p->pHead = pClause; + if (p->pTail == NULL) + p->pTail = pClause; + else { + p->pTail->pNext = pClause; + p->pTail = pClause; + } + + // add the empty clause + if (pClause->nLits == 0) { + if (p->pEmpty) { + printf("More than one empty clause!\n"); + return 0; + } + p->pEmpty = pClause; + } + return 1; +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as root clauses.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Sto_ManMarkRoots(Sto_Man_t* p) +{ + Sto_Cls_t* pClause; + p->nRoots = 0; + Sto_ManForEachClause(p, pClause) + { + pClause->fRoot = 1; + p->nRoots++; + } +} + +/**Function************************************************************* + + Synopsis [Mark all clauses added so far as clause of A.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Sto_ManMarkClausesA(Sto_Man_t* p) +{ + Sto_Cls_t* pClause; + p->nClausesA = 0; + Sto_ManForEachClause(p, pClause) + { + pClause->fA = 1; + p->nClausesA++; + } +} + +/**Function************************************************************* + + Synopsis [Returns the literal of the last clause.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline int Sto_ManChangeLastClause(Sto_Man_t* p) +{ + Sto_Cls_t *pClause, *pPrev; + pPrev = NULL; + Sto_ManForEachClause(p, pClause) pPrev = pClause; + assert(pPrev != NULL); + assert(pPrev->fA == 1); + assert(pPrev->nLits == 1); + p->nClausesA--; + pPrev->fA = 0; + return pPrev->pLits[0] >> 1; +} + +/**Function************************************************************* + + Synopsis [Writes the stored clauses into a file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Sto_ManDumpClauses(Sto_Man_t* p, char* pFileName) +{ + FILE* pFile; + Sto_Cls_t* pClause; + int i; + // start the file + pFile = fopen(pFileName, "w"); + if (pFile == NULL) { + printf("Error: Cannot open output file (%s).\n", pFileName); + return; + } + // write the data + fprintf(pFile, "p %d %d %d %d\n", p->nVars, p->nClauses, p->nRoots, p->nClausesA); + Sto_ManForEachClause(p, pClause) + { + for (i = 0; i < (int) pClause->nLits; i++) + fprintf(pFile, " %d", lit_print(pClause->pLits[i])); + fprintf(pFile, " 0\n"); + } + // fprintf( pFile, " 0\n" ); + fclose(pFile); +} + +/**Function************************************************************* + + Synopsis [Reads one literal from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline int Sto_ManLoadNumber(FILE* pFile, int* pNumber) +{ + int Char, Number = 0, Sign = 0; + // skip space-like chars + do { + Char = fgetc(pFile); + if (Char == EOF) + return 0; + } while (Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n'); + // read the literal + while (1) { + // get the next character + Char = fgetc(pFile); + if (Char == ' ' || Char == '\t' || Char == '\r' || Char == '\n') + break; + // check that the char is a digit + if ((Char < '0' || Char > '9') && Char != '-') { + printf("Error: Wrong char (%c) in the input file.\n", Char); + return 0; + } + // check if this is a minus + if (Char == '-') + Sign = 1; + else + Number = 10 * Number + Char; + } + // return the number + *pNumber = Sign ? -Number : Number; + return 1; +} + +/**Function************************************************************* + + Synopsis [Reads CNF from file.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline Sto_Man_t* Sto_ManLoadClauses(char* pFileName) +{ + FILE* pFile; + Sto_Man_t* p; + Sto_Cls_t* pClause; + char pBuffer[1024]; + int nLits, nLitsAlloc, Counter, Number; + lit* pLits; + + // start the file + pFile = fopen(pFileName, "r"); + if (pFile == NULL) { + printf("Error: Cannot open input file (%s).\n", pFileName); + return NULL; + } + + // create the manager + p = Sto_ManAlloc(); + + // alloc the array of literals + nLitsAlloc = 1024; + pLits = (lit*) ABC_ALLOC(char, sizeof(lit) * nLitsAlloc); + + // read file header + p->nVars = p->nClauses = p->nRoots = p->nClausesA = 0; + while (fgets(pBuffer, 1024, pFile)) { + if (pBuffer[0] == 'c') + continue; + if (pBuffer[0] == 'p') { + sscanf(pBuffer + 1, "%d %d %d %d", &p->nVars, &p->nClauses, &p->nRoots, + &p->nClausesA); + break; + } + printf("Warning: Skipping line: \"%s\"\n", pBuffer); + } + + // read the clauses + nLits = 0; + while (Sto_ManLoadNumber(pFile, &Number)) { + if (Number == 0) { + int RetValue; + RetValue = Sto_ManAddClause(p, pLits, pLits + nLits); + assert(RetValue); + nLits = 0; + continue; + } + if (nLits == nLitsAlloc) { + nLitsAlloc *= 2; + pLits = ABC_REALLOC(lit, pLits, nLitsAlloc); + } + pLits[nLits++] = lit_read(Number); + } + if (nLits > 0) + printf("Error: The last clause was not saved.\n"); + + // count clauses + Counter = 0; + Sto_ManForEachClause(p, pClause) Counter++; + + // check the number of clauses + if (p->nClauses != Counter) { + printf( + "Error: The actual number of clauses (%d) is different than declared (%d).\n", + Counter, p->nClauses); + Sto_ManFree(p); + return NULL; + } + + ABC_FREE(pLits); + fclose(pFile); + return p; +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END + +/*** satSolver.cpp ***/ + +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#include "abc/satSolver.h" +#include "abc/satStore.h" +#include +#include +#include +#include + +ABC_NAMESPACE_IMPL_START + +#define SAT_USE_ANALYZE_FINAL + +//================================================================================================= +// Debug: + +//#define VERBOSEDEBUG + +/**Function************************************************************* + + Synopsis [Merging two lists of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Abc_MergeSortCostMerge(int* p1Beg, int* p1End, int* p2Beg, int* p2End, int* pOut) +{ + int nEntries = (p1End - p1Beg) + (p2End - p2Beg); + int* pOutBeg = pOut; + while (p1Beg < p1End && p2Beg < p2End) { + if (p1Beg[1] == p2Beg[1]) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++, *pOut++ = *p2Beg++, + *pOut++ = *p2Beg++; + else if (p1Beg[1] < p2Beg[1]) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++; + else // if ( p1Beg[1] > p2Beg[1] ) + *pOut++ = *p2Beg++, *pOut++ = *p2Beg++; + } + while (p1Beg < p1End) + *pOut++ = *p1Beg++, *pOut++ = *p1Beg++; + while (p2Beg < p2End) + *pOut++ = *p2Beg++, *pOut++ = *p2Beg++; + assert(pOut - pOutBeg == nEntries); +} + +/**Function************************************************************* + + Synopsis [Recursive sorting.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void Abc_MergeSortCost_rec(int* pInBeg, int* pInEnd, int* pOutBeg) +{ + int nSize = (pInEnd - pInBeg) / 2; + assert(nSize > 0); + if (nSize == 1) + return; + if (nSize == 2) { + if (pInBeg[1] > pInBeg[3]) { + pInBeg[1] ^= pInBeg[3]; + pInBeg[3] ^= pInBeg[1]; + pInBeg[1] ^= pInBeg[3]; + pInBeg[0] ^= pInBeg[2]; + pInBeg[2] ^= pInBeg[0]; + pInBeg[0] ^= pInBeg[2]; + } + } else if (nSize < 8) { + int temp, i, j, best_i; + for (i = 0; i < nSize - 1; i++) { + best_i = i; + for (j = i + 1; j < nSize; j++) + if (pInBeg[2 * j + 1] < pInBeg[2 * best_i + 1]) + best_i = j; + temp = pInBeg[2 * i]; + pInBeg[2 * i] = pInBeg[2 * best_i]; + pInBeg[2 * best_i] = temp; + temp = pInBeg[2 * i + 1]; + pInBeg[2 * i + 1] = pInBeg[2 * best_i + 1]; + pInBeg[2 * best_i + 1] = temp; + } + } else { + Abc_MergeSortCost_rec(pInBeg, pInBeg + 2 * (nSize / 2), pOutBeg); + Abc_MergeSortCost_rec(pInBeg + 2 * (nSize / 2), pInEnd, pOutBeg + 2 * (nSize / 2)); + Abc_MergeSortCostMerge(pInBeg, pInBeg + 2 * (nSize / 2), pInBeg + 2 * (nSize / 2), + pInEnd, pOutBeg); + memcpy(pInBeg, pOutBeg, sizeof(int) * 2 * nSize); + } +} + +/**Function************************************************************* + + Synopsis [Sorting procedure.] + + Description [Returns permutation for the non-decreasing order of costs.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline int* Abc_MergeSortCost(int* pCosts, int nSize) +{ + int i, *pResult, *pInput, *pOutput; + pResult = (int*) calloc(sizeof(int), nSize); + if (nSize < 2) + return pResult; + pInput = (int*) malloc(sizeof(int) * 2 * nSize); + pOutput = (int*) malloc(sizeof(int) * 2 * nSize); + for (i = 0; i < nSize; i++) + pInput[2 * i] = i, pInput[2 * i + 1] = pCosts[i]; + Abc_MergeSortCost_rec(pInput, pInput + 2 * nSize, pOutput); + for (i = 0; i < nSize; i++) + pResult[i] = pInput[2 * i]; + free(pOutput); + free(pInput); + return pResult; +} + +// For derivation output (verbosity level 2) +#define L_IND "%-*d" +#define L_ind sat_solver_dl(s) * 2 + 2, sat_solver_dl(s) +#define L_LIT "%sx%d" +#define L_lit(p) lit_sign(p) ? "~" : "", (lit_var(p)) + +// Just like 'assert()' but expression will be evaluated in the release version as well. +static inline void check(int expr) +{ + assert(expr); +} + +// static inline void printlits(lit* begin, lit* end) +// { +// int i; +// for (i = 0; i < end - begin; i++) +// printf(L_LIT" ",L_lit(begin[i])); +// } + +//================================================================================================= +// Random numbers: + +// Returns a random float 0 <= x < 1. Seed must never be 0. +static inline double drand(double* seed) +{ + int q; + *seed *= 1389796; + q = (int) (*seed / 2147483647); + *seed -= (double) q * 2147483647; + return *seed / 2147483647; +} + +// Returns a random integer 0 <= x < size. Seed must never be 0. +static inline int irand(double* seed, int size) +{ + return (int) (drand(seed) * size); +} + +//================================================================================================= +// Variable datatype + minor functions: + +static const int var0 = 1; +static const int var1 = 0; +static const int varX = 3; + +struct varinfo_t { + unsigned val : 2; // variable value + unsigned pol : 1; // last polarity + unsigned tag : 1; // conflict analysis tag + unsigned lev : 28; // variable level +}; + +static inline int var_level(sat_solver* s, int v) +{ + return s->levels[v]; +} +static inline int var_value(sat_solver* s, int v) +{ + return s->assigns[v]; +} +static inline int var_polar(sat_solver* s, int v) +{ + return s->polarity[v]; +} + +static inline void var_set_level(sat_solver* s, int v, int lev) +{ + s->levels[v] = lev; +} +static inline void var_set_value(sat_solver* s, int v, int val) +{ + s->assigns[v] = val; +} +static inline void var_set_polar(sat_solver* s, int v, int pol) +{ + s->polarity[v] = pol; +} + +// variable tags +static inline int var_tag(sat_solver* s, int v) +{ + return s->tags[v]; +} +static inline void var_set_tag(sat_solver* s, int v, int tag) +{ + assert(tag > 0 && tag < 16); + if (s->tags[v] == 0) + veci_push(&s->tagged, v); + s->tags[v] = tag; +} +static inline void var_add_tag(sat_solver* s, int v, int tag) +{ + assert(tag > 0 && tag < 16); + if (s->tags[v] == 0) + veci_push(&s->tagged, v); + s->tags[v] |= tag; +} +static inline void solver2_clear_tags(sat_solver* s, int start) +{ + int i, *tagged = veci_begin(&s->tagged); + for (i = start; i < veci_size(&s->tagged); i++) + s->tags[tagged[i]] = 0; + veci_resize(&s->tagged, start); +} + +inline int sat_solver_get_var_value(sat_solver* s, int v) +{ + if (var_value(s, v) == var0) + return l_False; + if (var_value(s, v) == var1) + return l_True; + if (var_value(s, v) == varX) + return l_Undef; + assert(0); + return 0; +} + +//================================================================================================= +// Simple helpers: + +static inline int sat_solver_dl(sat_solver* s) +{ + return veci_size(&s->trail_lim); +} +static inline veci* sat_solver_read_wlist(sat_solver* s, lit l) +{ + return &s->wlists[l]; +} + +//================================================================================================= +// Variable order functions: + +static inline void order_update(sat_solver* s, int v) // updateorder +{ + int* orderpos = s->orderpos; + int* heap = veci_begin(&s->order); + int i = orderpos[v]; + int x = heap[i]; + int parent = (i - 1) / 2; + + assert(s->orderpos[v] != -1); + + while (i != 0 && s->activity[x] > s->activity[heap[parent]]) { + heap[i] = heap[parent]; + orderpos[heap[i]] = i; + i = parent; + parent = (i - 1) / 2; + } + + heap[i] = x; + orderpos[x] = i; +} + +static inline void order_assigned(sat_solver* s, int v) +{} + +static inline void order_unassigned(sat_solver* s, int v) // undoorder +{ + int* orderpos = s->orderpos; + if (orderpos[v] == -1) { + orderpos[v] = veci_size(&s->order); + veci_push(&s->order, v); + order_update(s, v); + // printf( "+%d ", v ); + } +} + +static inline int order_select(sat_solver* s, float random_var_freq) // selectvar +{ + int* heap = veci_begin(&s->order); + int* orderpos = s->orderpos; + // Random decision: + if (drand(&s->random_seed) < random_var_freq) { + int next = irand(&s->random_seed, s->size); + assert(next >= 0 && next < s->size); + if (var_value(s, next) == varX) + return next; + } + // Activity based decision: + while (veci_size(&s->order) > 0) { + int next = heap[0]; + int size = veci_size(&s->order) - 1; + int x = heap[size]; + veci_resize(&s->order, size); + orderpos[next] = -1; + if (size > 0) { + int i = 0; + int child = 1; + while (child < size) { + + if (child + 1 < size + && s->activity[heap[child]] < s->activity[heap[child + 1]]) + child++; + assert(child < size); + if (s->activity[x] >= s->activity[heap[child]]) + break; + + heap[i] = heap[child]; + orderpos[heap[i]] = i; + i = child; + child = 2 * child + 1; + } + heap[i] = x; + orderpos[heap[i]] = i; + } + if (var_value(s, next) == varX) + return next; + } + return var_Undef; +} + +inline void sat_solver_set_var_activity(sat_solver* s, int* pVars, int nVars) +{ + int i; + assert(s->VarActType == 1); + for (i = 0; i < s->size; i++) + s->activity[i] = 0; + s->var_inc = Abc_Dbl2Word(1); + for (i = 0; i < nVars; i++) { + int iVar = pVars ? pVars[i] : i; + s->activity[iVar] = Abc_Dbl2Word(nVars - i); + order_update(s, iVar); + } +} + +//================================================================================================= +// variable activities + +inline void solver_init_activities(sat_solver* s) +{ + // variable activities + if (s->VarActType == 0) { + s->var_inc = (1 << 5); + s->var_decay = -1; + } else if (s->VarActType == 1) { + s->var_inc = Abc_Dbl2Word(1.0); + s->var_decay = Abc_Dbl2Word(1.0 / 0.95); + } else if (s->VarActType == 2) { + s->var_inc = Xdbl_FromDouble(1.0); + s->var_decay = Xdbl_FromDouble(1.0 / 0.950); + } else + assert(0); + + // clause activities + if (s->ClaActType == 0) { + s->cla_inc = (1 << 11); + s->cla_decay = -1; + } else { + s->cla_inc = 1; + s->cla_decay = (float) (1 / 0.999); + } +} + +static inline void act_var_rescale(sat_solver* s) +{ + if (s->VarActType == 0) { + word* activity = s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] >>= 19; + s->var_inc >>= 19; + s->var_inc = Abc_MaxInt((unsigned) s->var_inc, (1 << 4)); + } else if (s->VarActType == 1) { + double* activity = (double*) s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] *= 1e-100; + s->var_inc = Abc_Dbl2Word(Abc_Word2Dbl(s->var_inc) * 1e-100); + // printf( "Rescaling var activity...\n" ); + } else if (s->VarActType == 2) { + xdbl* activity = s->activity; + int i; + for (i = 0; i < s->size; i++) + activity[i] = Xdbl_Div(activity[i], 200); // activity[i] / 2^200 + s->var_inc = Xdbl_Div(s->var_inc, 200); + } else + assert(0); +} +static inline void act_var_bump(sat_solver* s, int v) +{ + if (s->VarActType == 0) { + s->activity[v] += s->var_inc; + if ((unsigned) s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 1) { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc); + s->activity[v] = Abc_Dbl2Word(act); + if (act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 2) { + s->activity[v] = Xdbl_Add(s->activity[v], s->var_inc); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else + assert(0); +} +static inline void act_var_bump_global(sat_solver* s, int v) +{ + if (!s->pGlobalVars || !s->pGlobalVars[v]) + return; + if (s->VarActType == 0) { + s->activity[v] += (int) ((unsigned) s->var_inc * 3); + if (s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 1) { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc) * 3.0; + s->activity[v] = Abc_Dbl2Word(act); + if (act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 2) { + s->activity[v] = Xdbl_Add(s->activity[v], Xdbl_Mul(s->var_inc, Xdbl_FromDouble(3.0))); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else + assert(0); +} +static inline void act_var_bump_factor(sat_solver* s, int v) +{ + if (!s->factors) + return; + if (s->VarActType == 0) { + s->activity[v] += (int) ((unsigned) s->var_inc * (float) s->factors[v]); + if (s->activity[v] & 0x80000000) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 1) { + double act = Abc_Word2Dbl(s->activity[v]) + Abc_Word2Dbl(s->var_inc) * s->factors[v]; + s->activity[v] = Abc_Dbl2Word(act); + if (act > 1e100) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else if (s->VarActType == 2) { + s->activity[v] = Xdbl_Add(s->activity[v], + Xdbl_Mul(s->var_inc, Xdbl_FromDouble(s->factors[v]))); + if (s->activity[v] > ABC_CONST(0x014c924d692ca61b)) + act_var_rescale(s); + if (s->orderpos[v] != -1) + order_update(s, v); + } else + assert(0); +} + +static inline void act_var_decay(sat_solver* s) +{ + if (s->VarActType == 0) + s->var_inc += (s->var_inc >> 4); + else if (s->VarActType == 1) + s->var_inc = Abc_Dbl2Word(Abc_Word2Dbl(s->var_inc) * Abc_Word2Dbl(s->var_decay)); + else if (s->VarActType == 2) + s->var_inc = Xdbl_Mul(s->var_inc, s->var_decay); + else + assert(0); +} + +// clause activities +static inline void act_clause_rescale(sat_solver* s) +{ + if (s->ClaActType == 0) { + unsigned* activity = (unsigned*) veci_begin(&s->act_clas); + int i; + for (i = 0; i < veci_size(&s->act_clas); i++) + activity[i] >>= 14; + s->cla_inc >>= 14; + s->cla_inc = Abc_MaxInt(s->cla_inc, (1 << 10)); + } else { + float* activity = (float*) veci_begin(&s->act_clas); + int i; + for (i = 0; i < veci_size(&s->act_clas); i++) + activity[i] *= (float) 1e-20; + s->cla_inc *= (float) 1e-20; + } +} +static inline void act_clause_bump(sat_solver* s, clause* c) +{ + if (s->ClaActType == 0) { + unsigned* act = (unsigned*) veci_begin(&s->act_clas) + c->lits[c->size]; + *act += s->cla_inc; + if (*act & 0x80000000) + act_clause_rescale(s); + } else { + float* act = (float*) veci_begin(&s->act_clas) + c->lits[c->size]; + *act += s->cla_inc; + if (*act > 1e20) + act_clause_rescale(s); + } +} +static inline void act_clause_decay(sat_solver* s) +{ + if (s->ClaActType == 0) + s->cla_inc += (s->cla_inc >> 10); + else + s->cla_inc *= s->cla_decay; +} + +//================================================================================================= +// Sorting functions (sigh): + +static inline void selectionsort(void** array, int size, int (*comp)(const void*, const void*)) +{ + int i, j, best_i; + void* tmp; + + for (i = 0; i < size - 1; i++) { + best_i = i; + for (j = i + 1; j < size; j++) { + if (comp(array[j], array[best_i]) < 0) + best_i = j; + } + tmp = array[i]; + array[i] = array[best_i]; + array[best_i] = tmp; + } +} + +static inline void sortrnd(void** array, int size, int (*comp)(const void*, const void*), double* seed) +{ + if (size <= 15) + selectionsort(array, size, comp); + + else { + void* pivot = array[irand(seed, size)]; + void* tmp; + int i = -1; + int j = size; + + for (;;) { + do + i++; + while (comp(array[i], pivot) < 0); + do + j--; + while (comp(pivot, array[j]) < 0); + + if (i >= j) + break; + + tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + + sortrnd(array, i, comp, seed); + sortrnd(&array[i], size - i, comp, seed); + } +} + +//================================================================================================= +// Clause functions: + +static inline int sat_clause_compute_lbd(sat_solver* s, clause* c) +{ + int i, lev, minl = 0, lbd = 0; + for (i = 0; i < (int) c->size; i++) { + lev = var_level(s, lit_var(c->lits[i])); + if (!(minl & (1 << (lev & 31)))) { + minl |= 1 << (lev & 31); + lbd++; + // printf( "%d ", lev ); + } + } + // printf( " -> %d\n", lbd ); + return lbd; +} + +/* pre: size > 1 && no variable occurs twice + */ +inline int sat_solver_clause_new(sat_solver* s, lit* begin, lit* end, int learnt) +{ + int fUseBinaryClauses = 1; + int size; + clause* c; + int h; + + assert(end - begin > 1); + assert(learnt >= 0 && learnt < 2); + size = end - begin; + + // do not allocate memory for the two-literal problem clause + if (fUseBinaryClauses && size == 2 && !learnt) { + veci_push(sat_solver_read_wlist(s, lit_neg(begin[0])), (clause_from_lit(begin[1]))); + veci_push(sat_solver_read_wlist(s, lit_neg(begin[1])), (clause_from_lit(begin[0]))); + s->stats.clauses++; + s->stats.clauses_literals += size; + return 0; + } + + // create new clause + // h = Vec_SetAppend( &s->Mem, NULL, size + learnt + 1 + 1 ) << 1; + h = Sat_MemAppend(&s->Mem, begin, size, learnt, 0); + assert(!(h & 1)); + if (s->hLearnts == -1 && learnt) + s->hLearnts = h; + if (learnt) { + c = clause_read(s, h); + c->lbd = sat_clause_compute_lbd(s, c); + assert(clause_id(c) == veci_size(&s->act_clas)); + // veci_push(&s->learned, h); + // act_clause_bump(s,clause_read(s, h)); + if (s->ClaActType == 0) + veci_push(&s->act_clas, (1 << 10)); + else + veci_push(&s->act_clas, s->cla_inc); + s->stats.learnts++; + s->stats.learnts_literals += size; + } else { + s->stats.clauses++; + s->stats.clauses_literals += size; + } + + assert(begin[0] >= 0); + assert(begin[0] < s->size * 2); + assert(begin[1] >= 0); + assert(begin[1] < s->size * 2); + + assert(lit_neg(begin[0]) < s->size * 2); + assert(lit_neg(begin[1]) < s->size * 2); + + // veci_push(sat_solver_read_wlist(s,lit_neg(begin[0])),c); + // veci_push(sat_solver_read_wlist(s,lit_neg(begin[1])),c); + veci_push(sat_solver_read_wlist(s, lit_neg(begin[0])), + (size > 2 ? h : clause_from_lit(begin[1]))); + veci_push(sat_solver_read_wlist(s, lit_neg(begin[1])), + (size > 2 ? h : clause_from_lit(begin[0]))); + + return h; +} + +//================================================================================================= +// Minor (solver) functions: + +static inline int sat_solver_enqueue(sat_solver* s, lit l, int from) +{ + int v = lit_var(l); + if (s->pFreqs[v] == 0) + // { + s->pFreqs[v] = 1; + // s->nVarUsed++; + // } + +#ifdef VERBOSEDEBUG + printf(L_IND "enqueue(" L_LIT ")\n", L_ind, L_lit(l)); +#endif + if (var_value(s, v) != varX) + return var_value(s, v) == lit_sign(l); + else { + /* + if ( s->pCnfFunc ) + { + if ( lit_sign(l) ) + { + if ( (s->loads[v] & 1) == 0 ) + { + s->loads[v] ^= 1; + s->pCnfFunc( s->pCnfMan, l ); + } + } + else + { + if ( (s->loads[v] & 2) == 0 ) + { + s->loads[v] ^= 2; + s->pCnfFunc( s->pCnfMan, l ); + } + } + } + */ + // New fact -- store it. +#ifdef VERBOSEDEBUG + printf(L_IND "bind(" L_LIT ")\n", L_ind, L_lit(l)); +#endif + var_set_value(s, v, lit_sign(l)); + var_set_level(s, v, sat_solver_dl(s)); + s->reasons[v] = from; + s->trail[s->qtail++] = l; + order_assigned(s, v); + return true; + } +} + +static inline int sat_solver_decision(sat_solver* s, lit l) +{ + assert(s->qtail == s->qhead); + assert(var_value(s, lit_var(l)) == varX); +#ifdef VERBOSEDEBUG + printf(L_IND "assume(" L_LIT ") ", L_ind, L_lit(l)); + printf("act = %.20f\n", s->activity[lit_var(l)]); +#endif + veci_push(&s->trail_lim, s->qtail); + return sat_solver_enqueue(s, l, 0); +} + +static void sat_solver_canceluntil(sat_solver* s, int level) +{ + int bound; + int lastLev; + int c; + + if (sat_solver_dl(s) <= level) + return; + + assert(veci_size(&s->trail_lim) > 0); + bound = (veci_begin(&s->trail_lim))[level]; + lastLev = (veci_begin(&s->trail_lim))[veci_size(&s->trail_lim) - 1]; + + //////////////////////////////////////// + // added to cancel all assignments + // if ( level == -1 ) + // bound = 0; + //////////////////////////////////////// + + for (c = s->qtail - 1; c >= bound; c--) { + int x = lit_var(s->trail[c]); + var_set_value(s, x, varX); + s->reasons[x] = 0; + if (c < lastLev) + var_set_polar(s, x, !lit_sign(s->trail[c])); + } + // printf( "\n" ); + + for (c = s->qhead - 1; c >= bound; c--) + order_unassigned(s, lit_var(s->trail[c])); + + s->qhead = s->qtail = bound; + veci_resize(&s->trail_lim, level); +} + +static void sat_solver_canceluntil_rollback(sat_solver* s, int NewBound) +{ + int c, x; + + assert(sat_solver_dl(s) == 0); + assert(s->qtail == s->qhead); + assert(s->qtail >= NewBound); + + for (c = s->qtail - 1; c >= NewBound; c--) { + x = lit_var(s->trail[c]); + var_set_value(s, x, varX); + s->reasons[x] = 0; + } + + for (c = s->qhead - 1; c >= NewBound; c--) + order_unassigned(s, lit_var(s->trail[c])); + + s->qhead = s->qtail = NewBound; +} + +static void sat_solver_record(sat_solver* s, veci* cls) +{ + lit* begin = veci_begin(cls); + lit* end = begin + veci_size(cls); + int h = (veci_size(cls) > 1) ? sat_solver_clause_new(s, begin, end, 1) : 0; + sat_solver_enqueue(s, *begin, h); + assert(veci_size(cls) > 0); + if (h == 0) + veci_push(&s->unit_lits, *begin); + + /////////////////////////////////// + // add clause to internal storage + if (s->pStore) { + int RetValue = Sto_ManAddClause((Sto_Man_t*) s->pStore, begin, end); + assert(RetValue); + (void) RetValue; + } + /////////////////////////////////// + /* + if (h != 0) { + act_clause_bump(s,clause_read(s, h)); + s->stats.learnts++; + s->stats.learnts_literals += veci_size(cls); + } + */ +} + +inline int sat_solver_count_assigned(sat_solver* s) +{ + // count top-level assignments + int i, Count = 0; + assert(sat_solver_dl(s) == 0); + for (i = 0; i < s->size; i++) + if (var_value(s, i) != varX) + Count++; + return Count; +} + +static double sat_solver_progress(sat_solver* s) +{ + int i; + double progress = 0; + double F = 1.0 / s->size; + for (i = 0; i < s->size; i++) + if (var_value(s, i) != varX) + progress += pow(F, var_level(s, i)); + return progress / s->size; +} + +//================================================================================================= +// Major methods: + +static int sat_solver_lit_removable(sat_solver* s, int x, int minl) +{ + int top = veci_size(&s->tagged); + + assert(s->reasons[x] != 0); + veci_resize(&s->stack, 0); + veci_push(&s->stack, x); + + while (veci_size(&s->stack)) { + int v = veci_pop(&s->stack); + assert(s->reasons[v] != 0); + if (clause_is_lit(s->reasons[v])) { + v = lit_var(clause_read_lit(s->reasons[v])); + if (!var_tag(s, v) && var_level(s, v)) { + if (s->reasons[v] != 0 && ((1 << (var_level(s, v) & 31)) & minl)) { + veci_push(&s->stack, v); + var_set_tag(s, v, 1); + } else { + solver2_clear_tags(s, top); + return 0; + } + } + } else { + clause* c = clause_read(s, s->reasons[v]); + lit* lits = clause_begin(c); + int i; + for (i = 1; i < clause_size(c); i++) { + int v = lit_var(lits[i]); + if (!var_tag(s, v) && var_level(s, v)) { + if (s->reasons[v] != 0 + && ((1 << (var_level(s, v) & 31)) & minl)) { + veci_push(&s->stack, lit_var(lits[i])); + var_set_tag(s, v, 1); + } else { + solver2_clear_tags(s, top); + return 0; + } + } + } + } + } + return 1; +} + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +/* +void Solver::analyzeFinal(Clause* confl, bool skip_first) +{ + // -- NOTE! This code is relatively untested. Please report bugs! + conflict.clear(); + if (root_level == 0) return; + + vec& seen = analyze_seen; + for (int i = skip_first ? 1 : 0; i < confl->size(); i++){ + Var x = var((*confl)[i]); + if (level[x] > 0) + seen[x] = 1; + } + + int start = (root_level >= trail_lim.size()) ? trail.size()-1 : trail_lim[root_level]; + for (int i = start; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + GClause r = reason[x]; + if (r == GClause_NULL){ + assert(level[x] > 0); + conflict.push(~trail[i]); + }else{ + if (r.isLit()){ + Lit p = r.lit(); + if (level[var(p)] > 0) + seen[var(p)] = 1; + }else{ + Clause& c = *r.clause(); + for (int j = 1; j < c.size(); j++) + if (level[var(c[j])] > 0) + seen[var(c[j])] = 1; + } + } + seen[x] = 0; + } + } +} +*/ + +#ifdef SAT_USE_ANALYZE_FINAL + +static void sat_solver_analyze_final(sat_solver* s, int hConf, int skip_first) +{ + clause* conf = clause_read(s, hConf); + int i, j, start; + veci_resize(&s->conf_final, 0); + if (s->root_level == 0) + return; + assert(veci_size(&s->tagged) == 0); + // assert( s->tags[lit_var(p)] == l_Undef ); + // s->tags[lit_var(p)] = l_True; + for (i = skip_first ? 1 : 0; i < clause_size(conf); i++) { + int x = lit_var(clause_begin(conf)[i]); + if (var_level(s, x) > 0) + var_set_tag(s, x, 1); + } + + start = (s->root_level >= veci_size(&s->trail_lim)) ? + s->qtail - 1 : + (veci_begin(&s->trail_lim))[s->root_level]; + for (i = start; i >= (veci_begin(&s->trail_lim))[0]; i--) { + int x = lit_var(s->trail[i]); + if (var_tag(s, x)) { + if (s->reasons[x] == 0) { + assert(var_level(s, x) > 0); + veci_push(&s->conf_final, lit_neg(s->trail[i])); + } else { + if (clause_is_lit(s->reasons[x])) { + lit q = clause_read_lit(s->reasons[x]); + assert(lit_var(q) >= 0 && lit_var(q) < s->size); + if (var_level(s, lit_var(q)) > 0) + var_set_tag(s, lit_var(q), 1); + } else { + clause* c = clause_read(s, s->reasons[x]); + int* lits = clause_begin(c); + for (j = 1; j < clause_size(c); j++) + if (var_level(s, lit_var(lits[j])) > 0) + var_set_tag(s, lit_var(lits[j]), 1); + } + } + } + } + solver2_clear_tags(s, 0); +} + +#endif + +static void sat_solver_analyze(sat_solver* s, int h, veci* learnt) +{ + lit* trail = s->trail; + int cnt = 0; + lit p = lit_Undef; + int ind = s->qtail - 1; + lit* lits; + int i, j, minl; + veci_push(learnt, lit_Undef); + do { + assert(h != 0); + if (clause_is_lit(h)) { + int x = lit_var(clause_read_lit(h)); + if (var_tag(s, x) == 0 && var_level(s, x) > 0) { + var_set_tag(s, x, 1); + act_var_bump(s, x); + if (var_level(s, x) == sat_solver_dl(s)) + cnt++; + else + veci_push(learnt, clause_read_lit(h)); + } + } else { + clause* c = clause_read(s, h); + + if (clause_learnt(c)) + act_clause_bump(s, c); + lits = clause_begin(c); + // printlits(lits,lits+clause_size(c)); printf("\n"); + for (j = (p == lit_Undef ? 0 : 1); j < clause_size(c); j++) { + int x = lit_var(lits[j]); + if (var_tag(s, x) == 0 && var_level(s, x) > 0) { + var_set_tag(s, x, 1); + act_var_bump(s, x); + // bump variables propaged by the LBD=2 clause + // if ( s->reasons[x] && clause_read(s, + // s->reasons[x])->lbd <= 2 ) + // act_var_bump(s,x); + if (var_level(s, x) == sat_solver_dl(s)) + cnt++; + else + veci_push(learnt, lits[j]); + } + } + } + + while (!var_tag(s, lit_var(trail[ind--]))) + ; + + p = trail[ind + 1]; + h = s->reasons[lit_var(p)]; + cnt--; + + } while (cnt > 0); + + *veci_begin(learnt) = lit_neg(p); + + lits = veci_begin(learnt); + minl = 0; + for (i = 1; i < veci_size(learnt); i++) { + int lev = var_level(s, lit_var(lits[i])); + minl |= 1 << (lev & 31); + } + + // simplify (full) + for (i = j = 1; i < veci_size(learnt); i++) { + if (s->reasons[lit_var(lits[i])] == 0 + || !sat_solver_lit_removable(s, lit_var(lits[i]), minl)) + lits[j++] = lits[i]; + } + + // update size of learnt + statistics + veci_resize(learnt, j); + s->stats.tot_literals += j; + + // clear tags + solver2_clear_tags(s, 0); + +#ifdef DEBUG + for (i = 0; i < s->size; i++) + assert(!var_tag(s, i)); +#endif + +#ifdef VERBOSEDEBUG + printf(L_IND "Learnt {", L_ind); + for (i = 0; i < veci_size(learnt); i++) + printf(" " L_LIT, L_lit(lits[i])); +#endif + if (veci_size(learnt) > 1) { + int max_i = 1; + int max = var_level(s, lit_var(lits[1])); + lit tmp; + + for (i = 2; i < veci_size(learnt); i++) + if (var_level(s, lit_var(lits[i])) > max) { + max = var_level(s, lit_var(lits[i])); + max_i = i; + } + + tmp = lits[1]; + lits[1] = lits[max_i]; + lits[max_i] = tmp; + } +#ifdef VERBOSEDEBUG + { + int lev = veci_size(learnt) > 1 ? var_level(s, lit_var(lits[1])) : 0; + printf(" } at level %d\n", lev); + } +#endif +} + +//#define TEST_CNF_LOAD + +inline int sat_solver_propagate(sat_solver* s) +{ + int hConfl = 0; + lit* lits; + lit false_lit; + + // printf("sat_solver_propagate\n"); + while (hConfl == 0 && s->qtail - s->qhead > 0) { + lit p = s->trail[s->qhead++]; + +#ifdef TEST_CNF_LOAD + int v = lit_var(p); + if (s->pCnfFunc) { + if (lit_sign(p)) { + if ((s->loads[v] & 1) == 0) { + s->loads[v] ^= 1; + s->pCnfFunc(s->pCnfMan, p); + } + } else { + if ((s->loads[v] & 2) == 0) { + s->loads[v] ^= 2; + s->pCnfFunc(s->pCnfMan, p); + } + } + } + { +#endif + + veci* ws = sat_solver_read_wlist(s, p); + int* begin = veci_begin(ws); + int* end = begin + veci_size(ws); + int *i, *j; + + s->stats.propagations++; + // s->simpdb_props--; + + // printf("checking lit %d: "L_LIT"\n", veci_size(ws), L_lit(p)); + for (i = j = begin; i < end;) { + if (clause_is_lit(*i)) { + + int Lit = clause_read_lit(*i); + if (var_value(s, lit_var(Lit)) == lit_sign(Lit)) { + *j++ = *i++; + continue; + } + + *j++ = *i; + if (!sat_solver_enqueue(s, clause_read_lit(*i), + clause_from_lit(p))) { + hConfl = s->hBinary; + (clause_begin(s->binary))[1] = lit_neg(p); + (clause_begin(s->binary))[0] = clause_read_lit(*i++); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + } else { + + clause* c = clause_read(s, *i); + lits = clause_begin(c); + + // Make sure the false literal is data[1]: + false_lit = lit_neg(p); + if (lits[0] == false_lit) { + lits[0] = lits[1]; + lits[1] = false_lit; + } + assert(lits[1] == false_lit); + + // If 0th watch is true, then clause is already satisfied. + if (var_value(s, lit_var(lits[0])) == lit_sign(lits[0])) + *j++ = *i; + else { + // Look for new watch: + lit* stop = lits + clause_size(c); + lit* k; + for (k = lits + 2; k < stop; k++) { + if (var_value(s, lit_var(*k)) + != !lit_sign(*k)) { + lits[1] = *k; + *k = false_lit; + veci_push(sat_solver_read_wlist( + s, lit_neg(lits[1])), + *i); + goto next; + } + } + + *j++ = *i; + // Clause is unit under assignment: + if (c->lrn) + c->lbd = sat_clause_compute_lbd(s, c); + if (!sat_solver_enqueue(s, lits[0], *i)) { + hConfl = *i++; + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + } + } + next: + i++; + } + + s->stats.inspects += j - veci_begin(ws); + veci_resize(ws, j - veci_begin(ws)); +#ifdef TEST_CNF_LOAD + } +#endif + } + + return hConfl; +} + +//================================================================================================= +// External solver functions: + +inline sat_solver* sat_solver_new(void) +{ + sat_solver* s = (sat_solver*) ABC_CALLOC(char, sizeof(sat_solver)); + + // Vec_SetAlloc_(&s->Mem, 15); + Sat_MemAlloc_(&s->Mem, 17); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend(&s->Mem, NULL, 2, 0, 0); + s->binary = clause_read(s, s->hBinary); + + s->nLearntStart = LEARNT_MAX_START_DEFAULT; // starting learned clause limit + s->nLearntDelta = LEARNT_MAX_INCRE_DEFAULT; // delta of learned clause limit + s->nLearntRatio = LEARNT_MAX_RATIO_DEFAULT; // ratio of learned clause limit + s->nLearntMax = s->nLearntStart; + + // initialize vectors + veci_new(&s->order); + veci_new(&s->trail_lim); + veci_new(&s->tagged); + // veci_new(&s->learned); + veci_new(&s->act_clas); + veci_new(&s->stack); + // veci_new(&s->model); + veci_new(&s->unit_lits); + veci_new(&s->temp_clause); + veci_new(&s->conf_final); + + // initialize arrays + s->wlists = 0; + s->activity = 0; + s->orderpos = 0; + s->reasons = 0; + s->trail = 0; + + // initialize other vars + s->size = 0; + s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_new(&s->act_vars); + + s->root_level = 0; + // s->simpdb_assigns = 0; + // s->simpdb_props = 0; + s->progress_estimate = 0; + // s->binary = (clause*)ABC_ALLOC( char, sizeof(clause) + sizeof(lit)*2); + // s->binary->size_learnt = (2 << 1); + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + return s; +} + +inline sat_solver* zsat_solver_new_seed(double seed) +{ + sat_solver* s = (sat_solver*) ABC_CALLOC(char, sizeof(sat_solver)); + + // Vec_SetAlloc_(&s->Mem, 15); + Sat_MemAlloc_(&s->Mem, 15); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend(&s->Mem, NULL, 2, 0, 0); + s->binary = clause_read(s, s->hBinary); + + s->nLearntStart = LEARNT_MAX_START_DEFAULT; // starting learned clause limit + s->nLearntDelta = LEARNT_MAX_INCRE_DEFAULT; // delta of learned clause limit + s->nLearntRatio = LEARNT_MAX_RATIO_DEFAULT; // ratio of learned clause limit + s->nLearntMax = s->nLearntStart; + + // initialize vectors + veci_new(&s->order); + veci_new(&s->trail_lim); + veci_new(&s->tagged); + // veci_new(&s->learned); + veci_new(&s->act_clas); + veci_new(&s->stack); + // veci_new(&s->model); + veci_new(&s->unit_lits); + veci_new(&s->temp_clause); + veci_new(&s->conf_final); + + // initialize arrays + s->wlists = 0; + s->activity = 0; + s->orderpos = 0; + s->reasons = 0; + s->trail = 0; + + // initialize other vars + s->size = 0; + s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_new(&s->act_vars); + + s->root_level = 0; + // s->simpdb_assigns = 0; + // s->simpdb_props = 0; + s->random_seed = seed; + s->progress_estimate = 0; + // s->binary = (clause*)ABC_ALLOC( char, sizeof(clause) + sizeof(lit)*2); + // s->binary->size_learnt = (2 << 1); + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + return s; +} + +inline int sat_solver_addvar(sat_solver* s) +{ + sat_solver_setnvars(s, s->size + 1); + return s->size - 1; +} +inline void sat_solver_setnvars(sat_solver* s, int n) +{ + int var; + + if (s->cap < n) { + int old_cap = s->cap; + while (s->cap < n) + s->cap = s->cap * 2 + 1; + if (s->cap < 50000) + s->cap = 50000; + + s->wlists = ABC_REALLOC(veci, s->wlists, s->cap * 2); + // s->vi = ABC_REALLOC(varinfo,s->vi, s->cap); + s->levels = ABC_REALLOC(int, s->levels, s->cap); + s->assigns = ABC_REALLOC(char, s->assigns, s->cap); + s->polarity = ABC_REALLOC(char, s->polarity, s->cap); + s->tags = ABC_REALLOC(char, s->tags, s->cap); + s->loads = ABC_REALLOC(char, s->loads, s->cap); + s->activity = ABC_REALLOC(word, s->activity, s->cap); + s->activity2 = ABC_REALLOC(word, s->activity2, s->cap); + s->pFreqs = ABC_REALLOC(char, s->pFreqs, s->cap); + + if (s->factors) + s->factors = ABC_REALLOC(double, s->factors, s->cap); + s->orderpos = ABC_REALLOC(int, s->orderpos, s->cap); + s->reasons = ABC_REALLOC(int, s->reasons, s->cap); + s->trail = ABC_REALLOC(lit, s->trail, s->cap); + s->model = ABC_REALLOC(int, s->model, s->cap); + memset(s->wlists + 2 * old_cap, 0, 2 * (s->cap - old_cap) * sizeof(veci)); + } + + for (var = s->size; var < n; var++) { + assert(!s->wlists[2 * var].size); + assert(!s->wlists[2 * var + 1].size); + if (s->wlists[2 * var].ptr == NULL) + veci_new(&s->wlists[2 * var]); + if (s->wlists[2 * var + 1].ptr == NULL) + veci_new(&s->wlists[2 * var + 1]); + + if (s->VarActType == 0) + s->activity[var] = (1 << 10); + else if (s->VarActType == 1) + s->activity[var] = 0; + else if (s->VarActType == 2) + s->activity[var] = 0; + else + assert(0); + + s->pFreqs[var] = 0; + if (s->factors) + s->factors[var] = 0; + // *((int*)s->vi + var) = 0; s->vi[var].val = varX; + s->levels[var] = 0; + s->assigns[var] = varX; + s->polarity[var] = 0; + s->tags[var] = 0; + s->loads[var] = 0; + s->orderpos[var] = veci_size(&s->order); + s->reasons[var] = 0; + s->model[var] = 0; + + /* does not hold because variables enqueued at top level will not be reinserted in + the heap assert(veci_size(&s->order) == var); + */ + veci_push(&s->order, var); + order_update(s, var); + } + + s->size = n > s->size ? n : s->size; +} + +inline void sat_solver_delete(sat_solver* s) +{ + // Vec_SetFree_( &s->Mem ); + Sat_MemFree_(&s->Mem); + + // delete vectors + veci_delete(&s->order); + veci_delete(&s->trail_lim); + veci_delete(&s->tagged); + // veci_delete(&s->learned); + veci_delete(&s->act_clas); + veci_delete(&s->stack); + // veci_delete(&s->model); + veci_delete(&s->act_vars); + veci_delete(&s->unit_lits); + veci_delete(&s->pivot_vars); + veci_delete(&s->temp_clause); + veci_delete(&s->conf_final); + + veci_delete(&s->user_vars); + veci_delete(&s->user_values); + + // delete arrays + if (s->reasons != 0) { + int i; + for (i = 0; i < s->cap * 2; i++) + veci_delete(&s->wlists[i]); + ABC_FREE(s->wlists); + // ABC_FREE(s->vi ); + ABC_FREE(s->levels); + ABC_FREE(s->assigns); + ABC_FREE(s->polarity); + ABC_FREE(s->tags); + ABC_FREE(s->loads); + ABC_FREE(s->activity); + ABC_FREE(s->activity2); + ABC_FREE(s->pFreqs); + ABC_FREE(s->factors); + ABC_FREE(s->orderpos); + ABC_FREE(s->reasons); + ABC_FREE(s->trail); + ABC_FREE(s->model); + } + + sat_solver_store_free(s); + ABC_FREE(s); +} + +inline void sat_solver_restart(sat_solver* s) +{ + int i; + Sat_MemRestart(&s->Mem); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend(&s->Mem, NULL, 2, 0, 0); + s->binary = clause_read(s, s->hBinary); + + veci_resize(&s->trail_lim, 0); + veci_resize(&s->order, 0); + for (i = 0; i < s->size * 2; i++) + s->wlists[i].size = 0; + + s->nDBreduces = 0; + + // initialize other vars + s->size = 0; + // s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + // variable activities + solver_init_activities(s); + veci_resize(&s->act_clas, 0); + + s->root_level = 0; + // s->simpdb_assigns = 0; + // s->simpdb_props = 0; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; +} + +inline void zsat_solver_restart_seed(sat_solver* s, double seed) +{ + int i; + Sat_MemRestart(&s->Mem); + s->hLearnts = -1; + s->hBinary = Sat_MemAppend(&s->Mem, NULL, 2, 0, 0); + s->binary = clause_read(s, s->hBinary); + + veci_resize(&s->trail_lim, 0); + veci_resize(&s->order, 0); + for (i = 0; i < s->size * 2; i++) + s->wlists[i].size = 0; + + s->nDBreduces = 0; + + // initialize other vars + s->size = 0; + // s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + veci_resize(&s->act_clas, 0); + + s->root_level = 0; + // s->simpdb_assigns = 0; + // s->simpdb_props = 0; + s->random_seed = seed; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; +} + +// returns memory in bytes used by the SAT solver +inline double sat_solver_memory(sat_solver* s) +{ + int i; + double Mem = sizeof(sat_solver); + for (i = 0; i < s->cap * 2; i++) + Mem += s->wlists[i].cap * sizeof(int); + Mem += s->cap * sizeof(veci); // ABC_FREE(s->wlists ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->levels ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->assigns ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->polarity ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->tags ); + Mem += s->cap * sizeof(char); // ABC_FREE(s->loads ); + Mem += s->cap * sizeof(word); // ABC_FREE(s->activity ); + if (s->activity2) + Mem += s->cap * sizeof(word); // ABC_FREE(s->activity ); + if (s->factors) + Mem += s->cap * sizeof(double); // ABC_FREE(s->factors ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->orderpos ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->reasons ); + Mem += s->cap * sizeof(lit); // ABC_FREE(s->trail ); + Mem += s->cap * sizeof(int); // ABC_FREE(s->model ); + + Mem += s->order.cap * sizeof(int); + Mem += s->trail_lim.cap * sizeof(int); + Mem += s->tagged.cap * sizeof(int); + // Mem += s->learned.cap * sizeof(int); + Mem += s->stack.cap * sizeof(int); + Mem += s->act_vars.cap * sizeof(int); + Mem += s->unit_lits.cap * sizeof(int); + Mem += s->act_clas.cap * sizeof(int); + Mem += s->temp_clause.cap * sizeof(int); + Mem += s->conf_final.cap * sizeof(int); + Mem += Sat_MemMemoryAll(&s->Mem); + return Mem; +} + +inline int sat_solver_simplify(sat_solver* s) +{ + assert(sat_solver_dl(s) == 0); + if (sat_solver_propagate(s) != 0) + return false; + return true; +} + +inline void sat_solver_reducedb(sat_solver* s) +{ + static abctime TimeTotal = 0; + abctime clk = Abc_Clock(); + Sat_Mem_t* pMem = &s->Mem; + int nLearnedOld = veci_size(&s->act_clas); + int* act_clas = veci_begin(&s->act_clas); + int *pPerm, *pArray, *pSortValues, nCutoffValue; + int i, k, j, Id, Counter, CounterStart, nSelected; + clause* c; + + assert(s->nLearntMax > 0); + assert(nLearnedOld == Sat_MemEntryNum(pMem, 1)); + assert(nLearnedOld == (int) s->stats.learnts); + + s->nDBreduces++; + + // printf( "Calling reduceDB with %d learned clause limit.\n", s->nLearntMax ); + s->nLearntMax = s->nLearntStart + s->nLearntDelta * s->nDBreduces; + // return; + + // create sorting values + pSortValues = ABC_ALLOC(int, nLearnedOld); + Sat_MemForEachLearned(pMem, c, i, k) + { + Id = clause_id(c); + // pSortValues[Id] = act[Id]; + if (s->ClaActType == 0) + pSortValues[Id] = ((7 - Abc_MinInt(c->lbd, 7)) << 28) | (act_clas[Id] >> 4); + else + pSortValues[Id] = ((7 - Abc_MinInt(c->lbd, 7)) + << 28); // | (act_clas[Id] >> 4); + assert(pSortValues[Id] >= 0); + } + + // preserve 1/20 of last clauses + CounterStart = nLearnedOld - (s->nLearntMax / 20); + + // preserve 3/4 of most active clauses + nSelected = nLearnedOld * s->nLearntRatio / 100; + + // find non-decreasing permutation + pPerm = Abc_MergeSortCost(pSortValues, nLearnedOld); + assert(pSortValues[pPerm[0]] <= pSortValues[pPerm[nLearnedOld - 1]]); + nCutoffValue = pSortValues[pPerm[nLearnedOld - nSelected]]; + ABC_FREE(pPerm); + // ActCutOff = ABC_INFINITY; + + // mark learned clauses to remove + Counter = j = 0; + Sat_MemForEachLearned(pMem, c, i, k) + { + assert(c->mark == 0); + if (Counter++ > CounterStart || clause_size(c) < 3 + || pSortValues[clause_id(c)] > nCutoffValue + || s->reasons[lit_var(c->lits[0])] == Sat_MemHand(pMem, i, k)) + act_clas[j++] = act_clas[clause_id(c)]; + else // delete + { + c->mark = 1; + s->stats.learnts_literals -= clause_size(c); + s->stats.learnts--; + } + } + assert(s->stats.learnts == (unsigned) j); + assert(Counter == nLearnedOld); + veci_resize(&s->act_clas, j); + ABC_FREE(pSortValues); + + // update ID of each clause to be its new handle + Counter = Sat_MemCompactLearned(pMem, 0); + assert(Counter == (int) s->stats.learnts); + + // update reasons + for (i = 0; i < s->size; i++) { + if (!s->reasons[i]) // no reason + continue; + if (clause_is_lit(s->reasons[i])) // 2-lit clause + continue; + if (!clause_learnt_h(pMem, s->reasons[i])) // problem clause + continue; + c = clause_read(s, s->reasons[i]); + assert(c->mark == 0); + s->reasons[i] = clause_id(c); // updating handle here!!! + } + + // update watches + for (i = 0; i < s->size * 2; i++) { + pArray = veci_begin(&s->wlists[i]); + for (j = k = 0; k < veci_size(&s->wlists[i]); k++) { + if (clause_is_lit(pArray[k])) // 2-lit clause + pArray[j++] = pArray[k]; + else if (!clause_learnt_h(pMem, pArray[k])) // problem clause + pArray[j++] = pArray[k]; + else { + c = clause_read(s, pArray[k]); + if (!c->mark) // useful learned clause + pArray[j++] = clause_id(c); // updating handle here!!! + } + } + veci_resize(&s->wlists[i], j); + } + + // perform final move of the clauses + Counter = Sat_MemCompactLearned(pMem, 1); + assert(Counter == (int) s->stats.learnts); + + // report the results + TimeTotal += Abc_Clock() - clk; +} + +// reverses to the previously bookmarked point +inline void sat_solver_rollback(sat_solver* s) +{ + Sat_Mem_t* pMem = &s->Mem; + int i, k, j; + static int Count = 0; + Count++; + assert(s->iVarPivot >= 0 && s->iVarPivot <= s->size); + assert(s->iTrailPivot >= 0 && s->iTrailPivot <= s->qtail); + // reset implication queue + sat_solver_canceluntil_rollback(s, s->iTrailPivot); + // update order + if (s->iVarPivot < s->size) { + if (s->activity2) { + s->var_inc = s->var_inc2; + memcpy(s->activity, s->activity2, sizeof(word) * s->iVarPivot); + } + veci_resize(&s->order, 0); + for (i = 0; i < s->iVarPivot; i++) { + if (var_value(s, i) != varX) + continue; + s->orderpos[i] = veci_size(&s->order); + veci_push(&s->order, i); + order_update(s, i); + } + } + // compact watches + for (i = 0; i < s->iVarPivot * 2; i++) { + cla* pArray = veci_begin(&s->wlists[i]); + for (j = k = 0; k < veci_size(&s->wlists[i]); k++) { + if (clause_is_lit(pArray[k])) { + if (clause_read_lit(pArray[k]) < s->iVarPivot * 2) + pArray[j++] = pArray[k]; + } else if (Sat_MemClauseUsed(pMem, pArray[k])) + pArray[j++] = pArray[k]; + } + veci_resize(&s->wlists[i], j); + } + // reset watcher lists + for (i = 2 * s->iVarPivot; i < 2 * s->size; i++) + s->wlists[i].size = 0; + + // reset clause counts + s->stats.clauses = pMem->BookMarkE[0]; + s->stats.learnts = pMem->BookMarkE[1]; + // rollback clauses + Sat_MemRollBack(pMem); + + // resize learned arrays + veci_resize(&s->act_clas, s->stats.learnts); + + // initialize other vars + s->size = s->iVarPivot; + if (s->size == 0) { + // s->size = 0; + // s->cap = 0; + s->qhead = 0; + s->qtail = 0; + + solver_init_activities(s); + + s->root_level = 0; + s->progress_estimate = 0; + s->verbosity = 0; + + s->stats.starts = 0; + s->stats.decisions = 0; + s->stats.propagations = 0; + s->stats.inspects = 0; + s->stats.conflicts = 0; + s->stats.clauses = 0; + s->stats.clauses_literals = 0; + s->stats.learnts = 0; + s->stats.learnts_literals = 0; + s->stats.tot_literals = 0; + + // initialize rollback + s->iVarPivot = 0; // the pivot for variables + s->iTrailPivot = 0; // the pivot for trail + s->hProofPivot = 1; // the pivot for proof records + } +} + +inline int sat_solver_addclause(sat_solver* s, lit* begin, lit* end) +{ + lit *i, *j; + int maxvar; + lit last; + assert(begin < end); + if (s->fPrintClause) { + for (i = begin; i < end; i++) + printf("%s%d ", (*i) & 1 ? "!" : "", (*i) >> 1); + printf("\n"); + } + + veci_resize(&s->temp_clause, 0); + for (i = begin; i < end; i++) + veci_push(&s->temp_clause, *i); + begin = veci_begin(&s->temp_clause); + end = begin + veci_size(&s->temp_clause); + + // insertion sort + maxvar = lit_var(*begin); + for (i = begin + 1; i < end; i++) { + lit l = *i; + maxvar = lit_var(l) > maxvar ? lit_var(l) : maxvar; + for (j = i; j > begin && *(j - 1) > l; j--) + *j = *(j - 1); + *j = l; + } + sat_solver_setnvars(s, maxvar + 1); + + /////////////////////////////////// + // add clause to internal storage + if (s->pStore) { + int RetValue = Sto_ManAddClause((Sto_Man_t*) s->pStore, begin, end); + assert(RetValue); + (void) RetValue; + } + /////////////////////////////////// + + // delete duplicates + last = lit_Undef; + for (i = j = begin; i < end; i++) { + // printf("lit: "L_LIT", value = %d\n", L_lit(*i), (lit_sign(*i) ? + // -s->assignss[lit_var(*i)] : s->assignss[lit_var(*i)])); + if (*i == lit_neg(last) || var_value(s, lit_var(*i)) == lit_sign(*i)) + return true; // tautology + else if (*i != last && var_value(s, lit_var(*i)) == varX) + last = *j++ = *i; + } + // j = i; + + if (j == begin) // empty clause + return false; + + if (j - begin == 1) // unit clause + return sat_solver_enqueue(s, *begin, 0); + + // create new clause + sat_solver_clause_new(s, begin, j, 0); + return true; +} + +inline double luby(double y, int x) +{ + int size, seq; + for (size = 1, seq = 0; size < x + 1; seq++, size = 2 * size + 1) + ; + while (size - 1 != x) { + size = (size - 1) >> 1; + seq--; + x = x % size; + } + return pow(y, (double) seq); +} + +inline void luby_test() +{ + int i; + for (i = 0; i < 20; i++) + printf("%d ", (int) luby(2, i)); + printf("\n"); +} + +static lbool sat_solver_search(sat_solver* s, ABC_INT64_T nof_conflicts) +{ + // double var_decay = 0.95; + // double clause_decay = 0.999; + double random_var_freq = s->fNotUseRandom ? 0.0 : 0.02; + ABC_INT64_T conflictC = 0; + veci learnt_clause; + int i; + + assert(s->root_level == sat_solver_dl(s)); + + s->nRestarts++; + s->stats.starts++; + // s->var_decay = (float)(1 / var_decay ); // move this to sat_solver_new() + // s->cla_decay = (float)(1 / clause_decay); // move this to sat_solver_new() + // veci_resize(&s->model,0); + veci_new(&learnt_clause); + + // use activity factors in every even restart + if ((s->nRestarts & 1) && veci_size(&s->act_vars) > 0) + // if ( veci_size(&s->act_vars) > 0 ) + for (i = 0; i < s->act_vars.size; i++) + act_var_bump_factor(s, s->act_vars.ptr[i]); + + // use activity factors in every restart + if (s->pGlobalVars && veci_size(&s->act_vars) > 0) + for (i = 0; i < s->act_vars.size; i++) + act_var_bump_global(s, s->act_vars.ptr[i]); + + for (;;) { + int hConfl = sat_solver_propagate(s); + if (hConfl != 0) { + // CONFLICT + int blevel; + +#ifdef VERBOSEDEBUG + printf(L_IND "**CONFLICT**\n", L_ind); +#endif + s->stats.conflicts++; + conflictC++; + if (sat_solver_dl(s) == s->root_level) { +#ifdef SAT_USE_ANALYZE_FINAL + sat_solver_analyze_final(s, hConfl, 0); +#endif + veci_delete(&learnt_clause); + return l_False; + } + + veci_resize(&learnt_clause, 0); + sat_solver_analyze(s, hConfl, &learnt_clause); + blevel = veci_size(&learnt_clause) > 1 ? + var_level(s, lit_var(veci_begin(&learnt_clause)[1])) : + s->root_level; + blevel = s->root_level > blevel ? s->root_level : blevel; + sat_solver_canceluntil(s, blevel); + sat_solver_record(s, &learnt_clause); +#ifdef SAT_USE_ANALYZE_FINAL + // if (learnt_clause.size() == 1) level[var(learnt_clause[0])] = + // 0; // (this is ugly (but needed for 'analyzeFinal()') -- in + // future versions, we will backtrack past the 'root_level' and redo the assumptions) + if (learnt_clause.size == 1) + var_set_level(s, lit_var(learnt_clause.ptr[0]), 0); +#endif + act_var_decay(s); + act_clause_decay(s); + + } else { + // NO CONFLICT + int next; + + // Reached bound on number of conflicts: + if ((!s->fNoRestarts && nof_conflicts >= 0 && conflictC >= nof_conflicts) + || (s->nRuntimeLimit && (s->stats.conflicts & 63) == 0 + && Abc_Clock() > s->nRuntimeLimit)) { + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s, s->root_level); + veci_delete(&learnt_clause); + return l_Undef; + } + + // Reached bound on number of conflicts: + if ((s->nConfLimit && s->stats.conflicts > s->nConfLimit) + || (s->nInsLimit && s->stats.propagations > s->nInsLimit)) { + s->progress_estimate = sat_solver_progress(s); + sat_solver_canceluntil(s, s->root_level); + veci_delete(&learnt_clause); + return l_Undef; + } + + // Simplify the set of problem clauses: + if (sat_solver_dl(s) == 0 && !s->fSkipSimplify) + sat_solver_simplify(s); + + // Reduce the set of learnt clauses: + // if (s->nLearntMax && veci_size(&s->learned) - s->qtail >= s->nLearntMax) + if (s->nLearntMax && veci_size(&s->act_clas) >= s->nLearntMax) + sat_solver_reducedb(s); + + // New variable decision: + s->stats.decisions++; + next = order_select(s, (float) random_var_freq); + + if (next == var_Undef) { + // Model found: + int i; + for (i = 0; i < s->size; i++) + s->model[i] = (var_value(s, i) == var1 ? l_True : l_False); + sat_solver_canceluntil(s, s->root_level); + veci_delete(&learnt_clause); + + /* + veci apa; veci_new(&apa); + for (i = 0; i < s->size; i++) + veci_push(&apa,(int)(s->model.ptr[i] == l_True ? toLit(i) : + lit_neg(toLit(i)))); printf("model: "); printlits((lit*)apa.ptr, + (lit*)apa.ptr + veci_size(&apa)); printf("\n"); veci_delete(&apa); + */ + + return l_True; + } + + if (var_polar(s, next)) // positive polarity + sat_solver_decision(s, toLit(next)); + else + sat_solver_decision(s, lit_neg(toLit(next))); + } + } + + return l_Undef; // cannot happen +} + +// internal call to the SAT solver +inline int sat_solver_solve_internal(sat_solver* s) +{ + lbool status = l_Undef; + int restart_iter = 0; + veci_resize(&s->unit_lits, 0); + s->nCalls++; + + if (s->verbosity >= 1) { + printf("==================================[MINISAT]================================" + "===\n"); + printf("| Conflicts | ORIGINAL | LEARNT | " + "Progress |\n"); + printf("| | Clauses Literals | Limit Clauses Literals Lit/Cl | " + " |\n"); + printf("===========================================================================" + "===\n"); + } + + while (status == l_Undef) { + ABC_INT64_T nof_conflicts; + double Ratio = (s->stats.learnts == 0) ? + 0.0 : + s->stats.learnts_literals / (double) s->stats.learnts; + if (s->nRuntimeLimit && Abc_Clock() > s->nRuntimeLimit) + break; + if (s->verbosity >= 1) { + printf("| %9.0f | %7.0f %8.0f | %7.0f %7.0f %8.0f %7.1f | %6.3f %% |\n", + (double) s->stats.conflicts, (double) s->stats.clauses, + (double) s->stats.clauses_literals, (double) 0, + (double) s->stats.learnts, (double) s->stats.learnts_literals, Ratio, + s->progress_estimate * 100); + fflush(stdout); + } + nof_conflicts = (ABC_INT64_T)(100 * luby(2, restart_iter++)); + status = sat_solver_search(s, nof_conflicts); + // quit the loop if reached an external limit + if (s->nConfLimit && s->stats.conflicts > s->nConfLimit) + break; + if (s->nInsLimit && s->stats.propagations > s->nInsLimit) + break; + if (s->nRuntimeLimit && Abc_Clock() > s->nRuntimeLimit) + break; + if (s->pFuncStop && s->pFuncStop(s->RunId)) + break; + } + if (s->verbosity >= 1) + printf("===========================================================================" + "===\n"); + + sat_solver_canceluntil(s, s->root_level); + // save variable values + if (status == l_True && s->user_vars.size) { + int v; + for (v = 0; v < s->user_vars.size; v++) + veci_push(&s->user_values, sat_solver_var_value(s, s->user_vars.ptr[v])); + } + return status; +} + +// pushing one assumption to the stack of assumptions +inline int sat_solver_push(sat_solver* s, int p) +{ + assert(lit_var(p) < s->size); + veci_push(&s->trail_lim, s->qtail); + s->root_level++; + if (!sat_solver_enqueue(s, p, 0)) { + int h = s->reasons[lit_var(p)]; + if (h) { + if (clause_is_lit(h)) { + (clause_begin(s->binary))[1] = lit_neg(p); + (clause_begin(s->binary))[0] = clause_read_lit(h); + h = s->hBinary; + } + sat_solver_analyze_final(s, h, 1); + veci_push(&s->conf_final, lit_neg(p)); + } else { + veci_resize(&s->conf_final, 0); + veci_push(&s->conf_final, lit_neg(p)); + // the two lines below are a bug fix by Siert Wieringa + if (var_level(s, lit_var(p)) > 0) + veci_push(&s->conf_final, p); + } + // sat_solver_canceluntil(s, 0); + return false; + } else { + int fConfl = sat_solver_propagate(s); + if (fConfl) { + sat_solver_analyze_final(s, fConfl, 0); + // assert(s->conf_final.size > 0); + // sat_solver_canceluntil(s, 0); + return false; + } + } + return true; +} + +// removing one assumption from the stack of assumptions +inline void sat_solver_pop(sat_solver* s) +{ + assert(sat_solver_dl(s) > 0); + sat_solver_canceluntil(s, --s->root_level); +} + +inline void sat_solver_set_resource_limits(sat_solver* s, ABC_INT64_T nConfLimit, + ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, + ABC_INT64_T nInsLimitGlobal) +{ + // set the external limits + s->nRestarts = 0; + s->nConfLimit = 0; + s->nInsLimit = 0; + if (nConfLimit) + s->nConfLimit = s->stats.conflicts + nConfLimit; + if (nInsLimit) + // s->nInsLimit = s->stats.inspects + nInsLimit; + s->nInsLimit = s->stats.propagations + nInsLimit; + if (nConfLimitGlobal && (s->nConfLimit == 0 || s->nConfLimit > nConfLimitGlobal)) + s->nConfLimit = nConfLimitGlobal; + if (nInsLimitGlobal && (s->nInsLimit == 0 || s->nInsLimit > nInsLimitGlobal)) + s->nInsLimit = nInsLimitGlobal; +} + +inline int sat_solver_solve(sat_solver* s, lit* begin, lit* end, ABC_INT64_T nConfLimit, + ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, + ABC_INT64_T nInsLimitGlobal) +{ + lbool status; + lit* i; + //////////////////////////////////////////////// + if (s->fSolved) { + if (s->pStore) { + int RetValue = Sto_ManAddClause((Sto_Man_t*) s->pStore, NULL, NULL); + assert(RetValue); + (void) RetValue; + } + return l_False; + } + //////////////////////////////////////////////// + + if (s->fVerbose) + printf("Running SAT solver with parameters %d and %d and %d.\n", s->nLearntStart, + s->nLearntDelta, s->nLearntRatio); + + sat_solver_set_resource_limits(s, nConfLimit, nInsLimit, nConfLimitGlobal, nInsLimitGlobal); + +#ifdef SAT_USE_ANALYZE_FINAL + // Perform assumptions: + s->root_level = 0; + for (i = begin; i < end; i++) + if (!sat_solver_push(s, *i)) { + sat_solver_canceluntil(s, 0); + s->root_level = 0; + return l_False; + } + assert(s->root_level == sat_solver_dl(s)); +#else + // printf("solve: "); printlits(begin, end); printf("\n"); + for (i = begin; i < end; i++) { + // switch (lit_sign(*i) ? -s->assignss[lit_var(*i)] : s->assignss[lit_var(*i)]){ + switch (var_value(s, *i)) { + case var1: // l_True: + break; + case varX: // l_Undef + sat_solver_decision(s, *i); + if (sat_solver_propagate(s) == 0) + break; + // fallthrough + case var0: // l_False + sat_solver_canceluntil(s, 0); + return l_False; + } + } + s->root_level = sat_solver_dl(s); +#endif + + status = sat_solver_solve_internal(s); + + sat_solver_canceluntil(s, 0); + s->root_level = 0; + + //////////////////////////////////////////////// + if (status == l_False && s->pStore) { + int RetValue = Sto_ManAddClause((Sto_Man_t*) s->pStore, NULL, NULL); + assert(RetValue); + (void) RetValue; + } + //////////////////////////////////////////////// + return status; +} + +// This LEXSAT procedure should be called with a set of literals (pLits, nLits), +// which defines both (1) variable order, and (2) assignment to begin search from. +// It retuns the LEXSAT assigment that is the same or larger than the given one. +// (It assumes that there is no smaller assignment than the one given!) +// The resulting assignment is returned in the same set of literals (pLits, nLits). +// It pushes/pops assumptions internally and will undo them before terminating. +inline int sat_solver_solve_lexsat(sat_solver* s, int* pLits, int nLits) +{ + int i, iLitFail = -1; + lbool status; + assert(nLits > 0); + // help the SAT solver by setting desirable polarity + sat_solver_set_literal_polarity(s, pLits, nLits); + // check if there exists a satisfying assignment + status = sat_solver_solve_internal(s); + if (status != l_True) // no assignment + return status; + // there is at least one satisfying assignment + assert(status == l_True); + // find the first mismatching literal + for (i = 0; i < nLits; i++) + if (pLits[i] != sat_solver_var_literal(s, Abc_Lit2Var(pLits[i]))) + break; + if (i == nLits) // no mismatch - the current assignment is the minimum one! + return l_True; + // mismatch happens in literal i + iLitFail = i; + // create assumptions up to this literal (as in pLits) - including this literal! + for (i = 0; i <= iLitFail; i++) + if (!sat_solver_push(s, pLits[i])) // can become UNSAT while adding the last assumption + break; + if (i < iLitFail + 1) // the solver became UNSAT while adding assumptions + status = l_False; + else // solve under the assumptions + status = sat_solver_solve_internal(s); + if (status == l_True) { + // we proved that there is a sat assignment with literal (iLitFail) having polarity + // as in pLits continue solving recursively + if (iLitFail + 1 < nLits) + status = sat_solver_solve_lexsat(s, pLits + iLitFail + 1, + nLits - iLitFail - 1); + } else if (status == l_False) { + // we proved that there is no assignment with iLitFail having polarity as in pLits + assert(Abc_LitIsCompl(pLits[iLitFail])); // literal is 0 + // (this assert may fail only if there is a sat assignment smaller than one + // originally given in pLits) now we flip this literal (make it 1), change the last + // assumption and contiue looking for the 000...0-assignment of other literals + sat_solver_pop(s); + pLits[iLitFail] = Abc_LitNot(pLits[iLitFail]); + if (!sat_solver_push(s, pLits[iLitFail])) + printf( + "sat_solver_solve_lexsat(): A satisfying assignment should exist.\n"); // because we know that the problem is satisfiable + // update other literals to be 000...0 + for (i = iLitFail + 1; i < nLits; i++) + pLits[i] = Abc_LitNot(Abc_LitRegular(pLits[i])); + // continue solving recursively + if (iLitFail + 1 < nLits) + status = sat_solver_solve_lexsat(s, pLits + iLitFail + 1, + nLits - iLitFail - 1); + else + status = l_True; + } + // undo the assumptions + for (i = iLitFail; i >= 0; i--) + sat_solver_pop(s); + return status; +} + +// This procedure is called on a set of assumptions to minimize their number. +// The procedure relies on the fact that the current set of assumptions is UNSAT. +// It receives and returns SAT solver without assumptions. It returns the number +// of assumptions after minimization. The set of assumptions is returned in pLits. +inline int sat_solver_minimize_assumptions(sat_solver* s, int* pLits, int nLits, int nConfLimit) +{ + int i, k, nLitsL, nLitsR, nResL, nResR, status; + if (nLits == 1) { + // since the problem is UNSAT, we will try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + if (nConfLimit) + s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal(s); + // printf( "%c", status == l_False ? 'u' : 's' ); + return (int) (status != l_False); // return 1 if the problem is not UNSAT + } + assert(nLits >= 2); + nLitsL = nLits / 2; + nLitsR = nLits - nLitsL; + // assume the left lits + for (i = 0; i < nLitsL; i++) + if (!sat_solver_push(s, pLits[i])) { + for (k = i; k >= 0; k--) + sat_solver_pop(s); + return sat_solver_minimize_assumptions(s, pLits, i + 1, nConfLimit); + } + // solve with these assumptions + if (nConfLimit) + s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal(s); + if (status == l_False) // these are enough + { + for (i = 0; i < nLitsL; i++) + sat_solver_pop(s); + return sat_solver_minimize_assumptions(s, pLits, nLitsL, nConfLimit); + } + // solve for the right lits + nResL = nLitsR == 1 ? 1 : + sat_solver_minimize_assumptions(s, pLits + nLitsL, nLitsR, nConfLimit); + for (i = 0; i < nLitsL; i++) + sat_solver_pop(s); + // swap literals + // assert( nResL <= nLitsL ); + // for ( i = 0; i < nResL; i++ ) + // ABC_SWAP( int, pLits[i], pLits[nLitsL+i] ); + veci_resize(&s->temp_clause, 0); + for (i = 0; i < nLitsL; i++) + veci_push(&s->temp_clause, pLits[i]); + for (i = 0; i < nResL; i++) + pLits[i] = pLits[nLitsL + i]; + for (i = 0; i < nLitsL; i++) + pLits[nResL + i] = veci_begin(&s->temp_clause)[i]; + // assume the right lits + for (i = 0; i < nResL; i++) + if (!sat_solver_push(s, pLits[i])) { + for (k = i; k >= 0; k--) + sat_solver_pop(s); + return sat_solver_minimize_assumptions(s, pLits, i + 1, nConfLimit); + } + // solve with these assumptions + if (nConfLimit) + s->nConfLimit = s->stats.conflicts + nConfLimit; + status = sat_solver_solve_internal(s); + if (status == l_False) // these are enough + { + for (i = 0; i < nResL; i++) + sat_solver_pop(s); + return nResL; + } + // solve for the left lits + nResR = nLitsL == 1 ? 1 : + sat_solver_minimize_assumptions(s, pLits + nResL, nLitsL, nConfLimit); + for (i = 0; i < nResL; i++) + sat_solver_pop(s); + return nResL + nResR; +} + +// This is a specialized version of the above procedure with several custom changes: +// - makes sure that at least one of the marked literals is preserved in the clause +// - sets literals to zero when they do not have to be used +// - sets literals to zero for disproved variables +inline int sat_solver_minimize_assumptions2(sat_solver* s, int* pLits, int nLits, int nConfLimit) +{ + int i, k, nLitsL, nLitsR, nResL, nResR; + if (nLits == 1) { + // since the problem is UNSAT, we will try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + int RetValue = 1, LitNot = Abc_LitNot(pLits[0]); + int status = l_False; + int Temp = s->nConfLimit; + s->nConfLimit = nConfLimit; + + RetValue = sat_solver_push(s, LitNot); + assert(RetValue); + status = sat_solver_solve_internal(s); + sat_solver_pop(s); + + // if the problem is UNSAT, add clause + if (status == l_False) { + RetValue = sat_solver_addclause(s, &LitNot, &LitNot + 1); + assert(RetValue); + } + + s->nConfLimit = Temp; + return (int) (status != l_False); // return 1 if the problem is not UNSAT + } + assert(nLits >= 2); + nLitsL = nLits / 2; + nLitsR = nLits - nLitsL; + // assume the left lits + for (i = 0; i < nLitsL; i++) + if (!sat_solver_push(s, pLits[i])) { + for (k = i; k >= 0; k--) + sat_solver_pop(s); + + // add clauses for these literal + for (k = i + 1; k > nLitsL; k++) { + int LitNot = Abc_LitNot(pLits[i]); + int RetValue = sat_solver_addclause(s, &LitNot, &LitNot + 1); + assert(RetValue); + } + + return sat_solver_minimize_assumptions2(s, pLits, i + 1, nConfLimit); + } + // solve for the right lits + nResL = sat_solver_minimize_assumptions2(s, pLits + nLitsL, nLitsR, nConfLimit); + for (i = 0; i < nLitsL; i++) + sat_solver_pop(s); + // swap literals + // assert( nResL <= nLitsL ); + veci_resize(&s->temp_clause, 0); + for (i = 0; i < nLitsL; i++) + veci_push(&s->temp_clause, pLits[i]); + for (i = 0; i < nResL; i++) + pLits[i] = pLits[nLitsL + i]; + for (i = 0; i < nLitsL; i++) + pLits[nResL + i] = veci_begin(&s->temp_clause)[i]; + // assume the right lits + for (i = 0; i < nResL; i++) + if (!sat_solver_push(s, pLits[i])) { + for (k = i; k >= 0; k--) + sat_solver_pop(s); + + // add clauses for these literal + for (k = i + 1; k > nResL; k++) { + int LitNot = Abc_LitNot(pLits[i]); + int RetValue = sat_solver_addclause(s, &LitNot, &LitNot + 1); + assert(RetValue); + } + + return sat_solver_minimize_assumptions2(s, pLits, i + 1, nConfLimit); + } + // solve for the left lits + nResR = sat_solver_minimize_assumptions2(s, pLits + nResL, nLitsL, nConfLimit); + for (i = 0; i < nResL; i++) + sat_solver_pop(s); + return nResL + nResR; +} + +inline int sat_solver_nvars(sat_solver* s) +{ + return s->size; +} + +inline int sat_solver_nclauses(sat_solver* s) +{ + return s->stats.clauses; +} + +inline int sat_solver_nconflicts(sat_solver* s) +{ + return (int) s->stats.conflicts; +} + +//================================================================================================= +// Clause storage functions: + +inline void sat_solver_store_alloc(sat_solver* s) +{ + assert(s->pStore == NULL); + s->pStore = Sto_ManAlloc(); +} + +inline void sat_solver_store_write(sat_solver* s, char* pFileName) +{ + if (s->pStore) + Sto_ManDumpClauses((Sto_Man_t*) s->pStore, pFileName); +} + +inline void sat_solver_store_free(sat_solver* s) +{ + if (s->pStore) + Sto_ManFree((Sto_Man_t*) s->pStore); + s->pStore = NULL; +} + +inline int sat_solver_store_change_last(sat_solver* s) +{ + if (s->pStore) + return Sto_ManChangeLastClause((Sto_Man_t*) s->pStore); + return -1; +} + +inline void sat_solver_store_mark_roots(sat_solver* s) +{ + if (s->pStore) + Sto_ManMarkRoots((Sto_Man_t*) s->pStore); +} + +inline void sat_solver_store_mark_clauses_a(sat_solver* s) +{ + if (s->pStore) + Sto_ManMarkClausesA((Sto_Man_t*) s->pStore); +} + +inline void* sat_solver_store_release(sat_solver* s) +{ + void* pTemp; + if (s->pStore == NULL) + return NULL; + pTemp = s->pStore; + s->pStore = NULL; + return pTemp; +} + +ABC_NAMESPACE_IMPL_END + +/*** SimpSolver.cpp */ + +/***********************************************************************************[SimpSolver.cc] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 "abc/SimpSolver.h" +#include "abc/Sort.h" +#include "abc/system.h" + +ABC_NAMESPACE_IMPL_START + +namespace Gluco { + +//================================================================================================= +// Options: + +static const char* _cat = "SIMP"; + +//================================================================================================= +// Constructor/Destructor: + +inline SimpSolver::SimpSolver() + : grow(0) + , clause_lim(20) + , subsumption_lim(1000) + , simp_garbage_frac(0.5) + , use_asymm(false) + , use_rcheck(false) + , use_elim(true) + , merges(0) + , asymm_lits(0) + , eliminated_vars(0) + , eliminated_clauses(0) + , elimorder(1) + , use_simplification(true) + , occurs(ClauseDeleted(ca)) + , elim_heap(ElimLt(n_occ)) + , bwdsub_assigns(0) + , n_touched(0) +{ + vec dummy(1, lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + +inline SimpSolver::~SimpSolver() +{} + +inline Var SimpSolver::newVar(bool sign, bool dvar) +{ + Var v = Solver::newVar(sign, dvar); + + frozen.push((char) false); + eliminated.push((char) false); + + if (use_simplification) { + n_occ.push(0); + n_occ.push(0); + occurs.init(v); + touched.push(0); + elim_heap.insert(v); + } + return v; +} + +inline lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + + do_simp &= use_simplification; + + if (do_simp) { + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++) { + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]) { + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } + } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("===========================================================================" + "====\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + return result; +} + +inline bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if (use_simplification && clauses.size() == nclauses + 1) { + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++) { + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + +inline void SimpSolver::removeClause(CRef cr) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++) { + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr); +} + +inline bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (certifiedUNSAT) { + for (int i = 0; i < c.size(); i++) + if (c[i] != l) + fprintf(certifiedOutput, "%i ", + (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + if (c.size() == 2) { + removeClause(cr); + c.strengthen(l); + } else { + if (certifiedUNSAT) { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", + (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + int i, j; + for (i = 0; i < qs.size(); i++) { + if (var(qs[i]) != v) { + for (j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) { + if (ps[j] == ~qs[i]) + return false; + else + goto next; + } + out_clause.push(qs[i]); + } + next:; + } + + for (i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + +// Returns FALSE if clause is always satisfied. +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*) ps; + const Lit* __qs = (const Lit*) qs; + + size = ps.size() - 1; + + for (int i = 0; i < qs.size(); i++) { + if (var(__qs[i]) != v) { + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) { + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + } + size++; + } + next:; + } + + return true; +} + +inline void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) + return; + + int i, j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]) { + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0) { + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + +inline bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) { + cancelUntil(0); + return false; + } else if (value(c[i]) != l_False) { + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + +// Backward subsumption + backward subsumption resolution +inline bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) { + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt) { + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; + } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()) { + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); + } + + CRef cr = subsumption_queue.peek(); + subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) + continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", + subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 + || value(c[0]) + == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*) _cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr + && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)) { + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error) { + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check + // candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + +inline bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) + return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v && value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + else + l = c[i]; + + if (propagate() != CRef_Undef) { + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + } else + cancelUntil(0); + + return true; +} + +inline bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++) { + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + +inline bool SimpSolver::eliminateVar(Var v) +{ + int i, j; + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). + // Moreover, no clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (i = 0; i < pos.size(); i++) + for (j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) + && (++cnt > cls.size() + grow + || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses: + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()) { + for (i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + eliminated_clauses += neg.size(); + } else { + for (i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + eliminated_clauses += pos.size(); + } + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (i = 0; i < pos.size(); i++) + for (j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + if (watches[mkLit(v)].size() == 0) + watches[mkLit(v)].clear(true); + if (watches[~mkLit(v)].size() == 0) + watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + +inline bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) + return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++) { + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++) { + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + } + + return true; +} + +inline void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + for (i = elimclauses.size() - 1; i > 0; i -= j) { + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + +inline bool SimpSolver::eliminate(bool turn_off_elim) +{ + // abctime clk = Abc_Clock(); + if (!simplify()) + return false; + else if (!use_simplification) + return true; + + // Main simplification loop: + // + + int toPerform = clauses.size() <= 4800000; + + if (!toPerform) { + printf("c Too many clauses... No preprocessing\n"); + } + + while (toPerform && (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0)) { + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), + // subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) + && !backwardSubsumptionCheck(true)) { + ok = false; + goto cleanup; + } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt) { + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; + } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++) { + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) + break; + + if (isEliminated(elim) || value(elim) != l_Undef) + continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("elimination left: %10d\r", elim_heap.size()); + + if (use_asymm) { + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim] != 0; + frozen[elim] = true; + if (!asymmVar(elim)) { + ok = false; + goto cleanup; + } + frozen[elim] = was_frozen; + } + + // At this point, the variable may have been set by assymetric branching, so + // check it again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] + && !eliminateVar(elim)) { + ok = false; + goto cleanup; + } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } +cleanup: + + // If no more simplification is needed, free all simplification-related data structures: + if (turn_off_elim) { + touched.clear(true); + occurs.clear(true); + n_occ.clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + } else { + // Cheaper cleanup: + cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? + checkGarbage(); + } + + if (verbosity >= 1 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb " + " |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024 * 1024)); + return ok; +} + +inline void SimpSolver::cleanUpClauses() +{ + occurs.cleanAll(); + int i, j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); +} + +//================================================================================================= +// Garbage Collection methods: + +inline void SimpSolver::relocAll(ClauseAllocator& to) +{ + int i; + if (!use_simplification) + return; + + // All occurs lists: + // + for (i = 0; i < nVars(); i++) { + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + +inline void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. + // This is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + cleanUpClauses(); + to.extra_clause_field + = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +inline void SimpSolver::reset() +{ + Solver::reset(); + grow = 0; + asymm_lits = eliminated_vars = bwdsub_assigns = n_touched = 0; + elimclauses.clear(false); + touched.clear(false); + occurs.clear(false); + n_occ.clear(false); + elim_heap.clear(false); + subsumption_queue.clear(false); + frozen.clear(false); + eliminated.clear(false); + vec dummy(1, lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + +} /* namespace Gluco */ + +ABC_NAMESPACE_IMPL_END + +/*** Glucose.cpp ***/ + +/***************************************************************************************[Solver.cc] + Glucose -- Copyright (c) 2013, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 "abc/Constants.h" +#include "abc/Solver.h" +#include "abc/Sort.h" +#include "abc/system.h" + +#include + +ABC_NAMESPACE_IMPL_START + +//================================================================================================= +// Options: + +static const char* _cat = "CORE"; +static const char* _cr = "CORE -- RESTART"; +static const char* _cred = "CORE -- REDUCE"; +static const char* _cm = "CORE -- MINIMIZE"; +static const char* _certified = "CORE -- CERTIFIED UNSAT"; + +const bool opt_incremental = false; +const double opt_K = 0.8; +const double opt_R = 1.4; +const int opt_size_lbd_queue = 50; + +const int opt_first_reduce_db = 2000; +const int opt_inc_reduce_db = 300; +const int opt_spec_inc_reduce_db = 1000; +const int opt_lb_lbd_frozen_clause = 30; + +const int opt_lb_size_minimzing_clause = 30; +const int opt_lb_lbd_minimzing_clause = 6; + +const double opt_var_decay = 0.8; +const double opt_clause_decay = 0.999; +const double opt_random_var_freq = 0; +const double opt_random_seed = 91648253; +const int opt_ccmin_mode = 2; +const int opt_phase_saving = 2; +const bool opt_rnd_init_act = false; +/* +static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, +IntRange(1, INT32_MAX)); static DoubleOption opt_restart_inc (_cat, "rinc", "Restart +interval increase factor", 2, DoubleRange(1, +false, HUGE_VAL, false)); +*/ +const double opt_garbage_frac = 0.20; + +const bool opt_certified_ = false; +const char* const opt_certified_file_ = "NULL"; + +namespace Gluco { + +//================================================================================================= +// Constructor/Destructor: + +inline Solver::Solver() + : + + // Parameters (user settable): + // + SolverType(0) + , pCnfFunc(NULL) + , nCallConfl(1000) + , terminate_search_early(false) + , pstop(NULL) + , nRuntimeLimit(0) + + , verbosity(0) + , verbEveryConflicts(10000) + , showModel(0) + , K(opt_K) + , R(opt_R) + , sizeLBDQueue(50) + , sizeTrailQueue(5000) + , firstReduceDB(opt_first_reduce_db) + , incReduceDB(opt_inc_reduce_db) + , specialIncReduceDB(opt_spec_inc_reduce_db) + , lbLBDFrozenClause(opt_lb_lbd_frozen_clause) + , lbSizeMinimizingClause(opt_lb_size_minimzing_clause) + , lbLBDMinimizingClause(opt_lb_lbd_minimzing_clause) + , var_decay(opt_var_decay) + , clause_decay(opt_clause_decay) + , random_var_freq(opt_random_var_freq) + , random_seed(opt_random_seed) + , ccmin_mode(opt_ccmin_mode) + , phase_saving(opt_phase_saving) + , rnd_pol(false) + , rnd_init_act(opt_rnd_init_act) + , garbage_frac(opt_garbage_frac) + , certifiedOutput(NULL) + , certifiedUNSAT(opt_certified_) + // Statistics: (formerly in 'SolverStats') + // + , nbRemovedClauses(0) + , nbReducedClauses(0) + , nbDL2(0) + , nbBin(0) + , nbUn(0) + , nbReduceDB(0) + , solves(0) + , starts(0) + , decisions(0) + , rnd_decisions(0) + , propagations(0) + , conflicts(0) + , conflictsRestarts(0) + , nbstopsrestarts(0) + , nbstopsrestartssame(0) + , lastblockatrestart(0) + , dec_vars(0) + , clauses_literals(0) + , learnts_literals(0) + , max_literals(0) + , tot_literals(0) + , curRestart(1) + + , ok(true) + , cla_inc(1) + , var_inc(1) + , watches(WatcherDeleted(ca)) + , watchesBin(WatcherDeleted(ca)) + , qhead(0) + , simpDB_assigns(-1) + , simpDB_props(0) + , order_heap(VarOrderLt(activity)) + , progress_estimate(0) + , remove_satisfied(true) + + // Resource constraints: + // + , conflict_budget(-1) + , propagation_budget(-1) + , asynch_interrupt(false) + , incremental(opt_incremental) + , nbVarsInitialFormula(INT32_MAX) +{ + MYFLAG = 0; + // Initialize only first time. Useful for incremental solving, useless otherwise + lbdQueue.initSize(sizeLBDQueue); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + totalTime4Sat = 0; + totalTime4Unsat = 0; + nbSatCalls = 0; + nbUnsatCalls = 0; + + if (certifiedUNSAT) { + if (!strcmp(opt_certified_file_, "NULL")) { + certifiedOutput = fopen("/dev/stdout", "wb"); + } else { + certifiedOutput = fopen(opt_certified_file_, "wb"); + } + // fprintf(certifiedOutput,"o proof DRUP\n"); + } +} + +inline Solver::~Solver() +{} + +/**************************************************************** + Set the incremental mode +****************************************************************/ + +// This function set the incremental mode to true. +// You can add special code for this mode here. + +inline void Solver::setIncrementalMode() +{ + incremental = true; +} + +// Number of variables without selectors +inline void Solver::initNbInitialVars(int nb) +{ + nbVarsInitialFormula = nb; +} + +//================================================================================================= +// Minor methods: + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// +inline Var Solver::newVar(bool sign, bool dvar) +{ + int v = nVars(); + watches.init(mkLit(v, false)); + watches.init(mkLit(v, true)); + watchesBin.init(mkLit(v, false)); + watchesBin.init(mkLit(v, true)); + assigns.push(l_Undef); + vardata.push(mkVarData(CRef_Undef, 0)); + // activity .push(0); + activity.push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen.push(0); + permDiff.push(0); + polarity.push(sign); + decision.push(); + trail.capacity(v + 1); + setDecisionVar(v, dvar); + return v; +} + +inline bool Solver::addClause_(vec& ps) +{ + assert(decisionLevel() == 0); + if (!ok) + return false; + + if (0) { + for (int i = 0; i < ps.size(); i++) + printf("%s%d ", (toInt(ps[i]) & 1) ? "-" : "", toInt(ps[i]) >> 1); + printf("\n"); + } + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + + vec oc; + oc.clear(); + + Lit p; + int i, j, flag = 0; + if (certifiedUNSAT) { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) { + oc.push(ps[i]); + if (value(ps[i]) == l_True || ps[i] == ~p || value(ps[i]) == l_False) + flag = 1; + } + } + + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + if (value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if (value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if (0) { + for (int i = 0; i < ps.size(); i++) + printf("%s%d ", (toInt(ps[i]) & 1) ? "-" : "", toInt(ps[i]) >> 1); + printf("\n"); + } + + if (flag && (certifiedUNSAT)) { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + fprintf(certifiedOutput, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + + fprintf(certifiedOutput, "d "); + for (i = j = 0, p = lit_Undef; i < oc.size(); i++) + fprintf(certifiedOutput, "%i ", (var(oc[i]) + 1) * (-2 * sign(oc[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + if (ps.size() == 0) + return ok = false; + else if (ps.size() == 1) { + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + } else { + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + +inline void Solver::attachClause(CRef cr) +{ + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if (c.size() == 2) { + watchesBin[~c[0]].push(Watcher(cr, c[1])); + watchesBin[~c[1]].push(Watcher(cr, c[0])); + } else { + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + } + if (c.learnt()) + learnts_literals += c.size(); + else + clauses_literals += c.size(); +} + +inline void Solver::detachClause(CRef cr, bool strict) +{ + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if (c.size() == 2) { + if (strict) { + remove(watchesBin[~c[0]], Watcher(cr, c[1])); + remove(watchesBin[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watchesBin.smudge(~c[0]); + watchesBin.smudge(~c[1]); + } + } else { + if (strict) { + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + } + if (c.learnt()) + learnts_literals -= c.size(); + else + clauses_literals -= c.size(); +} + +inline void Solver::removeClause(CRef cr) +{ + + Clause& c = ca[cr]; + + if (certifiedUNSAT) { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + detachClause(cr); + // Don't leave pointers to free'd memory! + if (locked(c)) + vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free_(cr); +} + +inline bool Solver::satisfied(const Clause& c) const +{ + if (incremental) // Check clauses with many selectors is too time consuming + return (value(c[0]) == l_True) || (value(c[1]) == l_True); + + // Default mode. + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) + return true; + return false; +} + +/************************************************************ + * Compute LBD functions + *************************************************************/ + +inline unsigned int Solver::computeLBD(const vec& lits, int end) +{ + int nblevels = 0; + MYFLAG++; + + if (incremental) { // ----------------- INCREMENTAL MODE + if (end == -1) + end = lits.size(); + unsigned int nbDone = 0; + for (int i = 0; i < lits.size(); i++) { + if (nbDone >= end) + break; + if (isSelector(var(lits[i]))) + continue; + nbDone++; + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for (int i = 0; i < lits.size(); i++) { + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } + + return nblevels; +} + +inline unsigned int Solver::computeLBD(const Clause& c) +{ + int nblevels = 0; + MYFLAG++; + + if (incremental) { // ----------------- INCREMENTAL MODE + int nbDone = 0; + for (int i = 0; i < c.size(); i++) { + if (nbDone >= c.sizeWithoutSelectors()) + break; + if (isSelector(var(c[i]))) + continue; + nbDone++; + int l = level(var(c[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for (int i = 0; i < c.size(); i++) { + int l = level(var(c[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } + return nblevels; +} + +/****************************************************************** + * Minimisation with binary reolution + ******************************************************************/ +inline void Solver::minimisationWithBinaryResolution(vec& out_learnt) +{ + + // Find the LBD measure + unsigned int lbd = computeLBD(out_learnt); + Lit p = ~out_learnt[0]; + + if (lbd <= lbLBDMinimizingClause) { + MYFLAG++; + + for (int i = 1; i < out_learnt.size(); i++) { + permDiff[var(out_learnt[i])] = MYFLAG; + } + + vec& wbin = watchesBin[p]; + int nb = 0; + for (int k = 0; k < wbin.size(); k++) { + Lit imp = wbin[k].blocker; + if (permDiff[var(imp)] == MYFLAG && value(imp) == l_True) { + nb++; + permDiff[var(imp)] = MYFLAG - 1; + } + } + int l = out_learnt.size() - 1; + if (nb > 0) { + nbReducedClauses++; + for (int i = 1; i < out_learnt.size() - nb; i++) { + if (permDiff[var(out_learnt[i])] != MYFLAG) { + Lit p = out_learnt[l]; + out_learnt[l] = out_learnt[i]; + out_learnt[i] = p; + l--; + i--; + } + } + + out_learnt.shrink(nb); + } + } +} + +// Revert to the state at given level (keeping all assignment at 'level' but not beyond). +// +inline void Solver::cancelUntil(int level) +{ + if (decisionLevel() > level) { + for (int c = trail.size() - 1; c >= trail_lim[level]; c--) { + Var x = var(trail[c]); + assigns[x] = l_Undef; + if (phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) + polarity[x] = sign(trail[c]); + insertVarOrder(x); + } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + +//================================================================================================= +// Major methods: + +inline Lit Solver::pickBranchLit() +{ + Var next = var_Undef; + + // Random decision: + if (drand(random_seed) < random_var_freq && !order_heap.empty()) { + next = order_heap[irand(random_seed, order_heap.size())]; + if (value(next) == l_Undef && decision[next]) + rnd_decisions++; + } + + // Activity based decision: + while (next == var_Undef || value(next) != l_Undef || !decision[next]) + if (order_heap.empty()) { + next = var_Undef; + break; + } else + next = order_heap.removeMin(); + + return next == var_Undef ? + lit_Undef : + mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : (polarity[next] != 0)); +} + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +inline void Solver::analyze(CRef confl, vec& out_learnt, vec& selectors, int& out_btlevel, + unsigned int& lbd, unsigned int& szWithoutSelectors) +{ + int pathC = 0; + Lit p = lit_Undef; + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + + do { + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause& c = ca[confl]; + + // Special case for binary clauses + // The first one has to be SAT + if (p != lit_Undef && c.size() == 2 && value(c[0]) == l_False) { + + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + if (c.learnt()) + claBumpActivity(c); + +#ifdef DYNAMICNBLEVEL + // DYNAMIC NBLEVEL trick (see competition'09 companion paper) + if (c.learnt() && c.lbd() > 2) { + unsigned int nblevels = computeLBD(c); + if (nblevels + 1 < c.lbd()) { // improve the LBD + if (c.lbd() <= lbLBDFrozenClause) { + c.setCanBeDel(false); + } + // seems to be interesting : keep it for the next round + c.setLBD(nblevels); // Update it + } + } +#endif + + for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++) { + Lit q = c[j]; + + if (!seen[var(q)] && level(var(q)) > 0) { + if (!isSelector(var(q))) + varBumpActivity(var(q)); + seen[var(q)] = 1; + if (level(var(q)) >= decisionLevel()) { + pathC++; +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if (!isSelector(var(q)) && (reason(var(q)) != CRef_Undef) + && ca[reason(var(q))].learnt()) + lastDecisionLevel.push(q); +#endif + + } else { + if (isSelector(var(q))) { + assert(value(q) == l_False); + selectors.push(q); + } else + out_learnt.push(q); + } + } + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]) + ; + p = trail[index + 1]; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + } while (pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + + for (i = 0; i < selectors.size(); i++) + out_learnt.push(selectors[i]); + + out_learnt.copyTo(analyze_toclear); + if (ccmin_mode == 2) { + uint32_t abstract_level = 0; + for (i = 1; i < out_learnt.size(); i++) + abstract_level |= abstractLevel(var( + out_learnt[i])); // (maintain an abstraction of levels involved in conflict) + + for (i = j = 1; i < out_learnt.size(); i++) + if (reason(var(out_learnt[i])) == CRef_Undef + || !litRedundant(out_learnt[i], abstract_level)) + out_learnt[j++] = out_learnt[i]; + + } else if (ccmin_mode == 1) { + for (i = j = 1; i < out_learnt.size(); i++) { + Var x = var(out_learnt[i]); + + if (reason(x) == CRef_Undef) + out_learnt[j++] = out_learnt[i]; + else { + Clause& c = ca[reason(var(out_learnt[i]))]; + // Thanks to Siert Wieringa for this bug fix! + for (int k = ((c.size() == 2) ? 0 : 1); k < c.size(); k++) + if (!seen[var(c[k])] && level(var(c[k])) > 0) { + out_learnt[j++] = out_learnt[i]; + break; + } + } + } + } else + i = j = out_learnt.size(); + + max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + tot_literals += out_learnt.size(); + + /* *************************************** + Minimisation with binary clauses of the asserting clause + First of all : we look for small clauses + Then, we reduce clauses with small LBD. + Otherwise, this can be useless + */ + if (!incremental && out_learnt.size() <= lbSizeMinimizingClause) { + minimisationWithBinaryResolution(out_learnt); + } + // Find correct backtrack level: + // + if (out_learnt.size() == 1) + out_btlevel = 0; + else { + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for (int i = 2; i < out_learnt.size(); i++) + if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } + + // Compute the size of the clause without selectors (incremental mode) + if (incremental) { + szWithoutSelectors = 0; + for (int i = 0; i < out_learnt.size(); i++) { + if (!isSelector(var((out_learnt[i])))) + szWithoutSelectors++; + else if (i > 0) + break; + } + } else + szWithoutSelectors = out_learnt.size(); + + // Compute LBD + lbd = computeLBD(out_learnt, out_learnt.size() - selectors.size()); + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if (lastDecisionLevel.size() > 0) { + for (int i = 0; i < lastDecisionLevel.size(); i++) { + if (ca[reason(var(lastDecisionLevel[i]))].lbd() < lbd) + varBumpActivity(var(lastDecisionLevel[i])); + } + lastDecisionLevel.clear(); + } +#endif + + for (j = 0; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) + for (j = 0; j < selectors.size(); j++) + seen[var(selectors[j])] = 0; +} + +// Check if 'p' can be removed. 'abstract_levels' is used to abort early if the algorithm is +// visiting literals at levels that cannot be removed later. +inline bool Solver::litRedundant(Lit p, uint32_t abstract_levels) +{ + analyze_stack.clear(); + analyze_stack.push(p); + int top = analyze_toclear.size(); + while (analyze_stack.size() > 0) { + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause& c = ca[reason(var(analyze_stack.last()))]; + analyze_stack.pop(); + if (c.size() == 2 && value(c[0]) == l_False) { + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + for (int i = 1; i < c.size(); i++) { + Lit p = c[i]; + if (!seen[var(p)] && level(var(p)) > 0) { + if (reason(var(p)) != CRef_Undef + && (abstractLevel(var(p)) & abstract_levels) != 0) { + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + } else { + for (int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + + return true; +} + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +inline void Solver::analyzeFinal(Lit p, vec& out_conflict) +{ + out_conflict.clear(); + out_conflict.push(p); + + if (decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for (int i = trail.size() - 1; i >= trail_lim[0]; i--) { + Var x = var(trail[i]); + if (seen[x]) { + if (reason(x) == CRef_Undef) { + assert(level(x) > 0); + out_conflict.push(~trail[i]); + } else { + Clause& c = ca[reason(x)]; + // for (int j = 1; j < c.size(); j++) Minisat (glucose 2.0) loop + // Bug in case of assumptions due to special data structures for Binary. + // Many thanks to Sam Bayless (sbayless@cs.ubc.ca) for discover this bug. + for (int j = ((c.size() == 2) ? 0 : 1); j < c.size(); j++) + if (level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + +inline void Solver::uncheckedEnqueue(Lit p, CRef from) +{ + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel()); + trail.push_(p); +} + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +inline CRef Solver::propagate() +{ + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watchesBin.cleanAll(); + while (qhead < trail.size()) { + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec& ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + // First, Propagate binary clauses + vec& wbin = watchesBin[p]; + for (int k = 0; k < wbin.size(); k++) { + Lit imp = wbin[k].blocker; + if (value(imp) == l_False) { + return wbin[k].cref; + } + if (value(imp) == l_Undef) { + uncheckedEnqueue(imp, wbin[k].cref); + } + } + + for (i = j = (Watcher*) ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if (value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True) { + *j++ = w; + continue; + } + + // Look for new watch: + if (incremental) { // ----------------- INCREMENTAL MODE + int choosenPos = -1; + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False) { + if (decisionLevel() > assumptions.size()) { + choosenPos = k; + break; + } else { + choosenPos = k; + + if (value(c[k]) == l_True + || !isSelector(var(c[k]))) { + break; + } + } + } + } + if (choosenPos != -1) { + c[1] = c[choosenPos]; + c[choosenPos] = false_lit; + watches[~c[1]].push(w); + goto NextClause; + } + } else { // ----------------- DEFAULT MODE (NOT INCREMENTAL) + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False) { + c[1] = c[k]; + c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; + } + } + } + + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if (value(first) == l_False) { + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } else { + uncheckedEnqueue(first, cr); + } + NextClause:; + } + ws.shrink(i - j); + } + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ +struct reduceDB_lt { + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) + : ca(ca_) + {} + bool operator()(CRef x, CRef y) + { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) + return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) + return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) + return 0; + + // Second one based on literal block distance + if (ca[x].lbd() > ca[y].lbd()) + return 1; + if (ca[x].lbd() < ca[y].lbd()) + return 0; + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + // return x->size() < y->size(); + // return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + +inline void Solver::reduceDB() +{ + int i, j; + nbReduceDB++; + sort(learnts, reduceDB_lt(ca)); + + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + if (ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd() <= 3) + nbclausesbeforereduce += specialIncReduceDB; + // Useless :-) + if (ca[learnts.last()].lbd() <= 5) + nbclausesbeforereduce += specialIncReduceDB; + + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + int limit = learnts.size() / 2; + for (i = j = 0; i < learnts.size(); i++) { + Clause& c = ca[learnts[i]]; + if (c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + nbRemovedClauses++; + } else { + if (!c.canBeDel()) + limit++; // we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + checkGarbage(); +} + +inline void Solver::removeSatisfied(vec& cs) +{ + int i, j; + for (i = j = 0; i < cs.size(); i++) { + Clause& c = ca[cs[i]]; + if (satisfied(c)) + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + +inline void Solver::rebuildOrderHeap() +{ + vec vs; + for (Var v = 0; v < nVars(); v++) + if (decision[v] && value(v) == l_Undef) + vs.push(v); + order_heap.build(vs); +} + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +inline bool Solver::simplify() +{ + assert(decisionLevel() == 0); + + if (!ok || propagate() != CRef_Undef) + return ok = false; + + if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + removeSatisfied(learnts); + if (remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + + checkGarbage(); + + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = clauses_literals + + learnts_literals; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + +/*_________________________________________________________________________________________________ +| +| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +inline lbool Solver::search(int nof_conflicts) +{ + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause, selectors; + unsigned int nblevels, szWoutSelectors; + bool blocked = false; + starts++; + for (;;) { + CRef confl = propagate(); + if (confl != CRef_Undef) { + // CONFLICT + conflicts++; + conflictC++; + conflictsRestarts++; + if (conflicts % 5000 == 0 && var_decay < 0.95) + var_decay += 0.01; + + if (verbosity >= 1 && conflicts % verbEveryConflicts == 0) { + printf("c | %8d %7d %5d | %7d %8d %8d | %5d %8d %6d %8d | " + "%6.3f %% |\n", + (int) starts, (int) nbstopsrestarts, + (int) (conflicts / starts), + (int) dec_vars + - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), + nClauses(), (int) clauses_literals, (int) nbReduceDB, + nLearnts(), (int) nbDL2, (int) nbRemovedClauses, + progressEstimate() * 100); + } + if (decisionLevel() == 0) { + return l_False; + } + + trailQueue.push(trail.size()); + // BLOCK RESTART (CP 2012 paper) + if (conflictsRestarts > LOWER_BOUND_FOR_BLOCKING_RESTART + && lbdQueue.isvalid() && trail.size() > R * trailQueue.getavg()) { + lbdQueue.fastclear(); + nbstopsrestarts++; + if (!blocked) { + lastblockatrestart = starts; + nbstopsrestartssame++; + blocked = true; + } + } + + learnt_clause.clear(); + selectors.clear(); + analyze(confl, learnt_clause, selectors, backtrack_level, nblevels, + szWoutSelectors); + + lbdQueue.push(nblevels); + sumLBD += nblevels; + + cancelUntil(backtrack_level); + + if (certifiedUNSAT) { + for (int i = 0; i < learnt_clause.size(); i++) + fprintf(certifiedOutput, "%i ", + (var(learnt_clause[i]) + 1) + * (-2 * sign(learnt_clause[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + + if (learnt_clause.size() == 1) { + uncheckedEnqueue(learnt_clause[0]); + nbUn++; + } else { + CRef cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(nblevels); + ca[cr].setSizeWithoutSelectors(szWoutSelectors); + if (nblevels <= 2) + nbDL2++; // stats + if (ca[cr].size() == 2) + nbBin++; // stats + learnts.push(cr); + attachClause(cr); + + claBumpActivity(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + } + varDecayActivity(); + claDecayActivity(); + + } else { + + // Our dynamic restart, see the SAT09 competition compagnion paper + if ((conflictsRestarts && lbdQueue.isvalid() + && lbdQueue.getavg() * K > sumLBD / conflictsRestarts) + || (pstop && *pstop)) { + lbdQueue.fastclear(); + progress_estimate = progressEstimate(); + int bt = 0; + if (incremental) { // DO NOT BACKTRACK UNTIL 0.. USELESS + bt = (decisionLevel() < assumptions.size()) ? + decisionLevel() : + assumptions.size(); + } + cancelUntil(bt); + return l_Undef; + } + + // Simplify the set of problem clauses: + if (decisionLevel() == 0 && !simplify()) { + return l_False; + } + // Perform clause database reduction ! + if (conflicts >= curRestart * nbclausesbeforereduce) { + + assert(learnts.size() > 0); + curRestart = (conflicts / nbclausesbeforereduce) + 1; + reduceDB(); + nbclausesbeforereduce += incReduceDB; + } + + Lit next = lit_Undef; + while (decisionLevel() < assumptions.size()) { + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if (value(p) == l_True) { + // Dummy decision level: + newDecisionLevel(); + } else if (value(p) == l_False) { + analyzeFinal(~p, conflict); + return l_False; + } else { + next = p; + break; + } + } + + if (next == lit_Undef) { + // New variable decision: + decisions++; + next = pickBranchLit(); + + if (next == lit_Undef) { + // printf("c last restart ## conflicts : %d %d + // \n",conflictC,decisionLevel()); + // Model found: + return l_True; + } + } + + // Increase decision level and enqueue 'next' + newDecisionLevel(); + uncheckedEnqueue(next); + } + } +} + +inline double Solver::progressEstimate() const +{ + double progress = 0; + double F = 1.0 / nVars(); + + for (int i = 0; i <= decisionLevel(); i++) { + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + +inline void Solver::printIncrementalStats() +{ + + printf("c---------- Glucose Stats -------------------------\n"); + printf("c restarts : %lld\n", starts); + printf("c nb ReduceDB : %lld\n", nbReduceDB); + printf("c nb removed Clauses : %lld\n", nbRemovedClauses); + printf("c nb learnts DL2 : %lld\n", nbDL2); + printf("c nb learnts size 2 : %lld\n", nbBin); + printf("c nb learnts size 1 : %lld\n", nbUn); + + printf("c conflicts : %lld\n", conflicts); + printf("c decisions : %lld\n", decisions); + printf("c propagations : %lld\n", propagations); + + printf("c SAT Calls : %d in %g seconds\n", nbSatCalls, totalTime4Sat); + printf("c UNSAT Calls : %d in %g seconds\n", nbUnsatCalls, totalTime4Unsat); + printf("c--------------------------------------------------\n"); +} + +// NOTE: assumptions passed in member-variable 'assumptions'. +inline lbool Solver::solve_() +{ + + if (incremental && certifiedUNSAT) { + printf("Can not use incremental and certified unsat in the same time\n"); + exit(-1); + } + model.clear(); + conflict.clear(); + if (!ok) + return l_False; + double curTime = cpuTime(); + + solves++; + + lbool status = l_Undef; + if (!incremental && verbosity >= 1) { + printf("c ========================================[ MAGIC CONSTANTS " + "]==============================================\n"); + printf("c | Constants are supposed to work well together :-) " + " |\n"); + printf("c | however, if you find better choices, please let us known... " + " |\n"); + printf("c " + "|--------------------------------------------------------------------------" + "-----------------------------|\n"); + printf("c | | | " + " |\n"); + printf("c | - Restarts: | - Reduce Clause DB: | - " + "Minimize Asserting: |\n"); + printf("c | * LBD Queue : %6d | * First : %6d | * size " + "< %3d |\n", + lbdQueue.maxSize(), nbclausesbeforereduce, lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * Inc : %6d | * lbd " + "< %3d |\n", + trailQueue.maxSize(), incReduceDB, lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Special : %6d | " + " |\n", + K, specialIncReduceDB); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | " + " |\n", + R, lbLBDFrozenClause); + printf("c | | | " + " |\n"); + printf("c ==================================[ Search Statistics (every %6d " + "conflicts) ]=========================\n", + verbEveryConflicts); + printf("c | " + " |\n"); + + printf("c | RESTARTS | ORIGINAL | " + "LEARNT | Progress |\n"); + printf("c | NB Blocked Avg Cfc | Vars Clauses Literals | Red " + "Learnts LBD2 Removed | |\n"); + printf("c " + "===========================================================================" + "==============================\n"); + } + + // Search: + int curr_restarts = 0; + while (status == l_Undef) { + status = search(0); // the parameter is useless in glucose, kept to allow modifications + if (!withinBudget() || terminate_search_early || (pstop && *pstop)) + break; + if (nRuntimeLimit && Abc_Clock() > nRuntimeLimit) + break; + curr_restarts++; + } + + if (!incremental && verbosity >= 1) + printf("c " + "===========================================================================" + "==============================\n"); + + if (certifiedUNSAT) { // Want certified output + if (status == l_False) + fprintf(certifiedOutput, "0\n"); + fclose(certifiedOutput); + } + + if (status == l_True) { + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) + model[i] = value(i); + } else if (status == l_False && conflict.size() == 0) + ok = false; + + cancelUntil(0); + + double finalTime = cpuTime(); + if (status == l_True) { + nbSatCalls++; + totalTime4Sat += (finalTime - curTime); + } + if (status == l_False) { + nbUnsatCalls++; + totalTime4Unsat += (finalTime - curTime); + } + + // ABC callback + if (pCnfFunc + && !terminate_search_early) { // hack to avoid calling callback twise if the solver was terminated early + int* pCex = NULL; + int message = (status == l_True ? 1 : status == l_False ? 0 : -1); + if (status == l_True) { + pCex = new int[nVars()]; + for (int i = 0; i < nVars(); i++) + pCex[i] = (model[i] == l_True); + } + + int callback_result = pCnfFunc(pCnfMan, message, pCex); + assert(callback_result == 0); + } else if (pCnfFunc) + terminate_search_early = false; // for next run + + return status; +} + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec& map, Var& max) +{ + if (map.size() <= x || map[x] == -1) { + map.growTo(x + 1, -1); + map[x] = max++; + } + return map[x]; +} + +inline void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) +{ + if (satisfied(c)) + return; + + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max) + 1); + fprintf(f, "0\n"); +} + +inline void Solver::toDimacs(const char* file, const vec& assumps) +{ + FILE* f = fopen(file, "wr"); + if (f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + +inline void Solver::toDimacs(FILE* f, const vec& assumps) +{ + // Handle case when solver is in contradictory state: + if (!ok) { + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; + } + + vec map; + Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int i, cnt = 0; + for (i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) + cnt++; + + for (i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) { + Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumptions.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for (i = 0; i < assumptions.size(); i++) { + assert(value(assumptions[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumptions[i]) ? "-" : "", + mapVar(var(assumptions[i]), map, max) + 1); + } + + for (i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + if (verbosity > 0) + printf("Wrote %d clauses with %d variables.\n", cnt, max); +} + +//================================================================================================= +// Garbage Collection methods: + +inline void Solver::relocAll(ClauseAllocator& to) +{ + int v, s, i, j; + // All watchers: + // + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watchesBin.cleanAll(); + for (v = 0; v < nVars(); v++) + for (s = 0; s < 2; s++) { + Lit p = mkLit(v, s != 0); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec& ws = watches[p]; + for (j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec& ws2 = watchesBin[p]; + for (j = 0; j < ws2.size(); j++) + ca.reloc(ws2[j].cref, to); + } + + // All reasons: + // + for (i = 0; i < trail.size(); i++) { + Var v = var(trail[i]); + + if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + for (i = 0; i < learnts.size(); i++) + ca.reloc(learnts[i], to); + + // All original: + // + for (i = 0; i < clauses.size(); i++) + ca.reloc(clauses[i], to); +} + +inline void Solver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. + // This is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +inline void Solver::reset() +{ + // Reset everything + ok = true; + K = (double) opt_K; + R = (double) opt_R; + firstReduceDB = opt_first_reduce_db; + var_decay = (double) opt_var_decay; + // max_var_decay = opt_max_var_decay; + solves = starts = decisions = propagations = conflicts = conflictsRestarts = 0; + curRestart = 1; + cla_inc = var_inc = 1; + watches.clear(false); // We don't free the memory, new calls should be of the same size order. + watchesBin.clear(false); + // unaryWatches.clear(false); + qhead = 0; + simpDB_assigns = -1; + simpDB_props = 0; + order_heap.clear(false); + progress_estimate = 0; + // lastLearntClause = CRef_Undef; + conflict_budget = -1; + propagation_budget = -1; + nbVarsInitialFormula = INT32_MAX; + totalTime4Sat = 0.; + totalTime4Unsat = 0.; + nbSatCalls = nbUnsatCalls = 0; + MYFLAG = 0; + lbdQueue.clear(false); + lbdQueue.initSize(sizeLBDQueue); + trailQueue.clear(false); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + // stats.clear(); + // stats.growTo(coreStatsSize, 0); + clauses.clear(false); + learnts.clear(false); + // permanentLearnts.clear(false); + // unaryWatchedClauses.clear(false); + model.clear(false); + conflict.clear(false); + activity.clear(false); + assigns.clear(false); + polarity.clear(false); + // forceUNSAT.clear(false); + decision.clear(false); + trail.clear(false); + nbpos.clear(false); + trail_lim.clear(false); + vardata.clear(false); + assumptions.clear(false); + permDiff.clear(false); + lastDecisionLevel.clear(false); + ca.clear(); + seen.clear(false); + analyze_stack.clear(false); + analyze_toclear.clear(false); + add_tmp.clear(false); + assumptionPositions.clear(false); + initialPositions.clear(false); +} + +} /* namespace Gluco */ + +ABC_NAMESPACE_IMPL_END + +/*** AbcGlucose.cpp */ + +/**CFile**************************************************************** + + FileName [AbcGlucose.cpp] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver Glucose 3.0 by Gilles Audemard and Laurent Simon.] + + Synopsis [Interface to Glucose.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 6, 2017.] + + Revision [$Id: AbcGlucose.cpp,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "abc/AbcGlucose.h" +#include "abc/Dimacs.h" +#include "abc/SimpSolver.h" +#include "abc/abc_global.h" +#include "abc/system.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define USE_SIMP_SOLVER 1 + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#ifdef USE_SIMP_SOLVER + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline Gluco::SimpSolver* glucose_solver_start() +{ + Gluco::SimpSolver* S = new Gluco::SimpSolver; + S->setIncrementalMode(); + return S; +} + +inline void glucose_solver_stop(Gluco::SimpSolver* S) +{ + delete S; +} + +inline void glucose_solver_reset(Gluco::SimpSolver* S) +{ + S->reset(); +} + +inline int glucose_solver_addclause(Gluco::SimpSolver* S, int* plits, int nlits) +{ + Gluco::vec lits; + for (int i = 0; i < nlits; i++, plits++) { + // note: Glucose uses the same var->lit conventiaon as ABC + while ((*plits) / 2 >= S->nVars()) + S->newVar(); + assert((*plits) / 2 + < S->nVars()); // NOTE: since we explicitely use new function bmc_add_var + Gluco::Lit p; + p.x = *plits; + lits.push(p); + } + return S->addClause(lits); // returns 0 if the problem is UNSAT +} + +inline void glucose_solver_setcallback(Gluco::SimpSolver* S, void* pman, + int (*pfunc)(void*, int, int*)) +{ + S->pCnfMan = pman; + S->pCnfFunc = pfunc; + S->nCallConfl = 1000; +} + +inline int glucose_solver_solve(Gluco::SimpSolver* S, int* plits, int nlits) +{ + Gluco::vec lits; + for (int i = 0; i < nlits; i++, plits++) { + Gluco::Lit p; + p.x = *plits; + lits.push(p); + } + Gluco::lbool res = S->solveLimited(lits, 0); + return (res == Gluco::l_True ? 1 : res == Gluco::l_False ? -1 : 0); +} + +inline int glucose_solver_addvar(Gluco::SimpSolver* S) +{ + S->newVar(); + return S->nVars() - 1; +} + +inline int glucose_solver_read_cex_varvalue(Gluco::SimpSolver* S, int ivar) +{ + return S->model[ivar] == Gluco::l_True; +} + +inline void glucose_solver_setstop(Gluco::SimpSolver* S, int* pstop) +{ + S->pstop = pstop; +} + +/**Function************************************************************* + + Synopsis [Wrapper APIs to calling from ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline bmcg_sat_solver* bmcg_sat_solver_start() +{ + return (bmcg_sat_solver*) glucose_solver_start(); +} +inline void bmcg_sat_solver_stop(bmcg_sat_solver* s) +{ + glucose_solver_stop((Gluco::SimpSolver*) s); +} +inline void bmcg_sat_solver_reset(bmcg_sat_solver* s) +{ + glucose_solver_reset((Gluco::SimpSolver*) s); +} + +inline int bmcg_sat_solver_addclause(bmcg_sat_solver* s, int* plits, int nlits) +{ + return glucose_solver_addclause((Gluco::SimpSolver*) s, plits, nlits); +} + +inline void bmcg_sat_solver_setcallback(bmcg_sat_solver* s, void* pman, + int (*pfunc)(void*, int, int*)) +{ + glucose_solver_setcallback((Gluco::SimpSolver*) s, pman, pfunc); +} + +inline int bmcg_sat_solver_solve(bmcg_sat_solver* s, int* plits, int nlits) +{ + return glucose_solver_solve((Gluco::SimpSolver*) s, plits, nlits); +} + +inline int bmcg_sat_solver_final(bmcg_sat_solver* s, int** ppArray) +{ + *ppArray = (int*) (Gluco::Lit*) ((Gluco::SimpSolver*) s)->conflict; + return ((Gluco::SimpSolver*) s)->conflict.size(); +} + +inline int bmcg_sat_solver_addvar(bmcg_sat_solver* s) +{ + return glucose_solver_addvar((Gluco::SimpSolver*) s); +} + +inline void bmcg_sat_solver_set_nvars(bmcg_sat_solver* s, int nvars) +{ + int i; + for (i = bmcg_sat_solver_varnum(s); i < nvars; i++) + bmcg_sat_solver_addvar(s); +} + +inline int bmcg_sat_solver_eliminate(bmcg_sat_solver* s, int turn_off_elim) +{ + // return 1; + return ((Gluco::SimpSolver*) s)->eliminate(turn_off_elim != 0); +} + +inline int bmcg_sat_solver_var_is_elim(bmcg_sat_solver* s, int v) +{ + // return 0; + return ((Gluco::SimpSolver*) s)->isEliminated(v); +} + +inline void bmcg_sat_solver_var_set_frozen(bmcg_sat_solver* s, int v, int freeze) +{ + ((Gluco::SimpSolver*) s)->setFrozen(v, freeze != 0); +} + +inline int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s) +{ + // return 0; + return ((Gluco::SimpSolver*) s)->eliminated_vars; +} + +inline int bmcg_sat_solver_read_cex_varvalue(bmcg_sat_solver* s, int ivar) +{ + return glucose_solver_read_cex_varvalue((Gluco::SimpSolver*) s, ivar); +} + +inline void bmcg_sat_solver_set_stop(bmcg_sat_solver* s, int* pstop) +{ + glucose_solver_setstop((Gluco::SimpSolver*) s, pstop); +} + +inline abctime bmcg_sat_solver_set_runtime_limit(bmcg_sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = ((Gluco::SimpSolver*) s)->nRuntimeLimit; + ((Gluco::SimpSolver*) s)->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +inline void bmcg_sat_solver_set_conflict_budget(bmcg_sat_solver* s, int Limit) +{ + if (Limit > 0) + ((Gluco::SimpSolver*) s)->setConfBudget((int64_t) Limit); + else + ((Gluco::SimpSolver*) s)->budgetOff(); +} + +inline int bmcg_sat_solver_varnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*) s)->nVars(); +} +inline int bmcg_sat_solver_clausenum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*) s)->nClauses(); +} +inline int bmcg_sat_solver_learntnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*) s)->nLearnts(); +} +inline int bmcg_sat_solver_conflictnum(bmcg_sat_solver* s) +{ + return ((Gluco::SimpSolver*) s)->conflicts; +} + +inline int bmcg_sat_solver_minimize_assumptions(bmcg_sat_solver* s, int* plits, int nlits, int pivot) +{ + Gluco::vec* array = &((Gluco::SimpSolver*) s)->user_vec; + int i, nlitsL, nlitsR, nresL, nresR, status; + assert(pivot >= 0); + // assert( nlits - pivot >= 2 ); + assert(nlits - pivot >= 1); + if (nlits - pivot == 1) { + // since the problem is UNSAT, we try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + status = bmcg_sat_solver_solve(s, plits, pivot); + return status != GLUCOSE_UNSAT; // return 1 if the problem is not UNSAT + } + assert(nlits - pivot >= 2); + nlitsL = (nlits - pivot) / 2; + nlitsR = (nlits - pivot) - nlitsL; + assert(nlitsL + nlitsR == nlits - pivot); + // solve with these assumptions + status = bmcg_sat_solver_solve(s, plits, pivot + nlitsL); + if (status == GLUCOSE_UNSAT) // these are enough + return bmcg_sat_solver_minimize_assumptions(s, plits, pivot + nlitsL, pivot); + // these are not enough + // solve for the right lits + // nResL = nLitsR == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nLitsL, nLitsR, nConfLimit ); + nresL = nlitsR == 1 ? 1 : + bmcg_sat_solver_minimize_assumptions(s, plits, nlits, pivot + nlitsL); + // swap literals + array->clear(); + for (i = 0; i < nlitsL; i++) + array->push(plits[pivot + i]); + for (i = 0; i < nresL; i++) + plits[pivot + i] = plits[pivot + nlitsL + i]; + for (i = 0; i < nlitsL; i++) + plits[pivot + nresL + i] = (*array)[i]; + // solve with these assumptions + status = bmcg_sat_solver_solve(s, plits, pivot + nresL); + if (status == GLUCOSE_UNSAT) // these are enough + return nresL; + // solve for the left lits + // nResR = nLitsL == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nResL, nLitsL, nConfLimit ); + nresR = nlitsL == 1 ? 1 : + bmcg_sat_solver_minimize_assumptions(s, plits, pivot + nresL + nlitsL, + pivot + nresL); + return nresL + nresR; +} + +inline int bmcg_sat_solver_add_and(bmcg_sat_solver* s, int iVar, int iVar0, int iVar1, int fCompl0, + int fCompl1, int fCompl) +{ + int Lits[3]; + + Lits[0] = Abc_Var2Lit(iVar, !fCompl); + Lits[1] = Abc_Var2Lit(iVar0, fCompl0); + if (!bmcg_sat_solver_addclause(s, Lits, 2)) + return 0; + + Lits[0] = Abc_Var2Lit(iVar, !fCompl); + Lits[1] = Abc_Var2Lit(iVar1, fCompl1); + if (!bmcg_sat_solver_addclause(s, Lits, 2)) + return 0; + + Lits[0] = Abc_Var2Lit(iVar, fCompl); + Lits[1] = Abc_Var2Lit(iVar0, !fCompl0); + Lits[2] = Abc_Var2Lit(iVar1, !fCompl1); + if (!bmcg_sat_solver_addclause(s, Lits, 3)) + return 0; + + return 1; +} + +#else + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline Gluco::Solver* glucose_solver_start() +{ + Gluco::Solver* S = new Gluco::Solver; + S->setIncrementalMode(); + return S; +} + +inline void glucose_solver_stop(Gluco::Solver* S) +{ + delete S; +} + +inline int glucose_solver_addclause(Gluco::Solver* S, int* plits, int nlits) +{ + Gluco::vec lits; + for (int i = 0; i < nlits; i++, plits++) { + // note: Glucose uses the same var->lit conventiaon as ABC + while ((*plits) / 2 >= S->nVars()) + S->newVar(); + assert((*plits) / 2 + < S->nVars()); // NOTE: since we explicitely use new function bmc_add_var + Gluco::Lit p; + p.x = *plits; + lits.push(p); + } + return S->addClause(lits); // returns 0 if the problem is UNSAT +} + +inline void glucose_solver_setcallback(Gluco::Solver* S, void* pman, int (*pfunc)(void*, int, int*)) +{ + S->pCnfMan = pman; + S->pCnfFunc = pfunc; + S->nCallConfl = 1000; +} + +inline int glucose_solver_solve(Gluco::Solver* S, int* plits, int nlits) +{ + vec lits; + for (int i = 0; i < nlits; i++, plits++) { + Lit p; + p.x = *plits; + lits.push(p); + } + Gluco::lbool res = S->solveLimited(lits); + return (res == l_True ? 1 : res == l_False ? -1 : 0); +} + +inline int glucose_solver_addvar(Gluco::Solver* S) +{ + S->newVar(); + return S->nVars() - 1; +} + +inline int glucose_solver_read_cex_varvalue(Gluco::Solver* S, int ivar) +{ + return S->model[ivar] == l_True; +} + +inline void glucose_solver_setstop(Gluco::Solver* S, int* pstop) +{ + S->pstop = pstop; +} + +/**Function************************************************************* + + Synopsis [Wrapper APIs to calling from ABC.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline bmcg_sat_solver* bmcg_sat_solver_start() +{ + return (bmcg_sat_solver*) glucose_solver_start(); +} +inline void bmcg_sat_solver_stop(bmcg_sat_solver* s) +{ + glucose_solver_stop((Gluco::Solver*) s); +} + +inline int bmcg_sat_solver_addclause(bmcg_sat_solver* s, int* plits, int nlits) +{ + return glucose_solver_addclause((Gluco::Solver*) s, plits, nlits); +} + +inline void bmcg_sat_solver_setcallback(bmcg_sat_solver* s, void* pman, + int (*pfunc)(void*, int, int*)) +{ + glucose_solver_setcallback((Gluco::Solver*) s, pman, pfunc); +} + +inline int bmcg_sat_solver_solve(bmcg_sat_solver* s, int* plits, int nlits) +{ + return glucose_solver_solve((Gluco::Solver*) s, plits, nlits); +} + +inline int bmcg_sat_solver_final(bmcg_sat_solver* s, int** ppArray) +{ + *ppArray = (int*) (Lit*) ((Gluco::Solver*) s)->conflict; + return ((Gluco::Solver*) s)->conflict.size(); +} + +inline int bmcg_sat_solver_addvar(bmcg_sat_solver* s) +{ + return glucose_solver_addvar((Gluco::Solver*) s); +} + +inline void bmcg_sat_solver_set_nvars(bmcg_sat_solver* s, int nvars) +{ + int i; + for (i = bmcg_sat_solver_varnum(s); i < nvars; i++) + bmcg_sat_solver_addvar(s); +} + +inline int bmcg_sat_solver_eliminate(bmcg_sat_solver* s, int turn_off_elim) +{ + return 1; + // return ((Gluco::SimpSolver*)s)->eliminate(turn_off_elim != 0); +} + +inline int bmcg_sat_solver_var_is_elim(bmcg_sat_solver* s, int v) +{ + return 0; + // return ((Gluco::SimpSolver*)s)->isEliminated(v); +} + +inline void bmcg_sat_solver_var_set_frozen(bmcg_sat_solver* s, int v, int freeze) +{ + // ((Gluco::SimpSolver*)s)->setFrozen(v, freeze); +} + +inline int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s) +{ + return 0; + // return ((Gluco::SimpSolver*)s)->eliminated_vars; +} + +inline int bmcg_sat_solver_read_cex_varvalue(bmcg_sat_solver* s, int ivar) +{ + return glucose_solver_read_cex_varvalue((Gluco::Solver*) s, ivar); +} + +inline void bmcg_sat_solver_set_stop(bmcg_sat_solver* s, int* pstop) +{ + glucose_solver_setstop((Gluco::Solver*) s, pstop); +} + +inline abctime bmcg_sat_solver_set_runtime_limit(bmcg_sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = ((Gluco::Solver*) s)->nRuntimeLimit; + ((Gluco::Solver*) s)->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +inline void bmcg_sat_solver_set_conflict_budget(bmcg_sat_solver* s, int Limit) +{ + if (Limit > 0) + ((Gluco::Solver*) s)->setConfBudget((int64_t) Limit); + else + ((Gluco::Solver*) s)->budgetOff(); +} + +inline int bmcg_sat_solver_varnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*) s)->nVars(); +} +inline int bmcg_sat_solver_clausenum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*) s)->nClauses(); +} +inline int bmcg_sat_solver_learntnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*) s)->nLearnts(); +} +inline int bmcg_sat_solver_conflictnum(bmcg_sat_solver* s) +{ + return ((Gluco::Solver*) s)->conflicts; +} + +inline int bmcg_sat_solver_minimize_assumptions(bmcg_sat_solver* s, int* plits, int nlits, int pivot) +{ + vec* array = &((Gluco::Solver*) s)->user_vec; + int i, nlitsL, nlitsR, nresL, nresR, status; + assert(pivot >= 0); + // assert( nlits - pivot >= 2 ); + assert(nlits - pivot >= 1); + if (nlits - pivot == 1) { + // since the problem is UNSAT, we try to solve it without assuming the last literal + // if the result is UNSAT, the last literal can be dropped; otherwise, it is needed + status = bmcg_sat_solver_solve(s, plits, pivot); + return status != GLUCOSE_UNSAT; // return 1 if the problem is not UNSAT + } + assert(nlits - pivot >= 2); + nlitsL = (nlits - pivot) / 2; + nlitsR = (nlits - pivot) - nlitsL; + assert(nlitsL + nlitsR == nlits - pivot); + // solve with these assumptions + status = bmcg_sat_solver_solve(s, plits, pivot + nlitsL); + if (status == GLUCOSE_UNSAT) // these are enough + return bmcg_sat_solver_minimize_assumptions(s, plits, pivot + nlitsL, pivot); + // these are not enough + // solve for the right lits + // nResL = nLitsR == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nLitsL, nLitsR, nConfLimit ); + nresL = nlitsR == 1 ? 1 : + bmcg_sat_solver_minimize_assumptions(s, plits, nlits, pivot + nlitsL); + // swap literals + array->clear(); + for (i = 0; i < nlitsL; i++) + array->push(plits[pivot + i]); + for (i = 0; i < nresL; i++) + plits[pivot + i] = plits[pivot + nlitsL + i]; + for (i = 0; i < nlitsL; i++) + plits[pivot + nresL + i] = (*array)[i]; + // solve with these assumptions + status = bmcg_sat_solver_solve(s, plits, pivot + nresL); + if (status == GLUCOSE_UNSAT) // these are enough + return nresL; + // solve for the left lits + // nResR = nLitsL == 1 ? 1 : sat_solver_minimize_assumptions( s, pLits + nResL, nLitsL, nConfLimit ); + nresR = nlitsL == 1 ? 1 : + bmcg_sat_solver_minimize_assumptions(s, plits, pivot + nresL + nlitsL, + pivot + nresL); + return nresL + nresR; +} + +#endif + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +inline void glucose_print_stats(Gluco::SimpSolver& s, abctime clk) +{ + double cpu_time = (double) (unsigned) clk / CLOCKS_PER_SEC; + printf("c restarts : %d (%d conflicts on average)\n", (int) s.starts, + s.starts > 0 ? (int) (s.conflicts / s.starts) : 0); + printf("c blocked restarts : %d (multiple: %d) \n", (int) s.nbstopsrestarts, + (int) s.nbstopsrestartssame); + printf("c last block at restart : %d\n", (int) s.lastblockatrestart); + printf("c nb ReduceDB : %-12d\n", (int) s.nbReduceDB); + printf("c nb removed Clauses : %-12d\n", (int) s.nbRemovedClauses); + printf("c nb learnts DL2 : %-12d\n", (int) s.nbDL2); + printf("c nb learnts size 2 : %-12d\n", (int) s.nbBin); + printf("c nb learnts size 1 : %-12d\n", (int) s.nbUn); + printf("c conflicts : %-12d (%.0f /sec)\n", (int) s.conflicts, + s.conflicts / cpu_time); + printf("c decisions : %-12d (%4.2f %% random) (%.0f /sec)\n", (int) s.decisions, + (float) s.rnd_decisions * 100 / (float) s.decisions, s.decisions / cpu_time); + printf("c propagations : %-12d (%.0f /sec)\n", (int) s.propagations, + s.propagations / cpu_time); + printf("c conflict literals : %-12d (%4.2f %% deleted)\n", (int) s.tot_literals, + (s.max_literals - s.tot_literals) * 100 / (double) s.max_literals); + printf("c nb reduced Clauses : %-12d\n", (int) s.nbReducedClauses); + // printf("c CPU time : %.2f sec\n", cpu_time); +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END + +#undef SAT_USE_ANALYZE_FINAL +#undef L_IND +#undef L_ind +#undef L_LIT +#undef L_lit +#undef USE_SIMP_SOLVER diff --git a/lib/bill/bill/sat/solver/abc/AbcGlucose.h b/lib/bill/bill/sat/solver/abc/AbcGlucose.h new file mode 100644 index 0000000..cc53db4 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/AbcGlucose.h @@ -0,0 +1,107 @@ +/**CFile**************************************************************** + + FileName [AbcGlucose.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver Glucose 3.0 by Gilles Audemard and Laurent Simon.] + + Synopsis [Interface to Glucose.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - September 6, 2017.] + + Revision [$Id: AbcGlucose.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC_SAT_GLUCOSE_H_ +#define ABC_SAT_GLUCOSE_H_ + +#include "abc_global.h" + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +#define GLUCOSE_UNSAT -1 +#define GLUCOSE_SAT 1 +#define GLUCOSE_UNDEC 0 + + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Glucose_Pars_ Glucose_Pars; +struct Glucose_Pars_ { + int pre; // preprocessing + int verb; // verbosity + int cust; // customizable + int nConfls; // conflict limit (0 = no limit) +}; + +static inline Glucose_Pars Glucose_CreatePars(int p, int v, int c, int nConfls) +{ + Glucose_Pars pars; + pars.pre = p; + pars.verb = v; + pars.cust = c; + pars.nConfls = nConfls; + return pars; +} + +typedef void bmcg_sat_solver; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern bmcg_sat_solver * bmcg_sat_solver_start(); +extern void bmcg_sat_solver_stop( bmcg_sat_solver* s ); +extern void bmcg_sat_solver_reset( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_addclause( bmcg_sat_solver* s, int * plits, int nlits ); +extern void bmcg_sat_solver_setcallback( bmcg_sat_solver* s, void * pman, int(*pfunc)(void*, int, int*) ); +extern int bmcg_sat_solver_solve( bmcg_sat_solver* s, int * plits, int nlits ); +extern int bmcg_sat_solver_final( bmcg_sat_solver* s, int ** ppArray ); +extern int bmcg_sat_solver_addvar( bmcg_sat_solver* s ); +extern void bmcg_sat_solver_set_nvars( bmcg_sat_solver* s, int nvars ); +extern int bmcg_sat_solver_eliminate( bmcg_sat_solver* s, int turn_off_elim ); +extern int bmcg_sat_solver_var_is_elim( bmcg_sat_solver* s, int v ); +extern void bmcg_sat_solver_var_set_frozen( bmcg_sat_solver* s, int v, int freeze ); +extern int bmcg_sat_solver_elim_varnum(bmcg_sat_solver* s); +extern int bmcg_sat_solver_read_cex_varvalue( bmcg_sat_solver* s, int ); +extern void bmcg_sat_solver_set_stop( bmcg_sat_solver* s, int * pstop ); +extern abctime bmcg_sat_solver_set_runtime_limit( bmcg_sat_solver* s, abctime Limit ); +extern void bmcg_sat_solver_set_conflict_budget( bmcg_sat_solver* s, int Limit ); +extern int bmcg_sat_solver_varnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_clausenum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_learntnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_conflictnum( bmcg_sat_solver* s ); +extern int bmcg_sat_solver_minimize_assumptions( bmcg_sat_solver * s, int * plits, int nlits, int pivot ); +extern int bmcg_sat_solver_add_and( bmcg_sat_solver * s, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1, int fCompl ); + +extern void Glucose_SolveCnf( char * pFilename, Glucose_Pars * pPars ); + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/bill/bill/sat/solver/abc/Alg.h b/lib/bill/bill/sat/solver/abc/Alg.h new file mode 100644 index 0000000..9a93489 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Alg.h @@ -0,0 +1,88 @@ +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Alg_h +#define Abc_Glucose_Alg_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Alloc.h b/lib/bill/bill/sat/solver/abc/Alloc.h new file mode 100644 index 0000000..fb9f01c --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Alloc.h @@ -0,0 +1,136 @@ +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Abc_Glucose_Alloc_h +#define Abc_Glucose_Alloc_h + +#include "XAlloc.h" +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free_ (int size) { wasted_ += size; } + void clear () { sz = 0; wasted_=0; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + //printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + //printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/BoundedQueue.h b/lib/bill/bill/sat/solver/abc/BoundedQueue.h new file mode 100644 index 0000000..1df17be --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/BoundedQueue.h @@ -0,0 +1,114 @@ +/***********************************************************************************[BoundedQueue.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +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. +**************************************************************************************************/ + + +#ifndef Abc_BoundedQueue_h +#define Abc_BoundedQueue_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +//================================================================================================= + +namespace Gluco { + +template +class bqueue { + vec elems; + int first; + int last; + uint64_t sumofqueue; + int maxsize; + int queuesize; // Number of current elements (must be < maxsize !) + bool expComputed; + double exp,value; +public: + bqueue(void) : first(0), last(0), sumofqueue(0), maxsize(0), queuesize(0),expComputed(false) { } + + void initSize(int size) {growTo(size);exp = 2.0/(size+1);} // Init size of bounded size queue + + void push(T x) { + expComputed = false; + if (queuesize==maxsize) { + assert(last==first); // The queue is full, next value to enter will replace oldest one + sumofqueue -= elems[last]; + if ((++last) == maxsize) last = 0; + } else + queuesize++; + sumofqueue += x; + elems[first] = x; + if ((++first) == maxsize) {first = 0;last = 0;} + } + + T peek() { assert(queuesize>0); return elems[last]; } + void pop() {sumofqueue-=elems[last]; queuesize--; if ((++last) == maxsize) last = 0;} + + uint64_t getsum() const {return sumofqueue;} + unsigned int getavg() const {return (unsigned int)(sumofqueue/((uint64_t)queuesize));} + int maxSize() const {return maxsize;} + double getavgDouble() const { + double tmp = 0; + for(int i=0;i + +#include "SolverTypes.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// DIMACS Parser: + +template +static void readClause(B& in, Solver& S, vec& lits) { + int parsed_lit, var; + lits.clear(); + for (;;){ + parsed_lit = parseInt(in); + if (parsed_lit == 0) break; + var = abs(parsed_lit)-1; + while (var >= S.nVars()) S.newVar(); + lits.push( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) ); + } +} + +template +static void parse_DIMACS_main(B& in, Solver& S) { + vec lits; + int vars = 0; + int clauses = 0; + int cnt = 0; + for (;;){ + skipWhitespace(in); + if (*in == EOF) break; + else if (*in == 'p'){ + if (eagerMatch(in, "p cnf")){ + vars = parseInt(in); + clauses = parseInt(in); + // SATRACE'06 hack + // if (clauses > 4000000) + // S.eliminate(true); + }else{ + printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + } + } else if (*in == 'c' || *in == 'p') + skipLine(in); + else{ + cnt++; + readClause(in, S, lits); + S.addClause_(lits); } + } + if (vars != S.nVars()) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of variables.\n"); + if (cnt != clauses) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of clauses.\n"); +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Heap.h b/lib/bill/bill/sat/solver/abc/Heap.h new file mode 100644 index 0000000..d52ec11 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Heap.h @@ -0,0 +1,154 @@ +/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Heap_h +#define Abc_Glucose_Heap_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(vec& ns) { + int i; + for (i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + int i; + for (i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/IntTypes.h b/lib/bill/bill/sat/solver/abc/IntTypes.h new file mode 100644 index 0000000..101a366 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/IntTypes.h @@ -0,0 +1,49 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_IntTypes_h +#define Abc_Glucose_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +#define __STDC_LIMIT_MACROS +# include "pstdint.h" +//# include + +#endif + +#include + +#ifndef PRIu64 +#define PRIu64 "lu" +#define PRIi64 "ld" +#endif +//================================================================================================= + +#include "abc_namespaces.h" + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Map.h b/lib/bill/bill/sat/solver/abc/Map.h new file mode 100644 index 0000000..cd684f5 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Map.h @@ -0,0 +1,197 @@ +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Map_h +#define Abc_Glucose_Map_h + +#include "IntTypes.h" +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Default hash/equals functions +// + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Queue.h b/lib/bill/bill/sat/solver/abc/Queue.h new file mode 100644 index 0000000..4b18360 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Queue.h @@ -0,0 +1,73 @@ +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Queue_h +#define Abc_Glucose_Queue_h + +#include "Vec.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int j, i = 0; + for (j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/SimpSolver.h b/lib/bill/bill/sat/solver/abc/SimpSolver.h new file mode 100644 index 0000000..cbbfb64 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/SimpSolver.h @@ -0,0 +1,208 @@ +/************************************************************************************[SimpSolver.h] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_SimpSolver_h +#define Abc_Glucose_SimpSolver_h + +#include "Queue.h" +#include "Solver.h" +#include "abc_global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); + void addVar (Var v); + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + + // Memory managment: + // + virtual void reset(); + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + int parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + int eliminated_clauses; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + bool use_simplification; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr); + bool strengthenClause (CRef cr, Lit l); + void cleanUpClauses (); + bool implied (const vec& c); + void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +//inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline bool SimpSolver::isEliminated (Var v) const { return eliminated.size() > 0 ? eliminated[v] != 0 : 0; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +inline void SimpSolver::addVar(Var v) { while (v >= nVars()) newVar(); } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Solver.h b/lib/bill/bill/sat/solver/abc/Solver.h new file mode 100644 index 0000000..8238f1e --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Solver.h @@ -0,0 +1,493 @@ +/****************************************************************************************[Solver.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Solver_h +#define Abc_Glucose_Solver_h + +#include "Vec.h" +#include "Heap.h" +#include "Alg.h" +#include "SolverTypes.h" +#include "BoundedQueue.h" +#include "Constants.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Solver -- the main class: + +class Solver { +public: + + int SolverType; // ABC identifies Glucose's type as 0 + + // Constructor/Destructor: + // + Solver(); + virtual ~Solver(); + + // ABC callbacks + void * pCnfMan; // external CNF manager + int(*pCnfFunc)(void * p, int, int*); // external callback. messages: 0: unsat; 1: sat; -1: still working + int nCallConfl; // callback will be called every this number of conflicts + bool terminate_search_early; // used to stop the solver early if it as instructed by an external caller + int * pstop; // another callback + uint64_t nRuntimeLimit; // runtime limit + vec user_vec; + vec user_lits; + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + void addVar (Var v); // Add enough variables to make sure there is variable v. + + bool addClause (const vec& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + void printLit(Lit l); + void printClause(CRef c); + void printInitialClause(CRef c); + // Convenience versions of 'toDimacs()': + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () const; + + // Incremental mode + void setIncrementalMode(); + void initNbInitialVars(int nb); + void printIncrementalStats(); + + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void reset(); + virtual void garbageCollect(); // virtuality causes segfault for some reason + void checkGarbage(double gf); + void checkGarbage(); + + + + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + int verbosity; + int verbEveryConflicts; + int showModel; + // Constants For restarts + double K; + double R; + double sizeLBDQueue; + double sizeTrailQueue; + + // Constants for reduce DB + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB; + unsigned int lbLBDFrozenClause; + + // Constant for reducing clause + int lbSizeMinimizingClause; + unsigned int lbLBDMinimizingClause; + + double var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + // Certified UNSAT ( Thanks to Marijn Heule) + FILE* certifiedOutput; + bool certifiedUNSAT; + + + // Statistics: (read-only member variable) + // + int64_t nbRemovedClauses,nbReducedClauses,nbDL2,nbBin,nbUn,nbReduceDB,solves, starts, decisions, rnd_decisions, propagations, conflicts,conflictsRestarts,nbstopsrestarts,nbstopsrestartssame,lastblockatrestart; + int64_t dec_vars, clauses_literals, learnts_literals, max_literals, tot_literals; + +protected: + long curRestart; + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + + // Solver state: + // + int lastIndexRed; + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + double cla_inc; // Amount to bump next clause with. + vec activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + watchesBin; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + vec clauses; // List of problem clauses. + vec learnts; // List of learnt clauses. + + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec nbpos; + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap; // A priority queue of variables ordered with respect to the variable activity. + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + vec permDiff; // permDiff[var] contains the current conflict number... Used to count the number of LBD + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + vec lastDecisionLevel; +#endif + + ClauseAllocator ca; + + int nbclausesbeforereduce; // To know when it is time to reduce clause database + + bqueue trailQueue,lbdQueue; // Bounded queues for restarts. + float sumLBD; // used to compute the global average of LBD. Restarts... + int sumAssumptions; + + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + unsigned int MYFLAG; + + + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + + // Variables added for incremental mode + int incremental; // Use incremental SAT Solver + int nbVarsInitialFormula; // nb VAR in formula without assumptions (incremental SAT) + double totalTime4Sat,totalTime4Unsat; + int nbSatCalls,nbUnsatCalls; + vec assumptionPositions,initialPositions; + + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, vec & selectors, int& out_btlevel,unsigned int &nblevels,unsigned int &szWithoutSelectors); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int nof_conflicts); // Search for a given number of conflicts. + lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). + void reduceDB (); // Reduce the set of learnt clauses. + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap (); + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void removeClause (CRef cr); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + unsigned int computeLBD(const vec & lits,int end=-1); + unsigned int computeLBD(const Clause &c); + void minimisationWithBinaryResolution(vec &out_learnt); + + void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + int level (Var x) const; + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + inline bool isSelector(Var v) {return (incremental && v>nbVarsInitialFormula);} + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } +inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } +inline void Solver::varBumpActivity(Var v, double inc) { + if ( (activity[v] += inc) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) + order_heap.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) + ca[learnts[i]].activity() *= (float)1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect();} + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline bool Solver::locked (const Clause& c) const { + if(c.size()>2) + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; + return + (value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c) + || + (value(c[1]) == l_True && reason(var(c[1])) != CRef_Undef && ca.lea(reason(var(c[1]))) == &c); + } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) dec_vars++; + else if (!b && decision[v]) dec_vars--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + +inline void Solver::addVar(Var v) { while (v >= nVars()) newVar(); } + +//================================================================================================= +// Debug etc: + + +inline void Solver::printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void Solver::printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } +} + +inline void Solver::printInitialClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + if(!isSelector(var(c[i]))) { + printLit(c[i]); + printf(" "); + } + } +} + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/SolverTypes.h b/lib/bill/bill/sat/solver/abc/SolverTypes.h new file mode 100644 index 0000000..e8c1742 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/SolverTypes.h @@ -0,0 +1,442 @@ +/***********************************************************************************[SolverTypes.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Abc_Glucose_SolverTypes_h +#define Abc_Glucose_SolverTypes_h + +#include + +#include "IntTypes.h" +#include "Alg.h" +#include "Vec.h" +#include "Map.h" +#include "Alloc.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + + +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + + class lbool { + uint8_t value; + +public: + constexpr explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +constexpr auto l_True = Gluco::lbool((uint8_t)0); +constexpr auto l_False = Gluco::lbool((uint8_t)1); +constexpr auto l_Undef = Gluco::lbool((uint8_t)2); + +// #define l_True (Gluco::lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants. +// #define l_False (Gluco::lbool((uint8_t)1)) +// #define l_Undef (Gluco::lbool((uint8_t)2)) + + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause; +typedef RegionAllocator::Ref CRef; + +class Clause { + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned has_extra : 1; + unsigned reloced : 1; + unsigned lbd : 26; + unsigned canbedel : 1; + unsigned size : 32; + unsigned szWithoutSelectors : 32; + + } header; + union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, bool use_extra, bool learnt) { + header.mark = 0; + header.learnt = learnt; + header.has_extra = use_extra; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.canbedel = 1; + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.has_extra){ + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); } + } + +public: + void calcAbstraction() { + assert(header.has_extra); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + bool has_extra () const { return header.has_extra; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + float& activity () { assert(header.has_extra); return data[header.size].act; } + uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; } + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + void setLBD(int i) {header.lbd = i;} + // unsigned int& lbd () { return header.lbd; } + unsigned int lbd () const { return header.lbd; } + void setCanBeDel(bool b) {header.canbedel = b;} + bool canBeDel() {return header.canbedel;} + void setSizeWithoutSelectors (unsigned int n) {header.szWithoutSelectors = n; } + unsigned int sizeWithoutSelectors () const { return header.szWithoutSelectors; } + +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + +const CRef CRef_Undef = RegionAllocator::Ref_Undef; +class ClauseAllocator : public RegionAllocator +{ + static int clauseWord32Size(int size, bool has_extra){ + return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / sizeof(uint32_t); } + public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + bool use_extra = learnt | extra_clause_field; + + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), use_extra)); + new (lea(cid)) Clause(ps, use_extra, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free_(CRef cid) + { + Clause& c = operator[](cid); + RegionAllocator::free_(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()) { + to[cr].activity() = c.activity(); + to[cr].setLBD(c.lbd()); + to[cr].setSizeWithoutSelectors(c.sizeWithoutSelectors()); + to[cr].setCanBeDel(c.canBeDel()); + } + else if (to[cr].has_extra()) to[cr].calcAbstraction(); + } +}; + + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + + public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void clean (const Idx& idx); + void smudge (const Idx& idx){ + if (dirty[toInt(idx)] == 0){ + dirty[toInt(idx)] = 1; + dirties.push(idx); + } + } + + void clear(bool free = true){ + occs .clear(free); + dirty .clear(free); + dirties.clear(free); + } +}; + + +template +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + + public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.has_extra); assert(other.header.has_extra); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Sort.h b/lib/bill/bill/sat/solver/abc/Sort.h new file mode 100644 index 0000000..acbae7f --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Sort.h @@ -0,0 +1,101 @@ +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Sort_h +#define Abc_Glucose_Sort_h + +#include "Vec.h" + +//================================================================================================= +// Some sorting algorithms for vec's + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/Vec.h b/lib/bill/bill/sat/solver/abc/Vec.h new file mode 100644 index 0000000..6abd8de --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/Vec.h @@ -0,0 +1,135 @@ +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_Vec_h +#define Abc_Glucose_Vec_h + +#include +#include + +#include "IntTypes.h" +#include "XAlloc.h" +#include "abc_global.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but + // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not + // happen given the way capacities are calculated (below). Essentially, all capacities are + // even, but INT_MAX is odd. + + const T& last (void) const { return data[sz-1]; } + T& last (void) { return data[sz-1]; } + + // Vector interface: + const T& operator [] (int index) const { return data[index]; } + T& operator [] (int index) { return data[index]; } + + // Duplicatation (preferred instead): + void copyTo(vec& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/XAlloc.h b/lib/bill/bill/sat/solver/abc/XAlloc.h new file mode 100644 index 0000000..814cb1b --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/XAlloc.h @@ -0,0 +1,53 @@ +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Abc_Glucose_XAlloc_h +#define Abc_Glucose_XAlloc_h + +#include +#include +#include + +#include "abc_namespaces.h" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace Gluco { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else { + return mem; + } +} + +//================================================================================================= +} + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/abc_global.h b/lib/bill/bill/sat/solver/abc/abc_global.h new file mode 100644 index 0000000..8d94179 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/abc_global.h @@ -0,0 +1,416 @@ +/**CFile**************************************************************** + + FileName [abc_global.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Global declarations.] + + Synopsis [Global declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Jan 30, 2009.] + + Revision [$Id: abc_global.h,v 1.00 2009/01/30 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__util__abc_global_h +#define ABC__misc__util__abc_global_h + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include +#ifdef _WIN32 +#ifndef __MINGW32__ +//#define inline __inline // compatible with MS VS 6.0 +#pragma warning(disable : 4152) // warning C4152: nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4244) // warning C4244: '+=' : conversion from 'int ' to 'unsigned short ', possible loss of data +#pragma warning(disable : 4514) // warning C4514: 'Vec_StrPop' : unreferenced inline function has been removed +#pragma warning(disable : 4710) // warning C4710: function 'Vec_PtrGrow' not inlined +//#pragma warning( disable : 4273 ) +#endif +#endif + +#ifdef WIN32 + #ifdef WIN32_NO_DLL + #define ABC_DLLEXPORT + #define ABC_DLLIMPORT + #else + #define ABC_DLLEXPORT __declspec(dllexport) + #define ABC_DLLIMPORT __declspec(dllimport) + #endif +#else /* defined(WIN32) */ +#define ABC_DLLIMPORT +#endif /* defined(WIN32) */ + +#ifndef ABC_DLL +#define ABC_DLL ABC_DLLIMPORT +#endif + +#if !defined(___unused) +#if defined(__GNUC__) +#define ___unused __attribute__ ((__unused__)) +#else +#define ___unused +#endif +#endif + +/* +#ifdef __cplusplus +#error "C++ code" +#else +#error "C code" +#endif +*/ + +#include +#include +#include +#include +#include +#include + +// catch memory leaks in Visual Studio +#ifdef WIN32 + #ifdef _DEBUG + #define _CRTDBG_MAP_ALLOC + #include + #endif +#endif + +#ifdef _WIN32 +#include +#endif + +#include "abc_namespaces.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +/** + * Pointer difference type; replacement for ptrdiff_t. + * This is a signed integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRDIFF_T; +#elif defined(LIN64) +typedef long ABC_PTRDIFF_T; +#elif defined(NT64) +typedef long long ABC_PTRDIFF_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef int ABC_PTRDIFF_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * Unsigned integral type that can contain a pointer. + * This is an unsigned integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRUINT_T; +#elif defined(LIN64) +typedef unsigned long ABC_PTRUINT_T; +#elif defined(NT64) +typedef unsigned long long ABC_PTRUINT_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef unsigned int ABC_PTRUINT_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * Signed integral type that can contain a pointer. + * This is a signed integral type that is the same size as a pointer. + * NOTE: This type may be different sizes on different platforms. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_PTRINT_T; +#elif defined(LIN64) +typedef long ABC_PTRINT_T; +#elif defined(NT64) +typedef long long ABC_PTRINT_T; +#elif defined(NT) || defined(LIN) || defined(WIN32) +typedef int ABC_PTRINT_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * 64-bit signed integral type. + */ +#if defined(__ccdoc__) +typedef platform_dependent_type ABC_INT64_T; +#elif defined(LIN64) +typedef long ABC_INT64_T; +#elif defined(NT64) || defined(LIN) +typedef long long ABC_INT64_T; +#elif defined(WIN32) || defined(NT) +typedef signed __int64 ABC_INT64_T; +#else + #error unknown platform +#endif /* defined(PLATFORM) */ + +/** + * 64-bit unsigned integral type. + */ +typedef uint64_t ABC_UINT64_T; + +#ifdef LIN + #define ABC_CONST(number) number ## ULL +#else // LIN64 and windows + #define ABC_CONST(number) number +#endif + +typedef ABC_UINT64_T word; +typedef ABC_INT64_T iword; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_INFINITY (1000000000) + +#define ABC_SWAP(Type, a, b) { Type t = a; a = b; b = t; } + +#define ABC_PRT(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec\n", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTr(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec\r", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTn(a,t) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec ", 1.0*(t)/(CLOCKS_PER_SEC))) +#define ABC_PRTP(a,t,T) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%9.2f sec (%6.2f %%)\n", 1.0*(t)/(CLOCKS_PER_SEC), (T)? 100.0*(t)/(T) : 0.0)) +#define ABC_PRM(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB\n", 1.0*(f)/(1<<20))) +#define ABC_PRMr(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB\r", 1.0*(f)/(1<<20))) +#define ABC_PRMn(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB ", 1.0*(f)/(1<<20))) +#define ABC_PRMP(a,f,F) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB (%6.2f %%)\n", (1.0*(f)/(1<<20)), ((F)? 100.0*(f)/(F) : 0.0) ) ) + +#define ABC_ALLOC(type, num) ((type *) malloc(sizeof(type) * (num))) +#define ABC_CALLOC(type, num) ((type *) calloc((num), sizeof(type))) +#define ABC_FALLOC(type, num) ((type *) memset(malloc(sizeof(type) * (num)), 0xff, sizeof(type) * (num))) +#define ABC_FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) +#define ABC_REALLOC(type, obj, num) \ + ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \ + ((type *) malloc(sizeof(type) * (num)))) + +static inline int Abc_AbsInt( int a ) { return a < 0 ? -a : a; } +static inline int Abc_MaxInt( int a, int b ) { return a > b ? a : b; } +static inline int Abc_MinInt( int a, int b ) { return a < b ? a : b; } +static inline word Abc_MaxWord( word a, word b ) { return a > b ? a : b; } +static inline word Abc_MinWord( word a, word b ) { return a < b ? a : b; } +static inline float Abc_AbsFloat( float a ) { return a < 0 ? -a : a; } +static inline float Abc_MaxFloat( float a, float b ) { return a > b ? a : b; } +static inline float Abc_MinFloat( float a, float b ) { return a < b ? a : b; } +static inline double Abc_AbsDouble( double a ) { return a < 0 ? -a : a; } +static inline double Abc_MaxDouble( double a, double b ) { return a > b ? a : b; } +static inline double Abc_MinDouble( double a, double b ) { return a < b ? a : b; } + +static inline int Abc_Float2Int( float Val ) { union { int x; float y; } v; v.y = Val; return v.x; } +static inline float Abc_Int2Float( int Num ) { union { int x; float y; } v; v.x = Num; return v.y; } +static inline word Abc_Dbl2Word( double Dbl ) { union { word x; double y; } v; v.y = Dbl; return v.x; } +static inline double Abc_Word2Dbl( word Num ) { union { word x; double y; } v; v.x = Num; return v.y; } +static inline int Abc_Base2Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n >>= 1, r++ ) {}; return r; } +static inline int Abc_Base10Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n /= 10, r++ ) {}; return r; } +static inline int Abc_Base16Log( unsigned n ) { int r; if ( n < 2 ) return n; for ( r = 0, n--; n; n /= 16, r++ ) {}; return r; } +static inline char * Abc_UtilStrsav( char * s ) { return s ? strcpy(ABC_ALLOC(char, strlen(s)+1), s) : NULL; } +static inline int Abc_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } +static inline int Abc_Bit6WordNum( int nBits ) { return (nBits>>6) + ((nBits&63) > 0); } +static inline int Abc_TruthWordNum( int nVars ) { return nVars <= 5 ? 1 : (1 << (nVars - 5)); } +static inline int Abc_Truth6WordNum( int nVars ) { return nVars <= 6 ? 1 : (1 << (nVars - 6)); } +static inline int Abc_InfoHasBit( unsigned * p, int i ) { return (p[(i)>>5] & (1<<((i) & 31))) > 0; } +static inline void Abc_InfoSetBit( unsigned * p, int i ) { p[(i)>>5] |= (1<<((i) & 31)); } +static inline void Abc_InfoXorBit( unsigned * p, int i ) { p[(i)>>5] ^= (1<<((i) & 31)); } +static inline unsigned Abc_InfoMask( int nVar ) { return (~(unsigned)0) >> (32-nVar); } + +static inline int Abc_Var2Lit( int Var, int c ) { assert(Var >= 0 && !(c >> 1)); return Var + Var + c; } +static inline int Abc_Lit2Var( int Lit ) { assert(Lit >= 0); return Lit >> 1; } +static inline int Abc_LitIsCompl( int Lit ) { assert(Lit >= 0); return Lit & 1; } +static inline int Abc_LitNot( int Lit ) { assert(Lit >= 0); return Lit ^ 1; } +static inline int Abc_LitNotCond( int Lit, int c ) { assert(Lit >= 0); return Lit ^ (int)(c > 0); } +static inline int Abc_LitRegular( int Lit ) { assert(Lit >= 0); return Lit & ~01; } +static inline int Abc_Lit2LitV( int * pMap, int Lit ) { assert(Lit >= 0); return Abc_Var2Lit( pMap[Abc_Lit2Var(Lit)], Abc_LitIsCompl(Lit) ); } +static inline int Abc_Lit2LitL( int * pMap, int Lit ) { assert(Lit >= 0); return Abc_LitNotCond( pMap[Abc_Lit2Var(Lit)], Abc_LitIsCompl(Lit) ); } + +static inline int Abc_Ptr2Int( void * p ) { return (int)(ABC_PTRINT_T)p; } +static inline void * Abc_Int2Ptr( int i ) { return (void *)(ABC_PTRINT_T)i; } +static inline word Abc_Ptr2Wrd( void * p ) { return (word)(ABC_PTRUINT_T)p; } +static inline void * Abc_Wrd2Ptr( word i ) { return (void *)(ABC_PTRUINT_T)i; } + +static inline int Abc_Var2Lit2( int Var, int Att ) { assert(!(Att >> 2)); return (Var << 2) + Att; } +static inline int Abc_Lit2Var2( int Lit ) { assert(Lit >= 0); return Lit >> 2; } +static inline int Abc_Lit2Att2( int Lit ) { assert(Lit >= 0); return Lit & 3; } +static inline int Abc_Var2Lit3( int Var, int Att ) { assert(!(Att >> 3)); return (Var << 3) + Att; } +static inline int Abc_Lit2Var3( int Lit ) { assert(Lit >= 0); return Lit >> 3; } +static inline int Abc_Lit2Att3( int Lit ) { assert(Lit >= 0); return Lit & 7; } +static inline int Abc_Var2Lit4( int Var, int Att ) { assert(!(Att >> 4)); return (Var << 4) + Att; } +static inline int Abc_Lit2Var4( int Lit ) { assert(Lit >= 0); return Lit >> 4; } +static inline int Abc_Lit2Att4( int Lit ) { assert(Lit >= 0); return Lit & 15; } + +// time counting +typedef ABC_INT64_T abctime; +static inline abctime Abc_Clock() +{ +#if (defined(LIN) || defined(LIN64)) && !(__APPLE__ & __MACH__) && !defined(__MINGW32__) + struct timespec ts; +#ifdef _WIN32 + if ( clock_gettime(0, &ts) < 0 ) + return (abctime)-1; +#else + if ( clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) < 0 ) + return (abctime)-1; +#endif + abctime res = ((abctime) ts.tv_sec) * CLOCKS_PER_SEC; + res += (((abctime) ts.tv_nsec) * CLOCKS_PER_SEC) / 1000000000; + return res; +#else + return (abctime) clock(); +#endif +} + +// bridge communication +#define BRIDGE_NETLIST 106 +#define BRIDGE_ABS_NETLIST 107 +extern int Gia_ManToBridgeText( FILE * pFile, int Size, unsigned char * pBuffer ); +extern int Gia_ManToBridgeAbsNetlist( FILE * pFile, void * p, int pkg_type ); + +// string printing +extern char * vnsprintf(const char* format, va_list args); +extern char * nsprintf(const char* format, ...); + + +// misc printing procedures +enum Abc_VerbLevel +{ + ABC_PROMPT = -2, + ABC_ERROR = -1, + ABC_WARNING = 0, + ABC_STANDARD = 1, + ABC_VERBOSE = 2 +}; +static inline void Abc_Print( int level, const char * format, ... ) +{ + extern ABC_DLL int Abc_FrameIsBridgeMode(); + va_list args; + + if ( ! Abc_FrameIsBridgeMode() ){ + if ( level == ABC_ERROR ) + printf( "Error: " ); + else if ( level == ABC_WARNING ) + printf( "Warning: " ); + }else{ + if ( level == ABC_ERROR ) + Gia_ManToBridgeText( stdout, (int)strlen("Error: "), (unsigned char*)"Error: " ); + else if ( level == ABC_WARNING ) + Gia_ManToBridgeText( stdout, (int)strlen("Warning: "), (unsigned char*)"Warning: " ); + } + + va_start( args, format ); + if ( Abc_FrameIsBridgeMode() ) + { + char * tmp = vnsprintf( format, args ); + Gia_ManToBridgeText( stdout, (int)strlen(tmp), (unsigned char*)tmp ); + free( tmp ); + } + else + vprintf( format, args ); + va_end( args ); +} + +static inline void Abc_PrintInt( int i ) +{ + double v3 = (double)i/1000; + double v6 = (double)i/1000000; + + Abc_Print( 1, " " ); + + if ( i > -1000 && i < 1000 ) + Abc_Print( 1, " %4d", i ); + + else if ( v3 > -9.995 && v3 < 9.995 ) + Abc_Print( 1, "%4.2fk", v3 ); + else if ( v3 > -99.95 && v3 < 99.95 ) + Abc_Print( 1, "%4.1fk", v3 ); + else if ( v3 > -999.5 && v3 < 999.5 ) + Abc_Print( 1, "%4.0fk", v3 ); + + else if ( v6 > -9.995 && v6 < 9.995 ) + Abc_Print( 1, "%4.2fm", v6 ); + else if ( v6 > -99.95 && v6 < 99.95 ) + Abc_Print( 1, "%4.1fm", v6 ); + else if ( v6 > -999.5 && v6 < 999.5 ) + Abc_Print( 1, "%4.0fm", v6 ); +} + +static inline void Abc_PrintTime( int level, const char * pStr, abctime time ) +{ + ABC_PRT( pStr, time ); +} + +static inline void Abc_PrintTimeP( int level, const char * pStr, abctime time, abctime Time ) +{ + ABC_PRTP( pStr, time, Time ); +} + +static inline void Abc_PrintMemoryP( int level, const char * pStr, int mem, int Mem ) +{ + ABC_PRMP( pStr, mem, Mem ); +} + +// Returns the next prime >= p +static inline int Abc_PrimeCudd( unsigned int p ) +{ + int i,pn; + p--; + do { + p++; + if (p&1) + { + pn = 1; + i = 3; + while ((unsigned) (i * i) <= p) + { + if (p % i == 0) { + pn = 0; + break; + } + i += 2; + } + } + else + pn = 0; + } while (!pn); + return(p); + +} // end of Cudd_Prime + + +// sorting +extern void Abc_MergeSort( int * pInput, int nSize ); +extern int * Abc_MergeSortCost( int * pCosts, int nSize ); +extern void Abc_QuickSort1( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSort2( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSort3( word * pData, int nSize, int fDecrease ); +extern void Abc_QuickSortCostData( int * pCosts, int nSize, int fDecrease, word * pData, int * pResult ); +extern int * Abc_QuickSortCost( int * pCosts, int nSize, int fDecrease ); + + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/lib/bill/bill/sat/solver/abc/abc_namespaces.h b/lib/bill/bill/sat/solver/abc/abc_namespaces.h new file mode 100644 index 0000000..8cd681c --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/abc_namespaces.h @@ -0,0 +1,74 @@ +/**CFile**************************************************************** + + FileName [abc_namespaces.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Namespace logic.] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Nov 20, 2015.] + + Revision [] + +***********************************************************************/ + +#ifndef ABC__misc__util__abc_namespaces_h +#define ABC__misc__util__abc_namespaces_h + + +//////////////////////////////////////////////////////////////////////// +/// NAMESPACES /// +//////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +# ifdef ABC_NAMESPACE +# define ABC_NAMESPACE_HEADER_START namespace ABC_NAMESPACE { +# define ABC_NAMESPACE_HEADER_END } +# define ABC_NAMESPACE_CXX_HEADER_START ABC_NAMESPACE_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END ABC_NAMESPACE_HEADER_END +# define ABC_NAMESPACE_IMPL_START namespace ABC_NAMESPACE { +# define ABC_NAMESPACE_IMPL_END } +# define ABC_NAMESPACE_PREFIX ABC_NAMESPACE:: +# define ABC_NAMESPACE_USING_NAMESPACE using namespace ABC_NAMESPACE; +# else +# define ABC_NAMESPACE_HEADER_START extern "C" { +# define ABC_NAMESPACE_HEADER_END } +# define ABC_NAMESPACE_CXX_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END +# define ABC_NAMESPACE_IMPL_START +# define ABC_NAMESPACE_IMPL_END +# define ABC_NAMESPACE_PREFIX +# define ABC_NAMESPACE_USING_NAMESPACE +# endif // #ifdef ABC_NAMESPACE +#ifdef SATOKO_NAMESPACE + #define SATOKO_NAMESPACE_HEADER_START namespace SATOKO_NAMESPACE { + #define SATOKO_NAMESPACE_HEADER_END } + #define SATOKO_NAMESPACE_CXX_HEADER_START ABC_NAMESPACE_HEADER_START + #define SATOKO_NAMESPACE_CXX_HEADER_END ABC_NAMESPACE_HEADER_END + #define SATOKO_NAMESPACE_IMPL_START namespace SATOKO_NAMESPACE { + #define SATOKO_NAMESPACE_IMPL_END } + #define SATOKO_NAMESPACE_PREFIX SATOKO_NAMESPACE:: + #define SATOKO_NAMESPACE_USING_NAMESPACE using namespace SATOKO_NAMESPACE; +#endif +#else +# define ABC_NAMESPACE_HEADER_START +# define ABC_NAMESPACE_HEADER_END +# define ABC_NAMESPACE_CXX_HEADER_START +# define ABC_NAMESPACE_CXX_HEADER_END +# define ABC_NAMESPACE_IMPL_START +# define ABC_NAMESPACE_IMPL_END +# define ABC_NAMESPACE_PREFIX +# define ABC_NAMESPACE_USING_NAMESPACE +#endif // #ifdef __cplusplus + +#endif // #ifndef ABC__misc__util__abc_namespaces_h + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/lib/bill/bill/sat/solver/abc/pstdint.h b/lib/bill/bill/sat/solver/abc/pstdint.h new file mode 100644 index 0000000..989d016 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/pstdint.h @@ -0,0 +1,919 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2016 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.16.0 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is critically + * important for making several classes of applications portable + * including cryptography, hashing, variable length integer libraries + * and so on. But for most developers its likely useful just for + * programming sanity. + * + * The problem is that some compiler vendors chose to ignore the C99 + * standard and some older compilers have no opportunity to be updated. + * Because of this situation, simply including stdint.h in your code + * makes it unportable. + * + * So that's what this file is all about. It's an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. Even compilers + * that already come with stdint.h can use this file instead without + * any loss of functionality. A few things that should be noted about + * this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current version, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * PRINTF_UINTMAX_DEC_WIDTH + * PRINTF_UINT64_DEC_WIDTH + * PRINTF_UINT32_DEC_WIDTH + * PRINTF_UINT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * Florian Wobbe + * Christopher Sean Morrison + * Mikkel Fahnoe Jorgensen + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "l" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# else +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# if (UINT_MAX == UINT32_MAX) +# define PRINTF_INT32_MODIFIER "" +# else +# define PRINTF_INT32_MODIFIER "l" +# endif +# endif +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_UINT64_HEX_WIDTH +# define PRINTF_UINT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_UINT32_HEX_WIDTH +# define PRINTF_UINT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_UINT16_HEX_WIDTH +# define PRINTF_UINT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_UINT8_HEX_WIDTH +# define PRINTF_UINT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +# endif +# ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_UINTMAX_HEX_WIDTH +# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif +# ifndef PRINTF_UINTMAX_DEC_WIDTH +# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +/* + * I have no idea what is the truly correct thing to do on older Solaris. + * From some online discussions, this seems to be what is being + * recommended. For people who actually are developing on older Solaris, + * what I would like to know is, does this define all of the relevant + * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is + * considered optional. + */ + +#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)-1) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks) +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks) +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks) +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks) +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks) +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks) +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) && !defined(vxWorks) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif +#ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee has defined *int*_fast*_t types as well. This, + * of course, defies rationality -- you can't know what will be fast + * just from the type itself. Even for a given architecture, compatible + * implementations might have different performance characteristics. + * Developers are warned to stay away from these types when using this + * or any other stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# else +/* #error "This platform might not be supported yet" */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are + * not defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits) + +#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } + +#define X_SIZE_MAX ((size_t)-1) + +int main () { + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + +#define STR(v) #v +#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) +#ifdef INT64_MAX + Q(int64_t) +#endif + +#if UINT_MAX < X_SIZE_MAX + printf ("UINT_MAX < X_SIZE_MAX\n"); +#else + printf ("UINT_MAX >= X_SIZE_MAX\n"); +#endif + printf ("%" PRINTF_INT64_MODIFIER "u vs %" PRINTF_INT64_MODIFIER "u\n", UINT_MAX, X_SIZE_MAX); + + return EXIT_SUCCESS; +} + +#endif diff --git a/lib/bill/bill/sat/solver/abc/satClause.h b/lib/bill/bill/sat/solver/abc/satClause.h new file mode 100644 index 0000000..67d7f9d --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/satClause.h @@ -0,0 +1,476 @@ +/**CFile**************************************************************** + + FileName [satMem.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver.] + + Synopsis [Memory management.] + + Author [Alan Mishchenko ] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - January 1, 2004.] + + Revision [$Id: satMem.h,v 1.0 2004/01/01 1:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__sat__bsat__satMem_h +#define ABC__sat__bsat__satMem_h + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "abc_global.h" + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//#define LEARNT_MAX_START_DEFAULT 0 +#define LEARNT_MAX_START_DEFAULT 10000 +#define LEARNT_MAX_INCRE_DEFAULT 1000 +#define LEARNT_MAX_RATIO_DEFAULT 50 + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//================================================================================================= +// Clause datatype + minor functions: + +typedef struct clause_t clause; +struct clause_t +{ + unsigned lrn : 1; + unsigned mark : 1; + unsigned partA : 1; + unsigned lbd : 8; + unsigned size : 21; + lit lits[0]; +}; + +// learned clauses have "hidden" literal (c->lits[c->size]) to store clause ID + +// data-structure for logging entries +// memory is allocated in 2^nPageSize word-sized pages +// the first 'word' of each page are stores the word limit + +// although clause memory pieces are aligned to 64-bit words +// the integer clause handles are in terms of 32-bit unsigneds +// allowing for the first bit to be used for labeling 2-lit clauses + + +typedef struct Sat_Mem_t_ Sat_Mem_t; +struct Sat_Mem_t_ +{ + int nEntries[2]; // entry count + int BookMarkH[2]; // bookmarks for handles + int BookMarkE[2]; // bookmarks for entries + int iPage[2]; // current memory page + int nPageSize; // page log size in terms of ints + unsigned uPageMask; // page mask + unsigned uLearnedMask; // learned mask + int nPagesAlloc; // page count allocated + int ** pPages; // page pointers +}; + +static inline int Sat_MemLimit( int * p ) { return p[0]; } +static inline int Sat_MemIncLimit( int * p, int nInts ) { return p[0] += nInts; } +static inline void Sat_MemWriteLimit( int * p, int nInts ) { p[0] = nInts; } + +static inline int Sat_MemHandPage( Sat_Mem_t * p, cla h ) { return h >> p->nPageSize; } +static inline int Sat_MemHandShift( Sat_Mem_t * p, cla h ) { return h & p->uPageMask; } + +static inline int Sat_MemIntSize( int size, int lrn ) { return (size + 2 + lrn) & ~01; } +static inline int Sat_MemClauseSize( clause * p ) { return Sat_MemIntSize(p->size, p->lrn); } +static inline int Sat_MemClauseSize2( clause * p ) { return Sat_MemIntSize(p->size, 1); } + +//static inline clause * Sat_MemClause( Sat_Mem_t * p, int i, int k ) { assert(i <= p->iPage[i&1] && k <= Sat_MemLimit(p->pPages[i])); return (clause *)(p->pPages[i] + k ); } +static inline clause * Sat_MemClause( Sat_Mem_t * p, int i, int k ) { assert( k ); return (clause *)(p->pPages[i] + k); } +//static inline clause * Sat_MemClauseHand( Sat_Mem_t * p, cla h ) { assert(Sat_MemHandPage(p, h) <= p->iPage[(h & p->uLearnedMask) > 0]); assert(Sat_MemHandShift(p, h) >= 2 && Sat_MemHandShift(p, h) < (int)p->uLearnedMask); return Sat_MemClause( p, Sat_MemHandPage(p, h), Sat_MemHandShift(p, h) ); } +static inline clause * Sat_MemClauseHand( Sat_Mem_t * p, cla h ) { return h ? Sat_MemClause( p, Sat_MemHandPage(p, h), Sat_MemHandShift(p, h) ) : NULL; } +static inline int Sat_MemEntryNum( Sat_Mem_t * p, int lrn ) { return p->nEntries[lrn]; } + +static inline cla Sat_MemHand( Sat_Mem_t * p, int i, int k ) { return (i << p->nPageSize) | k; } +static inline cla Sat_MemHandCurrent( Sat_Mem_t * p, int lrn ) { return (p->iPage[lrn] << p->nPageSize) | Sat_MemLimit( p->pPages[p->iPage[lrn]] ); } + +static inline int Sat_MemClauseUsed( Sat_Mem_t * p, cla h ) { return h < p->BookMarkH[(h & p->uLearnedMask) > 0]; } + +static inline double Sat_MemMemoryHand( Sat_Mem_t * p, cla h ) { return 1.0 * ((Sat_MemHandPage(p, h) + 2)/2 * (1 << (p->nPageSize+2)) + Sat_MemHandShift(p, h) * 4); } +static inline double Sat_MemMemoryUsed( Sat_Mem_t * p, int lrn ) { return Sat_MemMemoryHand( p, Sat_MemHandCurrent(p, lrn) ); } +static inline double Sat_MemMemoryAllUsed( Sat_Mem_t * p ) { return Sat_MemMemoryUsed( p, 0 ) + Sat_MemMemoryUsed( p, 1 ); } +static inline double Sat_MemMemoryAll( Sat_Mem_t * p ) { return 1.0 * (p->iPage[0] + p->iPage[1] + 2) * (1 << (p->nPageSize+2)); } + +// p is memory storage +// c is clause pointer +// i is page number +// k is page offset + +// print problem clauses NOT in proof mode +#define Sat_MemForEachClause( p, c, i, k ) \ + for ( i = 0; i <= p->iPage[0]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize(c) ) if ( i == 0 && k == 2 ) {} else + +// print problem clauses in proof mode +#define Sat_MemForEachClause2( p, c, i, k ) \ + for ( i = 0; i <= p->iPage[0]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize2(c) ) if ( i == 0 && k == 2 ) {} else + +#define Sat_MemForEachLearned( p, c, i, k ) \ + for ( i = 1; i <= p->iPage[1]; i += 2 ) \ + for ( k = 2; k < Sat_MemLimit(p->pPages[i]) && ((c) = Sat_MemClause( p, i, k )); k += Sat_MemClauseSize(c) ) + +//////////////////////////////////////////////////////////////////////// +/// GLOBAL VARIABLES /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static inline int clause_from_lit( lit l ) { return l + l + 1; } +static inline int clause_is_lit( cla h ) { return (h & 1); } +static inline lit clause_read_lit( cla h ) { return (lit)(h >> 1); } + +static inline int clause_learnt_h( Sat_Mem_t * p, cla h ) { return (h & p->uLearnedMask) > 0; } +static inline int clause_learnt( clause * c ) { return c->lrn; } +static inline int clause_id( clause * c ) { return c->lits[c->size]; } +static inline void clause_set_id( clause * c, int id ) { c->lits[c->size] = id; } +static inline int clause_size( clause * c ) { return c->size; } +static inline lit * clause_begin( clause * c ) { return c->lits; } +static inline lit * clause_end( clause * c ) { return c->lits + c->size; } +static inline void clause_print_( clause * c ) +{ + int i; + printf( "{ " ); + for ( i = 0; i < clause_size(c); i++ ) + printf( "%d ", (clause_begin(c)[i] & 1)? -(clause_begin(c)[i] >> 1) : clause_begin(c)[i] >> 1 ); + printf( "}\n" ); +} + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocating vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemCountL( Sat_Mem_t * p ) +{ + clause * c; + int i, k, Count = 0; + Sat_MemForEachLearned( p, c, i, k ) + Count++; + return Count; +} + +/**Function************************************************************* + + Synopsis [Allocating vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemAlloc_( Sat_Mem_t * p, int nPageSize ) +{ + assert( nPageSize > 8 && nPageSize < 32 ); + memset( p, 0, sizeof(Sat_Mem_t) ); + p->nPageSize = nPageSize; + p->uLearnedMask = (unsigned)(1 << nPageSize); + p->uPageMask = (unsigned)((1 << nPageSize) - 1); + p->nPagesAlloc = 256; + p->pPages = ABC_CALLOC( int *, p->nPagesAlloc ); + p->pPages[0] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + p->pPages[1] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + p->iPage[0] = 0; + p->iPage[1] = 1; + Sat_MemWriteLimit( p->pPages[0], 2 ); + Sat_MemWriteLimit( p->pPages[1], 2 ); +} +static inline Sat_Mem_t * Sat_MemAlloc( int nPageSize ) +{ + Sat_Mem_t * p; + p = ABC_CALLOC( Sat_Mem_t, 1 ); + Sat_MemAlloc_( p, nPageSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Resetting vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemRestart( Sat_Mem_t * p ) +{ + p->nEntries[0] = 0; + p->nEntries[1] = 0; + p->iPage[0] = 0; + p->iPage[1] = 1; + Sat_MemWriteLimit( p->pPages[0], 2 ); + Sat_MemWriteLimit( p->pPages[1], 2 ); +} + +/**Function************************************************************* + + Synopsis [Sets the bookmark.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemBookMark( Sat_Mem_t * p ) +{ + p->BookMarkE[0] = p->nEntries[0]; + p->BookMarkE[1] = p->nEntries[1]; + p->BookMarkH[0] = Sat_MemHandCurrent( p, 0 ); + p->BookMarkH[1] = Sat_MemHandCurrent( p, 1 ); +} +static inline void Sat_MemRollBack( Sat_Mem_t * p ) +{ + p->nEntries[0] = p->BookMarkE[0]; + p->nEntries[1] = p->BookMarkE[1]; + p->iPage[0] = Sat_MemHandPage( p, p->BookMarkH[0] ); + p->iPage[1] = Sat_MemHandPage( p, p->BookMarkH[1] ); + Sat_MemWriteLimit( p->pPages[p->iPage[0]], Sat_MemHandShift( p, p->BookMarkH[0] ) ); + Sat_MemWriteLimit( p->pPages[p->iPage[1]], Sat_MemHandShift( p, p->BookMarkH[1] ) ); +} + +/**Function************************************************************* + + Synopsis [Freeing vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemFree_( Sat_Mem_t * p ) +{ + int i; + for ( i = 0; i < p->nPagesAlloc; i++ ) + ABC_FREE( p->pPages[i] ); + ABC_FREE( p->pPages ); +} +static inline void Sat_MemFree( Sat_Mem_t * p ) +{ + Sat_MemFree_( p ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [Creates new clause.] + + Description [The resulting clause is fully initialized.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemAppend( Sat_Mem_t * p, int * pArray, int nSize, int lrn, int fPlus1 ) +{ + clause * c; + int * pPage = p->pPages[p->iPage[lrn]]; + int nInts = Sat_MemIntSize( nSize, lrn | fPlus1 ); + assert( nInts + 3 < (1 << p->nPageSize) ); + // need two extra at the begining of the page and one extra in the end + if ( Sat_MemLimit(pPage) + nInts + 2 >= (1 << p->nPageSize) ) + { + p->iPage[lrn] += 2; + if ( p->iPage[lrn] >= p->nPagesAlloc ) + { + p->pPages = ABC_REALLOC( int *, p->pPages, p->nPagesAlloc * 2 ); + memset( p->pPages + p->nPagesAlloc, 0, sizeof(int *) * p->nPagesAlloc ); + p->nPagesAlloc *= 2; + } + if ( p->pPages[p->iPage[lrn]] == NULL ) + p->pPages[p->iPage[lrn]] = ABC_ALLOC( int, (int)(((word)1) << p->nPageSize) ); + pPage = p->pPages[p->iPage[lrn]]; + Sat_MemWriteLimit( pPage, 2 ); + } + pPage[Sat_MemLimit(pPage)] = 0; + c = (clause *)(pPage + Sat_MemLimit(pPage)); + c->size = nSize; + c->lrn = lrn; + if ( pArray ) + memcpy( c->lits, pArray, sizeof(int) * nSize ); + if ( lrn | fPlus1 ) + c->lits[c->size] = p->nEntries[lrn]; + p->nEntries[lrn]++; + Sat_MemIncLimit( pPage, nInts ); + return Sat_MemHandCurrent(p, lrn) - nInts; +} + +/**Function************************************************************* + + Synopsis [Shrinking vector size.] + + Description [] + + SideEffects [This procedure does not update the number of entries.] + + SeeAlso [] + +***********************************************************************/ +static inline void Sat_MemShrink( Sat_Mem_t * p, int h, int lrn ) +{ + assert( clause_learnt_h(p, h) == lrn ); + assert( h && h <= Sat_MemHandCurrent(p, lrn) ); + p->iPage[lrn] = Sat_MemHandPage(p, h); + Sat_MemWriteLimit( p->pPages[p->iPage[lrn]], Sat_MemHandShift(p, h) ); +} + + +/**Function************************************************************* + + Synopsis [Compacts learned clauses by removing marked entries.] + + Description [Returns the number of remaining entries.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Sat_MemCompactLearned( Sat_Mem_t * p, int fDoMove ) +{ + clause * c, * cPivot = NULL; + int i, k, iNew = 1, kNew = 2, nInts, fStartLooking, Counter = 0; + int hLimit = Sat_MemHandCurrent(p, 1); + if ( hLimit == Sat_MemHand(p, 1, 2) ) + return 0; + if ( fDoMove && p->BookMarkH[1] ) + { + // move the pivot + assert( p->BookMarkH[1] >= Sat_MemHand(p, 1, 2) && p->BookMarkH[1] <= hLimit ); + // get the pivot and remember it may be pointed offlimit + cPivot = Sat_MemClauseHand( p, p->BookMarkH[1] ); + if ( p->BookMarkH[1] < hLimit && !cPivot->mark ) + { + p->BookMarkH[1] = cPivot->lits[cPivot->size]; + cPivot = NULL; + } + // else find the next used clause after cPivot + } + // iterate through the learned clauses + fStartLooking = 0; + Sat_MemForEachLearned( p, c, i, k ) + { + assert( c->lrn ); + // skip marked entry + if ( c->mark ) + { + // if pivot is a marked clause, start looking for the next non-marked one + if ( cPivot && cPivot == c ) + { + fStartLooking = 1; + cPivot = NULL; + } + continue; + } + // if we started looking before, we found it! + if ( fStartLooking ) + { + fStartLooking = 0; + p->BookMarkH[1] = c->lits[c->size]; + } + // compute entry size + nInts = Sat_MemClauseSize(c); + assert( !(nInts & 1) ); + // check if we need to scroll to the next page + if ( kNew + nInts >= (1 << p->nPageSize) ) + { + // set the limit of the current page + if ( fDoMove ) + Sat_MemWriteLimit( p->pPages[iNew], kNew ); + // move writing position to the new page + iNew += 2; + kNew = 2; + } + if ( fDoMove ) + { + // make sure the result is the same as previous dry run + assert( c->lits[c->size] == Sat_MemHand(p, iNew, kNew) ); + // only copy the clause if it has changed + if ( i != iNew || k != kNew ) + { + memmove( p->pPages[iNew] + kNew, c, sizeof(int) * nInts ); +// c = Sat_MemClause( p, iNew, kNew ); // assersions do not hold during dry run + c = (clause *)(p->pPages[iNew] + kNew); + assert( nInts == Sat_MemClauseSize(c) ); + } + // set the new ID value + c->lits[c->size] = Counter; + } + else // remember the address of the clause in the new location + c->lits[c->size] = Sat_MemHand(p, iNew, kNew); + // update writing position + kNew += nInts; + assert( iNew <= i && kNew < (1 << p->nPageSize) ); + // update counter + Counter++; + } + if ( fDoMove ) + { + // update the counter + p->nEntries[1] = Counter; + // update the page count + p->iPage[1] = iNew; + // set the limit of the last page + Sat_MemWriteLimit( p->pPages[iNew], kNew ); + // check if the pivot need to be updated + if ( p->BookMarkH[1] ) + { + if ( cPivot ) + { + p->BookMarkH[1] = Sat_MemHandCurrent(p, 1); + p->BookMarkE[1] = p->nEntries[1]; + } + else + p->BookMarkE[1] = clause_id(Sat_MemClauseHand( p, p->BookMarkH[1] )); + } + + } + return Counter; +} + + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/bill/bill/sat/solver/abc/satSolver.h b/lib/bill/bill/sat/solver/abc/satSolver.h new file mode 100644 index 0000000..25f7528 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/satSolver.h @@ -0,0 +1,651 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef ABC__sat__bsat__satSolver_h +#define ABC__sat__bsat__satSolver_h + + +#include +#include +#include +#include + +#include "satVec.h" +#include "satClause.h" +#include "utilDouble.h" + +ABC_NAMESPACE_HEADER_START + +//================================================================================================= +// Public interface: + +struct sat_solver_t; +typedef struct sat_solver_t sat_solver; + +extern sat_solver* sat_solver_new(void); +extern sat_solver* zsat_solver_new_seed(double seed); +extern void sat_solver_delete(sat_solver* s); + +extern void solver_init_activities(sat_solver* s); + +extern int sat_solver_addclause(sat_solver* s, lit* begin, lit* end); +extern int sat_solver_clause_new(sat_solver* s, lit* begin, lit* end, int learnt); +extern int sat_solver_simplify(sat_solver* s); +extern int sat_solver_solve(sat_solver* s, lit* begin, lit* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal); +extern int sat_solver_solve_internal(sat_solver* s); +extern int sat_solver_solve_lexsat(sat_solver* s, int * pLits, int nLits); +extern int sat_solver_minimize_assumptions( sat_solver* s, int * pLits, int nLits, int nConfLimit ); +extern int sat_solver_minimize_assumptions2( sat_solver* s, int * pLits, int nLits, int nConfLimit ); +extern int sat_solver_push(sat_solver* s, int p); +extern void sat_solver_pop(sat_solver* s); +extern void sat_solver_set_resource_limits(sat_solver* s, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal); +extern void sat_solver_restart( sat_solver* s ); +extern void zsat_solver_restart_seed( sat_solver* s, double seed ); +extern void sat_solver_rollback( sat_solver* s ); + +extern int sat_solver_nvars(sat_solver* s); +extern int sat_solver_nclauses(sat_solver* s); +extern int sat_solver_nconflicts(sat_solver* s); +extern double sat_solver_memory(sat_solver* s); +extern int sat_solver_count_assigned(sat_solver* s); + +extern int sat_solver_addvar(sat_solver* s); +extern void sat_solver_setnvars(sat_solver* s,int n); +extern int sat_solver_get_var_value(sat_solver* s, int v); +extern void sat_solver_set_var_activity(sat_solver* s, int * pVars, int nVars); + +extern void Sat_SolverWriteDimacs( sat_solver * p, char * pFileName, lit* assumptionsBegin, lit* assumptionsEnd, int incrementVars ); +extern void Sat_SolverPrintStats( FILE * pFile, sat_solver * p ); +extern int * Sat_SolverGetModel( sat_solver * p, int * pVars, int nVars ); +extern void Sat_SolverDoubleClauses( sat_solver * p, int iVar ); + +// trace recording +extern void Sat_SolverTraceStart( sat_solver * pSat, char * pName ); +extern void Sat_SolverTraceStop( sat_solver * pSat ); +extern void Sat_SolverTraceWrite( sat_solver * pSat, int * pBeg, int * pEnd, int fRoot ); + +// clause storage +extern void sat_solver_store_alloc( sat_solver * s ); +extern void sat_solver_store_write( sat_solver * s, char * pFileName ); +extern void sat_solver_store_free( sat_solver * s ); +extern void sat_solver_store_mark_roots( sat_solver * s ); +extern void sat_solver_store_mark_clauses_a( sat_solver * s ); +extern void * sat_solver_store_release( sat_solver * s ); + +//================================================================================================= +// Solver representation: + +//struct clause_t; +//typedef struct clause_t clause; + +struct varinfo_t; +typedef struct varinfo_t varinfo; + +struct sat_solver_t +{ + int size; // nof variables + int cap; // size of varmaps + int qhead; // Head index of queue. + int qtail; // Tail index of queue. + + // clauses + Sat_Mem_t Mem; + int hLearnts; // the first learnt clause + int hBinary; // the special binary clause + clause * binary; + veci* wlists; // watcher lists + + // rollback + int iVarPivot; // the pivot for variables + int iTrailPivot; // the pivot for trail + int hProofPivot; // the pivot for proof records + + // activities + int VarActType = 0; + int ClaActType = 0; + word var_inc; // Amount to bump next variable with. + word var_inc2; // Amount to bump next variable with. + word var_decay; // INVERSE decay factor for variable activity: stores 1/decay. + word* activity; // A heuristic measurement of the activity of a variable. + word* activity2; // backup variable activity + unsigned cla_inc; // Amount to bump next clause with. + unsigned cla_decay; // INVERSE decay factor for clause activity: stores 1/decay. + veci act_clas; // contain clause activities + + char * pFreqs; // how many times this variable was assigned a value + int nVarUsed; + +// varinfo * vi; // variable information + int* levels; // + char* assigns; // Current values of variables. + char* polarity; // + char* tags; // + char* loads; // + + int* orderpos; // Index in variable order. + int* reasons; // + lit* trail; + veci tagged; // (contains: var) + veci stack; // (contains: var) + + veci order; // Variable order. (heap) (contains: var) + veci trail_lim; // Separator indices for different decision levels in 'trail'. (contains: int) +// veci model; // If problem is solved, this vector contains the model (contains: lbool). + int * model; // If problem is solved, this vector contains the model (contains: lbool). + veci conf_final; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + int root_level; // Level of first proper decision. + int simpdb_assigns;// Number of top-level assignments at last 'simplifyDB()'. + int simpdb_props; // Number of propagations before next 'simplifyDB()'. + double random_seed = 91648253; + double progress_estimate; + int verbosity; // Verbosity level. 0=silent, 1=some progress report, 2=everything + int fVerbose; + int fPrintClause; + + stats_t stats; + int nLearntMax; // max number of learned clauses + int nLearntStart; // starting learned clause limit + int nLearntDelta; // delta of learned clause limit + int nLearntRatio; // ratio percentage of learned clauses + int nDBreduces; // number of DB reductions + + ABC_INT64_T nConfLimit; // external limit on the number of conflicts + ABC_INT64_T nInsLimit; // external limit on the number of implications + abctime nRuntimeLimit; // external limit on runtime + + veci act_vars; // variables whose activity has changed + double* factors; // the activity factors + int nRestarts; // the number of local restarts + int nCalls; // the number of local restarts + int nCalls2; // the number of local restarts + veci unit_lits; // variables whose activity has changed + veci pivot_vars; // pivot variables + + int fSkipSimplify; // set to one to skip simplification of the clause database + int fNotUseRandom; // do not allow random decisions with a fixed probability + int fNoRestarts; // disables periodic restarts + + int * pGlobalVars; // for experiments with global vars during interpolation + // clause store + void * pStore; + int fSolved; + + // trace recording + FILE * pFile; + int nClauses; + int nRoots; + + veci temp_clause; // temporary storage for a CNF clause + + // assignment storage + veci user_vars; // variable IDs + veci user_values; // values of these variables + + // CNF loading + void * pCnfMan; // external CNF manager + int(*pCnfFunc)(void * p, int); // external callback + + // termination callback + int RunId; // SAT id in this run + int(*pFuncStop)(int); // callback to terminate +}; + +static inline clause * clause_read( sat_solver * s, cla h ) +{ + return Sat_MemClauseHand( &s->Mem, h ); +} + +static inline int sat_solver_var_value( sat_solver* s, int v ) +{ + assert( v >= 0 && v < s->size ); + return (int)(s->model[v] == l_True); +} +static inline int sat_solver_var_literal( sat_solver* s, int v ) +{ + assert( v >= 0 && v < s->size ); + return toLitCond( v, s->model[v] != l_True ); +} +static inline void sat_solver_flip_print_clause( sat_solver* s ) +{ + s->fPrintClause ^= 1; +} +static inline void sat_solver_act_var_clear(sat_solver* s) +{ + int i; + if ( s->VarActType == 0 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = (1 << 10); + s->var_inc = (1 << 5); + } + else if ( s->VarActType == 1 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = 0; + s->var_inc = 1; + } + else if ( s->VarActType == 2 ) + { + for (i = 0; i < s->size; i++) + s->activity[i] = Xdbl_Const1(); + s->var_inc = Xdbl_Const1(); + } + else assert(0); +} +static inline void sat_solver_compress(sat_solver* s) +{ + if ( s->qtail != s->qhead ) + { + int RetValue = sat_solver_simplify(s); + assert( RetValue != 0 ); + (void) RetValue; + } +} +static inline void sat_solver_delete_p( sat_solver ** ps ) +{ + if ( *ps ) + sat_solver_delete( *ps ); + *ps = NULL; +} +static inline void sat_solver_clean_polarity(sat_solver* s, int * pVars, int nVars ) +{ + int i; + for ( i = 0; i < nVars; i++ ) + s->polarity[pVars[i]] = 0; +} +static inline void sat_solver_set_polarity(sat_solver* s, int * pVars, int nVars ) +{ + int i; + for ( i = 0; i < s->size; i++ ) + s->polarity[i] = 0; + for ( i = 0; i < nVars; i++ ) + s->polarity[pVars[i]] = 1; +} +static inline void sat_solver_set_literal_polarity(sat_solver* s, int * pLits, int nLits ) +{ + int i; + for ( i = 0; i < nLits; i++ ) + s->polarity[Abc_Lit2Var(pLits[i])] = !Abc_LitIsCompl(pLits[i]); +} + +static inline int sat_solver_final(sat_solver* s, int ** ppArray) +{ + *ppArray = s->conf_final.ptr; + return s->conf_final.size; +} + +static inline abctime sat_solver_set_runtime_limit(sat_solver* s, abctime Limit) +{ + abctime nRuntimeLimit = s->nRuntimeLimit; + s->nRuntimeLimit = Limit; + return nRuntimeLimit; +} + +static inline int sat_solver_set_random(sat_solver* s, int fNotUseRandom) +{ + int fNotUseRandomOld = s->fNotUseRandom; + s->fNotUseRandom = fNotUseRandom; + return fNotUseRandomOld; +} + +static inline void sat_solver_bookmark(sat_solver* s) +{ + if ( s->qtail != s->qhead ) + { + int status = sat_solver_simplify( s ); + assert( status != 0 ); (void)status; + assert( s->qtail == s->qhead ); + } + s->iVarPivot = s->size; + s->iTrailPivot = s->qhead; + Sat_MemBookMark( &s->Mem ); + if ( s->activity2 ) + { + s->var_inc2 = s->var_inc; + memcpy( s->activity2, s->activity, sizeof(word) * s->iVarPivot ); + } +} +static inline void sat_solver_set_pivot_variables( sat_solver* s, int * pPivots, int nPivots ) +{ + s->pivot_vars.cap = nPivots; + s->pivot_vars.size = nPivots; + s->pivot_vars.ptr = pPivots; +} +static inline int sat_solver_count_usedvars(sat_solver* s) +{ + int i, nVars = 0; + for ( i = 0; i < s->size; i++ ) + if ( s->pFreqs[i] ) + { + s->pFreqs[i] = 0; + nVars++; + } + return nVars; +} +static inline void sat_solver_set_runid( sat_solver *s, int id ) +{ + s->RunId = id; +} +static inline void sat_solver_set_stop_func( sat_solver *s, int (*fnct)(int) ) +{ + s->pFuncStop = fnct; +} + +static inline int sat_solver_add_const( sat_solver * pSat, int iVar, int fCompl ) +{ + lit Lits[1]; + int Cid; + assert( iVar >= 0 ); + + Lits[0] = toLitCond( iVar, fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 1 ); + assert( Cid ); + return 1; +} +static inline int sat_solver_add_buffer( sat_solver * pSat, int iVarA, int iVarB, int fCompl ) +{ + lit Lits[2]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVarB, !fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + if ( Cid == 0 ) + return 0; + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 1 ); + Lits[1] = toLitCond( iVarB, fCompl ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + if ( Cid == 0 ) + return 0; + assert( Cid ); + return 2; +} +static inline int sat_solver_add_buffer_enable( sat_solver * pSat, int iVarA, int iVarB, int iVarEn, int fCompl ) +{ + lit Lits[3]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 && iVarEn >= 0 ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVarB, !fCompl ); + Lits[2] = toLitCond( iVarEn, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 1 ); + Lits[1] = toLitCond( iVarB, fCompl ); + Lits[2] = toLitCond( iVarEn, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + return 2; +} +static inline int sat_solver_add_and( sat_solver * pSat, int iVar, int iVar0, int iVar1, int fCompl0, int fCompl1, int fCompl ) +{ + lit Lits[3]; + int Cid; + + Lits[0] = toLitCond( iVar, !fCompl ); + Lits[1] = toLitCond( iVar0, fCompl0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, !fCompl ); + Lits[1] = toLitCond( iVar1, fCompl1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar0, !fCompl0 ); + Lits[2] = toLitCond( iVar1, !fCompl1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + return 3; +} +static inline int sat_solver_add_xor( sat_solver * pSat, int iVarA, int iVarB, int iVarC, int fCompl ) +{ + lit Lits[3]; + int Cid; + assert( iVarA >= 0 && iVarB >= 0 && iVarC >= 0 ); + + Lits[0] = toLitCond( iVarA, !fCompl ); + Lits[1] = toLitCond( iVarB, 1 ); + Lits[2] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, !fCompl ); + Lits[1] = toLitCond( iVarB, 0 ); + Lits[2] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, fCompl ); + Lits[1] = toLitCond( iVarB, 1 ); + Lits[2] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, fCompl ); + Lits[1] = toLitCond( iVarB, 0 ); + Lits[2] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + return 4; +} +static inline int sat_solver_add_mux( sat_solver * pSat, int iVarZ, int iVarC, int iVarT, int iVarE, int iComplC, int iComplT, int iComplE, int iComplZ ) +{ + lit Lits[3]; + int Cid; + assert( iVarC >= 0 && iVarT >= 0 && iVarE >= 0 && iVarZ >= 0 ); + + Lits[0] = toLitCond( iVarC, 1 ^ iComplC ); + Lits[1] = toLitCond( iVarT, 1 ^ iComplT ); + Lits[2] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 1 ^ iComplC ); + Lits[1] = toLitCond( iVarT, 0 ^ iComplT ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 0 ^ iComplC ); + Lits[1] = toLitCond( iVarE, 1 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 0 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarC, 0 ^ iComplC ); + Lits[1] = toLitCond( iVarE, 0 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + if ( iVarT == iVarE ) + return 4; + + Lits[0] = toLitCond( iVarT, 0 ^ iComplT ); + Lits[1] = toLitCond( iVarE, 0 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 1 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarT, 1 ^ iComplT ); + Lits[1] = toLitCond( iVarE, 1 ^ iComplE ); + Lits[2] = toLitCond( iVarZ, 0 ^ iComplZ ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + return 6; +} +static inline int sat_solver_add_mux41( sat_solver * pSat, int iVarZ, int iVarC0, int iVarC1, int iVarD0, int iVarD1, int iVarD2, int iVarD3 ) +{ + lit Lits[4]; + int Cid; + assert( iVarC0 >= 0 && iVarC1 >= 0 && iVarD0 >= 0 && iVarD1 >= 0 && iVarD2 >= 0 && iVarD3 >= 0 && iVarZ >= 0 ); + + Lits[0] = toLitCond( iVarD0, 1 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD1, 1 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD2, 1 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD3, 1 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + + Lits[0] = toLitCond( iVarD0, 0 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD1, 0 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 0 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD2, 0 ); + Lits[1] = toLitCond( iVarC0, 0 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarD3, 0 ); + Lits[1] = toLitCond( iVarC0, 1 ); + Lits[2] = toLitCond( iVarC1, 1 ); + Lits[3] = toLitCond( iVarZ, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + return 8; +} +static inline int sat_solver_add_xor_and( sat_solver * pSat, int iVarF, int iVarA, int iVarB, int iVarC ) +{ + // F = (a (+) b) * c + lit Lits[4]; + int Cid; + assert( iVarF >= 0 && iVarA >= 0 && iVarB >= 0 && iVarC >= 0 ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarA, 1 ); + Lits[2] = toLitCond( iVarB, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarA, 0 ); + Lits[2] = toLitCond( iVarB, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 1 ); + Lits[1] = toLitCond( iVarC, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 0 ); + Lits[1] = toLitCond( iVarA, 1 ); + Lits[2] = toLitCond( iVarB, 0 ); + Lits[3] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarF, 0 ); + Lits[1] = toLitCond( iVarA, 0 ); + Lits[2] = toLitCond( iVarB, 1 ); + Lits[3] = toLitCond( iVarC, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 4 ); + assert( Cid ); + return 5; +} +static inline int sat_solver_add_constraint( sat_solver * pSat, int iVar, int iVar2, int fCompl ) +{ + lit Lits[2]; + int Cid; + assert( iVar >= 0 ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar2, 0 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVar, fCompl ); + Lits[1] = toLitCond( iVar2, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + return 2; +} + +static inline int sat_solver_add_half_sorter( sat_solver * pSat, int iVarA, int iVarB, int iVar0, int iVar1 ) +{ + lit Lits[3]; + int Cid; + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVar0, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarA, 0 ); + Lits[1] = toLitCond( iVar1, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 2 ); + assert( Cid ); + + Lits[0] = toLitCond( iVarB, 0 ); + Lits[1] = toLitCond( iVar0, 1 ); + Lits[2] = toLitCond( iVar1, 1 ); + Cid = sat_solver_addclause( pSat, Lits, Lits + 3 ); + assert( Cid ); + return 3; +} + + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/satStore.h b/lib/bill/bill/sat/solver/abc/satStore.h new file mode 100644 index 0000000..f2480a7 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/satStore.h @@ -0,0 +1,158 @@ +/**CFile**************************************************************** + + FileName [satStore.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Proof recording.] + + Synopsis [External declarations.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: pr.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__sat__bsat__satStore_h +#define ABC__sat__bsat__satStore_h + + +/* + The trace of SAT solving contains the original clauses of the problem + along with the learned clauses derived during SAT solving. + The first line of the resulting file contains 3 numbers instead of 2: + c +*/ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "satSolver.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_HEADER_START + +#ifdef _WIN32 +#define inline __inline // compatible with MS VS 6.0 +#endif + +#define STO_MAX(a,b) ((a) > (b) ? (a) : (b)) + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +/* +typedef unsigned lit; +// variable/literal conversions (taken from MiniSat) +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } +*/ + +typedef struct Sto_Cls_t_ Sto_Cls_t; +struct Sto_Cls_t_ +{ + Sto_Cls_t * pNext; // the next clause + Sto_Cls_t * pNext0; // the next 0-watch + Sto_Cls_t * pNext1; // the next 1-watch + int Id; // the clause ID + unsigned fA : 1; // belongs to A + unsigned fRoot : 1; // original clause + unsigned fVisit : 1; // visited clause + unsigned nLits : 24; // the number of literals + lit pLits[0]; // literals of this clause +}; + +typedef struct Sto_Man_t_ Sto_Man_t; +struct Sto_Man_t_ +{ + // general data + int nVars; // the number of variables + int nRoots; // the number of root clauses + int nClauses; // the number of all clauses + int nClausesA; // the number of clauses of A + Sto_Cls_t * pHead; // the head clause + Sto_Cls_t * pTail; // the tail clause + Sto_Cls_t * pEmpty; // the empty clause + // memory management + int nChunkSize; // the number of bytes in a chunk + int nChunkUsed; // the number of bytes used in the last chunk + char * pChunkLast; // the last memory chunk +}; + +// iterators through the clauses +#define Sto_ManForEachClause( p, pCls ) for( pCls = p->pHead; pCls; pCls = pCls->pNext ) +#define Sto_ManForEachClauseRoot( p, pCls ) for( pCls = p->pHead; pCls && pCls->fRoot; pCls = pCls->pNext ) + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +/*=== satStore.c ==========================================================*/ +extern Sto_Man_t * Sto_ManAlloc(); +extern void Sto_ManFree( Sto_Man_t * p ); +extern int Sto_ManAddClause( Sto_Man_t * p, lit * pBeg, lit * pEnd ); +extern int Sto_ManMemoryReport( Sto_Man_t * p ); +extern void Sto_ManMarkRoots( Sto_Man_t * p ); +extern void Sto_ManMarkClausesA( Sto_Man_t * p ); +extern void Sto_ManDumpClauses( Sto_Man_t * p, char * pFileName ); +extern int Sto_ManChangeLastClause( Sto_Man_t * p ); +extern Sto_Man_t * Sto_ManLoadClauses( char * pFileName ); + + +/*=== satInter.c ==========================================================*/ +typedef struct Int_Man_t_ Int_Man_t; +extern Int_Man_t * Int_ManAlloc(); +extern int * Int_ManSetGlobalVars( Int_Man_t * p, int nGloVars ); +extern void Int_ManFree( Int_Man_t * p ); +extern int Int_ManInterpolate( Int_Man_t * p, Sto_Man_t * pCnf, int fVerbose, unsigned ** ppResult ); + +/*=== satInterA.c ==========================================================*/ +typedef struct Inta_Man_t_ Inta_Man_t; +extern Inta_Man_t * Inta_ManAlloc(); +extern void Inta_ManFree( Inta_Man_t * p ); +extern void * Inta_ManInterpolate( Inta_Man_t * p, Sto_Man_t * pCnf, abctime TimeToStop, void * vVarsAB, int fVerbose ); + +/*=== satInterB.c ==========================================================*/ +typedef struct Intb_Man_t_ Intb_Man_t; +extern Intb_Man_t * Intb_ManAlloc(); +extern void Intb_ManFree( Intb_Man_t * p ); +extern void * Intb_ManInterpolate( Intb_Man_t * p, Sto_Man_t * pCnf, void * vVarsAB, int fVerbose ); + +/*=== satInterP.c ==========================================================*/ +typedef struct Intp_Man_t_ Intp_Man_t; +extern Intp_Man_t * Intp_ManAlloc(); +extern void Intp_ManFree( Intp_Man_t * p ); +extern void * Intp_ManUnsatCore( Intp_Man_t * p, Sto_Man_t * pCnf, int fLearned, int fVerbose ); +extern void Intp_ManUnsatCorePrintForBmc( FILE * pFile, Sto_Man_t * pCnf, void * vCore, void * vVarMap ); + + +ABC_NAMESPACE_HEADER_END + + + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/bill/bill/sat/solver/abc/satVec.h b/lib/bill/bill/sat/solver/abc/satVec.h new file mode 100644 index 0000000..121a649 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/satVec.h @@ -0,0 +1,169 @@ +/************************************************************************************************** +MiniSat -- Copyright (c) 2005, Niklas Sorensson +http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/ + +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. +**************************************************************************************************/ +// Modified to compile with MS Visual Studio 6.0 by Alan Mishchenko + +#ifndef ABC__sat__bsat__satVec_h +#define ABC__sat__bsat__satVec_h + +#include "abc_global.h" + +ABC_NAMESPACE_HEADER_START + + +// vector of 32-bit intergers (added for 64-bit portability) +struct veci_t { + int cap; + int size; + int* ptr; +}; +typedef struct veci_t veci; + +static inline void veci_new (veci* v) { + v->cap = 4; + v->size = 0; + v->ptr = (int*)ABC_ALLOC( char, sizeof(int)*v->cap); +} + +static inline void veci_delete (veci* v) { ABC_FREE(v->ptr); } +static inline int* veci_begin (veci* v) { return v->ptr; } +static inline int veci_size (veci* v) { return v->size; } +static inline void veci_resize (veci* v, int k) { + assert(k <= v->size); +// memset( veci_begin(v) + k, -1, sizeof(int) * (veci_size(v) - k) ); + v->size = k; +} // only safe to shrink !! +static inline int veci_pop (veci* v) { assert(v->size); return v->ptr[--v->size]; } +static inline void veci_push (veci* v, int e) +{ + if (v->size == v->cap) { +// int newsize = v->cap * 2;//+1; + int newsize = (v->cap < 4) ? v->cap * 2 : (v->cap / 2) * 3; + v->ptr = ABC_REALLOC( int, v->ptr, newsize ); + if ( v->ptr == NULL ) + { + printf( "Failed to realloc memory from %.1f MB to %.1f MB.\n", + 1.0 * v->cap / (1<<20), 1.0 * newsize / (1<<20) ); + fflush( stdout ); + } + v->cap = newsize; } + v->ptr[v->size++] = e; +} +static inline void veci_remove(veci* v, int e) +{ + int * ws = (int*)veci_begin(v); + int j = 0; + for (; ws[j] != e ; j++); + assert(j < veci_size(v)); + for (; j < veci_size(v)-1; j++) ws[j] = ws[j+1]; + veci_resize(v,veci_size(v)-1); +} + + +// vector of 32- or 64-bit pointers +struct vecp_t { + int cap; + int size; + void** ptr; +}; +typedef struct vecp_t vecp; + +static inline void vecp_new (vecp* v) { + v->size = 0; + v->cap = 4; + v->ptr = (void**)ABC_ALLOC( char, sizeof(void*)*v->cap); +} + +static inline void vecp_delete (vecp* v) { ABC_FREE(v->ptr); } +static inline void** vecp_begin (vecp* v) { return v->ptr; } +static inline int vecp_size (vecp* v) { return v->size; } +static inline void vecp_resize (vecp* v, int k) { assert(k <= v->size); v->size = k; } // only safe to shrink !! +static inline void vecp_push (vecp* v, void* e) +{ + if (v->size == v->cap) { +// int newsize = v->cap * 2;//+1; + int newsize = (v->cap < 4) ? v->cap * 2 : (v->cap / 2) * 3; + v->ptr = ABC_REALLOC( void*, v->ptr, newsize ); + v->cap = newsize; } + v->ptr[v->size++] = e; +} +static inline void vecp_remove(vecp* v, void* e) +{ + void** ws = vecp_begin(v); + int j = 0; + for (; ws[j] != e ; j++); + assert(j < vecp_size(v)); + for (; j < vecp_size(v)-1; j++) ws[j] = ws[j+1]; + vecp_resize(v,vecp_size(v)-1); +} + + + +//================================================================================================= +// Simple types: + +#ifndef __cplusplus +#ifndef false +# define false 0 +#endif +#ifndef true +# define true 1 +#endif +#endif + +typedef int lit; +typedef int cla; + +// Explicitly make it signed so promotion-to-int behavior doesn't vary +// across platforms that define signedness of char differently. +typedef signed char lbool; + +// CryptoMinisat defines it's own var_Undef values. +// When it's included we prefer the ABC version instead. +#ifdef var_Undef +#undef var_Undef +#endif + +static const int var_Undef = -1; +static const lit lit_Undef = -2; + +static const lbool l_Undef = 0; +static const lbool l_True = 1; +static const lbool l_False = -1; + +static inline lit toLit (int v) { return v + v; } +static inline lit toLitCond(int v, int c) { return v + v + (c != 0); } +static inline lit lit_neg (lit l) { return l ^ 1; } +static inline int lit_var (lit l) { return l >> 1; } +static inline int lit_sign (lit l) { return l & 1; } +static inline int lit_print(lit l) { return lit_sign(l)? -lit_var(l)-1 : lit_var(l)+1; } +static inline lit lit_read (int s) { return s > 0 ? toLit(s-1) : lit_neg(toLit(-s-1)); } +static inline int lit_check(lit l, int n) { return l >= 0 && lit_var(l) < n; } + +struct stats_t +{ + unsigned starts, clauses, learnts; + ABC_INT64_T decisions, propagations, inspects, conflicts; + ABC_INT64_T clauses_literals, learnts_literals, tot_literals; +}; +typedef struct stats_t stats_t; + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/system.h b/lib/bill/bill/sat/solver/abc/system.h new file mode 100644 index 0000000..4a3bfd7 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/system.h @@ -0,0 +1,67 @@ +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Abc_Glucose_System_h +#define Abc_Glucose_System_h + +#include "IntTypes.h" + +ABC_NAMESPACE_CXX_HEADER_START + +//------------------------------------------------------------------------------------------------- + +namespace Gluco { + +static inline double cpuTime(void); // CPU-time in seconds. + +} + +ABC_NAMESPACE_CXX_HEADER_END + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +ABC_NAMESPACE_CXX_HEADER_START + +static inline double Gluco::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +ABC_NAMESPACE_CXX_HEADER_END + + +#else +#include +#include +#include + +ABC_NAMESPACE_CXX_HEADER_START + +static inline double Gluco::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +ABC_NAMESPACE_CXX_HEADER_END + +#endif + +#endif diff --git a/lib/bill/bill/sat/solver/abc/utilDouble.h b/lib/bill/bill/sat/solver/abc/utilDouble.h new file mode 100644 index 0000000..8880882 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/utilDouble.h @@ -0,0 +1,222 @@ +/**CFile**************************************************************** + + FileName [utilDouble.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [] + + Synopsis [Double floating point number implementation.] + + Author [Alan Mishchenko, Bruno Schmitt] + + Affiliation [UC Berkeley / UFRGS] + + Date [Ver. 1.0. Started - February 11, 2017.] + + Revision [] + +***********************************************************************/ + +#ifndef ABC__sat__Xdbl__Xdbl_h +#define ABC__sat__Xdbl__Xdbl_h + +#include "abc_global.h" + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// STRUCTURE DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/* + The xdbl floating-point number is represented as a 64-bit unsigned int. + The number is (2^Exp)*Mnt, where Exp is a 16-bit exponent and Mnt is a + 48-bit mantissa. The decimal point is located between the MSB of Mnt, + which is always 1, and the remaining 15 digits of Mnt. + + Currently, only positive numbers are represented. + + The range of possible values is [1.0; 2^(2^16-1)*1.111111111111111] + that is, the smallest possible number is 1.0 and the largest possible + number is 2^(---16 ones---).(1.---47 ones---) + + Comparison of numbers can be done by comparing the underlying unsigned ints. + + Only addition, multiplication, and division by 2^n are currently implemented. +*/ + +typedef word xdbl; + +static inline word Xdbl_Exp( xdbl a ) { return a >> 48; } +static inline word Xdbl_Mnt( xdbl a ) { return (a << 16) >> 16; } + +static inline xdbl Xdbl_Create( word Exp, word Mnt ) { assert(!(Exp>>16) && (Mnt>>47)==(word)1); return (Exp<<48) | Mnt; } + +static inline xdbl Xdbl_Const1() { return Xdbl_Create( (word)0, (word)1 << 47 ); } +static inline xdbl Xdbl_Const2() { return Xdbl_Create( (word)1, (word)1 << 47 ); } +static inline xdbl Xdbl_Const3() { return Xdbl_Create( (word)1, (word)3 << 46 ); } +static inline xdbl Xdbl_Const12() { return Xdbl_Create( (word)3, (word)3 << 46 ); } +static inline xdbl Xdbl_Const1point5() { return Xdbl_Create( (word)0, (word)3 << 46 ); } +static inline xdbl Xdbl_Const2point5() { return Xdbl_Create( (word)1, (word)5 << 45 ); } +static inline xdbl Xdbl_Maximum() { return ~(word)0; } + +static inline double Xdbl_ToDouble( xdbl a ) { assert(Xdbl_Exp(a) < 1023); return Abc_Word2Dbl(((Xdbl_Exp(a) + 1023) << 52) | (((a<<17)>>17) << 5)); } +static inline xdbl Xdbl_FromDouble( double a ) { word A = Abc_Dbl2Word(a); assert(a >= 1.0); return Xdbl_Create((A >> 52)-1023, (((word)1) << 47) | ((A << 12) >> 17)); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Adding two floating-point numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Add( xdbl a, xdbl b ) +{ + word Exp, Mnt; + if ( a < b ) a ^= b, b ^= a, a ^= b; + assert( a >= b ); + Mnt = Xdbl_Mnt(a) + (Xdbl_Mnt(b) >> (Xdbl_Exp(a) - Xdbl_Exp(b))); + Exp = Xdbl_Exp(a); + if ( Mnt >> 48 ) // new MSB is created + Exp++, Mnt >>= 1; + if ( Exp >> 16 ) // overflow + return Xdbl_Maximum(); + return Xdbl_Create( Exp, Mnt ); +} + +/**Function************************************************************* + + Synopsis [Multiplying two floating-point numbers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Mul( xdbl a, xdbl b ) +{ + word Exp, Mnt, MntA, MntB, MntAh, MntBh, MntAl, MntBl; + if ( a < b ) a ^= b, b ^= a, a ^= b; + assert( a >= b ); + MntA = Xdbl_Mnt(a); + MntB = Xdbl_Mnt(b); + MntAh = MntA>>32; + MntBh = MntB>>32; + MntAl = (MntA<<32)>>32; + MntBl = (MntB<<32)>>32; + Mnt = ((MntAh * MntBh) << 17) + ((MntAl * MntBl) >> 47) + ((MntAl * MntBh) >> 15) + ((MntAh * MntBl) >> 15); + Exp = Xdbl_Exp(a) + Xdbl_Exp(b); + if ( Mnt >> 48 ) // new MSB is created + Exp++, Mnt >>= 1; + if ( Exp >> 16 ) // overflow + return Xdbl_Maximum(); + return Xdbl_Create( Exp, Mnt ); +} + +/**Function************************************************************* + + Synopsis [Dividing floating point number by a degree of 2.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline xdbl Xdbl_Div( xdbl a, unsigned Deg2 ) +{ + if ( Xdbl_Exp(a) >= (word)Deg2 ) + return Xdbl_Create( Xdbl_Exp(a) - Deg2, Xdbl_Mnt(a) ); + return Xdbl_Const1(); // underflow +} + +/**Function************************************************************* + + Synopsis [Testing procedure.] + + Description [Helpful link https://www.h-schmidt.net/FloatConverter/IEEE754.html] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Xdbl_Test() +{ + xdbl c1 = Xdbl_Const1(); + xdbl c2 = Xdbl_Const2(); + xdbl c3 = Xdbl_Const3(); + xdbl c12 = Xdbl_Const12(); + xdbl c1p5 = Xdbl_Const1point5(); + xdbl c2p5 = Xdbl_Const2point5(); + + xdbl c1_ = Xdbl_FromDouble(1.0); + xdbl c2_ = Xdbl_FromDouble(2.0); + xdbl c3_ = Xdbl_FromDouble(3.0); + xdbl c12_ = Xdbl_FromDouble(12.0); + xdbl c1p5_ = Xdbl_FromDouble(1.5); + xdbl c2p5_ = Xdbl_FromDouble(2.5); + + xdbl sum1 = Xdbl_Add(c1, c1p5); + xdbl mul1 = Xdbl_Mul(c2, c1p5); + + xdbl sum2 = Xdbl_Add(c1p5, c2p5); + xdbl mul2 = Xdbl_Mul(c1p5, c2p5); + + xdbl a = Xdbl_FromDouble(1.2929725); + xdbl b = Xdbl_FromDouble(10.28828287); + xdbl ab = Xdbl_Mul(a, b); + + xdbl ten100 = Xdbl_FromDouble( 1e100 ); + xdbl ten100_ = ABC_CONST(0x014c924d692ca61b); + + assert( ten100 == ten100_ ); + +// float f1 = Xdbl_ToDouble(c1); +// Extra_PrintBinary( stdout, (int *)&c1, 32 ); printf( "\n" ); +// Extra_PrintBinary( stdout, (int *)&f1, 32 ); printf( "\n" ); + + printf( "1 = %lf\n", Xdbl_ToDouble(c1) ); + printf( "2 = %lf\n", Xdbl_ToDouble(c2) ); + printf( "3 = %lf\n", Xdbl_ToDouble(c3) ); + printf( "12 = %lf\n", Xdbl_ToDouble(c12) ); + printf( "1.5 = %lf\n", Xdbl_ToDouble(c1p5) ); + printf( "2.5 = %lf\n", Xdbl_ToDouble(c2p5) ); + + printf( "Converted 1 = %lf\n", Xdbl_ToDouble(c1_) ); + printf( "Converted 2 = %lf\n", Xdbl_ToDouble(c2_) ); + printf( "Converted 3 = %lf\n", Xdbl_ToDouble(c3_) ); + printf( "Converted 12 = %lf\n", Xdbl_ToDouble(c12_) ); + printf( "Converted 1.5 = %lf\n", Xdbl_ToDouble(c1p5_) ); + printf( "Converted 2.5 = %lf\n", Xdbl_ToDouble(c2p5_) ); + + printf( "1.0 + 1.5 = %lf\n", Xdbl_ToDouble(sum1) ); + printf( "2.0 * 1.5 = %lf\n", Xdbl_ToDouble(mul1) ); + + printf( "1.5 + 2.5 = %lf\n", Xdbl_ToDouble(sum2) ); + printf( "1.5 * 2.5 = %lf\n", Xdbl_ToDouble(mul2) ); + printf( "12 / 2^2 = %lf\n", Xdbl_ToDouble(Xdbl_Div(c12, 2)) ); + + printf( "12 / 2^2 = %lf\n", Xdbl_ToDouble(Xdbl_Div(c12, 2)) ); + + printf( "%.16lf * %.16lf = %.16lf (%.16lf)\n", Xdbl_ToDouble(a), Xdbl_ToDouble(b), Xdbl_ToDouble(ab), 1.2929725 * 10.28828287 ); + + assert( sum1 == c2p5 ); + assert( mul1 == c3 ); +} + +ABC_NAMESPACE_HEADER_END + +#endif diff --git a/lib/bill/bill/sat/solver/abc/vecInt.h b/lib/bill/bill/sat/solver/abc/vecInt.h new file mode 100644 index 0000000..51740e7 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/vecInt.h @@ -0,0 +1,2078 @@ +/**CFile**************************************************************** + + FileName [vecInt.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable arrays of integers.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecInt.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__vec__vecInt_h +#define ABC__misc__vec__vecInt_h + + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +ABC_NAMESPACE_HEADER_START + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Int_t_ Vec_Int_t; +struct Vec_Int_t_ +{ + int nCap; + int nSize; + int * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +#define Vec_IntForEachEntry( vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStart( vVec, Entry, i, Start ) \ + for ( i = Start; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStop( vVec, Entry, i, Stop ) \ + for ( i = 0; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryStartStop( vVec, Entry, i, Start, Stop ) \ + for ( i = Start; (i < Stop) && (((Entry) = Vec_IntEntry(vVec, i)), 1); i++ ) +#define Vec_IntForEachEntryReverse( vVec, pEntry, i ) \ + for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && (((pEntry) = Vec_IntEntry(vVec, i)), 1); i-- ) +#define Vec_IntForEachEntryTwo( vVec1, vVec2, Entry1, Entry2, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec1)) && (((Entry1) = Vec_IntEntry(vVec1, i)), 1) && (((Entry2) = Vec_IntEntry(vVec2, i)), 1); i++ ) +#define Vec_IntForEachEntryDouble( vVec, Entry1, Entry2, i ) \ + for ( i = 0; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryDoubleStart( vVec, Entry1, Entry2, i, Start ) \ + for ( i = Start; (i+1 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1); i += 2 ) +#define Vec_IntForEachEntryTriple( vVec, Entry1, Entry2, Entry3, i ) \ + for ( i = 0; (i+2 < Vec_IntSize(vVec)) && (((Entry1) = Vec_IntEntry(vVec, i)), 1) && (((Entry2) = Vec_IntEntry(vVec, i+1)), 1) && (((Entry3) = Vec_IntEntry(vVec, i+2)), 1); i += 3 ) +#define Vec_IntForEachEntryThisNext( vVec, This, Next, i ) \ + for ( i = 0, (This) = (Next) = (Vec_IntSize(vVec) ? Vec_IntEntry(vVec, 0) : -1); (i+1 < Vec_IntSize(vVec)) && (((Next) = Vec_IntEntry(vVec, i+1)), 1); i += 2, (This) = (Next) ) +#define Vec_IntForEachEntryInVec( vVec2, vVec, Entry, i ) \ + for ( i = 0; (i < Vec_IntSize(vVec)) && (((Entry) = Vec_IntEntry(vVec2, Vec_IntEntry(vVec, i))), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAlloc( int nCap ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + if ( nCap > 0 && nCap < 16 ) + nCap = 16; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} +static inline Vec_Int_t * Vec_IntAllocExact( int nCap ) +{ + Vec_Int_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStart( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0, sizeof(int) * nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartFull( int nSize ) +{ + Vec_Int_t * p; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + memset( p->pArray, 0xff, sizeof(int) * nSize ); + return p; +} +static inline Vec_Int_t * Vec_IntStartRange( int First, int Range ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( Range ); + p->nSize = Range; + for ( i = 0; i < Range; i++ ) + p->pArray[i] = First + i; + return p; +} + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given size and cleans it.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntStartNatural( int nSize ) +{ + Vec_Int_t * p; + int i; + p = Vec_IntAlloc( nSize ); + p->nSize = nSize; + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArray( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = pArray; + return p; +} + +/**Function************************************************************* + + Synopsis [Creates the vector from an integer array of the given size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntAllocArrayCopy( int * pArray, int nSize ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = nSize; + p->nCap = nSize; + p->pArray = ABC_ALLOC( int, nSize ); + memcpy( p->pArray, pArray, sizeof(int) * nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Duplicates the integer array.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDup( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nSize; + p->pArray = p->nCap? ABC_ALLOC( int, p->nCap ) : NULL; + memcpy( p->pArray, pVec->pArray, sizeof(int) * pVec->nSize ); + return p; +} + +/**Function************************************************************* + + Synopsis [Transfers the array into another vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntDupArray( Vec_Int_t * pVec ) +{ + Vec_Int_t * p; + p = ABC_ALLOC( Vec_Int_t, 1 ); + p->nSize = pVec->nSize; + p->nCap = pVec->nCap; + p->pArray = pVec->pArray; + pVec->nSize = 0; + pVec->nCap = 0; + pVec->pArray = NULL; + return p; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntZero( Vec_Int_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntErase( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_IntFree( Vec_Int_t * p ) +{ + ABC_FREE( p->pArray ); + ABC_FREE( p ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFreeP( Vec_Int_t ** p ) +{ + if ( *p == NULL ) + return; + ABC_FREE( (*p)->pArray ); + ABC_FREE( (*p) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntReleaseArray( Vec_Int_t * p ) +{ + int * pArray = p->pArray; + p->nCap = 0; + p->nSize = 0; + p->pArray = NULL; + return pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntArray( Vec_Int_t * p ) +{ + return p->pArray; +} +static inline int ** Vec_IntArrayP( Vec_Int_t * p ) +{ + return &p->pArray; +} +static inline int * Vec_IntLimit( Vec_Int_t * p ) +{ + return p->pArray + p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSize( Vec_Int_t * p ) +{ + return p->nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCap( Vec_Int_t * p ) +{ + return p->nCap; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_IntMemory( Vec_Int_t * p ) +{ + return !p ? 0.0 : 1.0 * sizeof(int) * p->nCap + sizeof(Vec_Int_t) ; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntry( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntEntryP( Vec_Int_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntWriteEntry( Vec_Int_t * p, int i, int Entry ) +{ + assert( i >= 0 && i < p->nSize ); + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntAddToEntry( Vec_Int_t * p, int i, int Addition ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray[i] += Addition; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntUpdateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) < Value ) + Vec_IntWriteEntry( p, i, Value ); +} +static inline void Vec_IntDowndateEntry( Vec_Int_t * p, int i, int Value ) +{ + if ( Vec_IntEntry( p, i ) > Value ) + Vec_IntWriteEntry( p, i, Value ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEntryLast( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[p->nSize-1]; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrow( Vec_Int_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntGrowResize( Vec_Int_t * p, int nCapMin ) +{ + p->nSize = nCapMin; + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( int, p->pArray, nCapMin ); + assert( p->pArray ); + p->nCap = nCapMin; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFill( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} +static inline void Vec_IntFillTwo( Vec_Int_t * p, int nSize, int FillEven, int FillOdd ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = (i & 1) ? FillOdd : FillEven; + p->nSize = nSize; +} +static inline void Vec_IntFillNatural( Vec_Int_t * p, int nSize ) +{ + int i; + Vec_IntGrow( p, nSize ); + for ( i = 0; i < nSize; i++ ) + p->pArray[i] = i; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Fills the vector with given number of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntFillExtra( Vec_Int_t * p, int nSize, int Fill ) +{ + int i; + if ( nSize <= p->nSize ) + return; + if ( nSize > 2 * p->nCap ) + Vec_IntGrow( p, nSize ); + else if ( nSize > p->nCap ) + Vec_IntGrow( p, 2 * p->nCap ); + for ( i = p->nSize; i < nSize; i++ ) + p->pArray[i] = Fill; + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntGetEntry( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntry( p, i ); +} +static inline int Vec_IntGetEntryFull( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + return Vec_IntEntry( p, i ); +} + +/**Function************************************************************* + + Synopsis [Returns the entry even if the place not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int * Vec_IntGetEntryP( Vec_Int_t * p, int i ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + return Vec_IntEntryP( p, i ); +} + +/**Function************************************************************* + + Synopsis [Inserts the entry even if the place does not exist.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSetEntry( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, 0 ); + Vec_IntWriteEntry( p, i, Entry ); +} +static inline void Vec_IntSetEntryFull( Vec_Int_t * p, int i, int Entry ) +{ + Vec_IntFillExtra( p, i + 1, -1 ); + Vec_IntWriteEntry( p, i, Entry ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntShrink( Vec_Int_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntClear( Vec_Int_t * p ) +{ + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPush( Vec_Int_t * p, int Entry ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->pArray[p->nSize++] = Entry; +} +static inline void Vec_IntPushTwo( Vec_Int_t * p, int Entry1, int Entry2 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); +} +static inline void Vec_IntPushThree( Vec_Int_t * p, int Entry1, int Entry2, int Entry3 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); +} +static inline void Vec_IntPushFour( Vec_Int_t * p, int Entry1, int Entry2, int Entry3, int Entry4 ) +{ + Vec_IntPush( p, Entry1 ); + Vec_IntPush( p, Entry2 ); + Vec_IntPush( p, Entry3 ); + Vec_IntPush( p, Entry4 ); +} +static inline void Vec_IntPushArray( Vec_Int_t * p, int * pEntries, int nEntries ) +{ + int i; + for ( i = 0; i < nEntries; i++ ) + Vec_IntPush( p, pEntries[i] ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushFirst( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize - 1; i >= 1; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[0] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrder( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] > Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} +static inline void Vec_IntPushOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( Vec_IntEntry(vCost, p->pArray[i]) > Vec_IntEntry(vCost, Entry) ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPushOrderReverse( Vec_Int_t * p, int Entry ) +{ + int i; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_IntGrow( p, 16 ); + else + Vec_IntGrow( p, 2 * p->nCap ); + } + p->nSize++; + for ( i = p->nSize-2; i >= 0; i-- ) + if ( p->pArray[i] < Entry ) + p->pArray[i+1] = p->pArray[i]; + else + break; + p->pArray[i+1] = Entry; +} + +/**Function************************************************************* + + Synopsis [Inserts the entry while preserving the increasing order.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUniqueOrder( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrder( p, Entry ); + return 0; +} +static inline int Vec_IntPushUniqueOrderCost( Vec_Int_t * p, int Entry, Vec_Int_t * vCost ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPushOrderCost( p, Entry, vCost ); + return 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPushUnique( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return 1; + Vec_IntPush( p, Entry ); + return 0; +} + +/**Function************************************************************* + + Synopsis [Returns the pointer to the next nWords entries in the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned * Vec_IntFetch( Vec_Int_t * p, int nWords ) +{ + if ( nWords == 0 ) + return NULL; + assert( nWords > 0 ); + p->nSize += nWords; + if ( p->nSize > p->nCap ) + { +// Vec_IntGrow( p, 2 * p->nSize ); + return NULL; + } + return ((unsigned *)p->pArray) + p->nSize - nWords; +} + +/**Function************************************************************* + + Synopsis [Returns the last entry and removes it from the list.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntPop( Vec_Int_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray[--p->nSize]; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFind( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + return i; + return -1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntRemove( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 0; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i == p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} +static inline int Vec_IntRemove1( Vec_Int_t * p, int Entry ) +{ + int i; + for ( i = 1; i < p->nSize; i++ ) + if ( p->pArray[i] == Entry ) + break; + if ( i >= p->nSize ) + return 0; + assert( i < p->nSize ); + for ( i++; i < p->nSize; i++ ) + p->pArray[i-1] = p->pArray[i]; + p->nSize--; + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntDrop( Vec_Int_t * p, int i ) +{ + int k; + assert( i >= 0 && i < Vec_IntSize(p) ); + p->nSize--; + for ( k = i; k < p->nSize; k++ ) + p->pArray[k] = p->pArray[k+1]; +} + +/**Function************************************************************* + + Synopsis [Interts entry at the index iHere. Shifts other entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntInsert( Vec_Int_t * p, int iHere, int Entry ) +{ + int i; + assert( iHere >= 0 && iHere <= p->nSize ); + Vec_IntPush( p, 0 ); + for ( i = p->nSize - 1; i > iHere; i-- ) + p->pArray[i] = p->pArray[i-1]; + p->pArray[i] = Entry; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMax( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best < p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Find entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntFindMin( Vec_Int_t * p ) +{ + int i, Best; + if ( p->nSize == 0 ) + return 0; + Best = p->pArray[0]; + for ( i = 1; i < p->nSize; i++ ) + if ( Best > p->pArray[i] ) + Best = p->pArray[i]; + return Best; +} + +/**Function************************************************************* + + Synopsis [Reverses the order of entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntReverseOrder( Vec_Int_t * p ) +{ + int i, Temp; + for ( i = 0; i < p->nSize/2; i++ ) + { + Temp = p->pArray[i]; + p->pArray[i] = p->pArray[p->nSize-1-i]; + p->pArray[p->nSize-1-i] = Temp; + } +} + +/**Function************************************************************* + + Synopsis [Removes odd entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemoveOdd( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i]; +} +static inline void Vec_IntRemoveEven( Vec_Int_t * p ) +{ + int i; + assert( (p->nSize & 1) == 0 ); + p->nSize >>= 1; + for ( i = 0; i < p->nSize; i++ ) + p->pArray[i] = p->pArray[2*i+1]; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntInvert( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( 0 ); + if ( Vec_IntSize(p) == 0 ) + return vRes; + Vec_IntFill( vRes, Vec_IntFindMax(p) + 1, Fill ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntWriteEntry( vRes, Entry, i ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_IntCondense( Vec_Int_t * p, int Fill ) +{ + int Entry, i; + Vec_Int_t * vRes = Vec_IntAlloc( Vec_IntSize(p) ); + Vec_IntForEachEntry( p, Entry, i ) + if ( Entry != Fill ) + Vec_IntPush( vRes, Entry ); + return vRes; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSum( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += p->pArray[i]; + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountEntry( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == Entry); + return Counter; +} +static inline int Vec_IntCountLarger( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > Entry); + return Counter; +} +static inline int Vec_IntCountSmaller( Vec_Int_t * p, int Entry ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] < Entry); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountPositive( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] > 0); + return Counter; +} +static inline int Vec_IntCountZero( Vec_Int_t * p ) +{ + int i, Counter = 0; + for ( i = 0; i < p->nSize; i++ ) + Counter += (p->pArray[i] == 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Checks if two vectors are equal.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntEqual( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + int i; + if ( p1->nSize != p2->nSize ) + return 0; + for ( i = 0; i < p1->nSize; i++ ) + if ( p1->pArray[i] != p2->pArray[i] ) + return 0; + return 1; +} + +/**Function************************************************************* + + Synopsis [Counts the number of common entries.] + + Description [Assumes that the entries are non-negative integers that + are not very large, so inversion of the array can be performed.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCountCommon( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + Vec_Int_t * vTemp; + int Entry, i, Counter = 0; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + vTemp = p1, p1 = p2, p2 = vTemp; + assert( Vec_IntSize(p1) >= Vec_IntSize(p2) ); + vTemp = Vec_IntInvert( p2, -1 ); + Vec_IntFillExtra( vTemp, Vec_IntFindMax(p1) + 1, -1 ); + Vec_IntForEachEntry( p1, Entry, i ) + if ( Vec_IntEntry(vTemp, Entry) >= 0 ) + Counter++; + Vec_IntFree( vTemp ); + return Counter; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare1( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_IntSortCompare2( int * pp1, int * pp2 ) +{ + // for some reason commenting out lines (as shown) led to crashing of the release version + if ( *pp1 > *pp2 ) + return -1; + if ( *pp1 < *pp2 ) // + return 1; + return 0; // +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSort( Vec_Int_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} +static inline void Vec_IntSortMulti( Vec_Int_t * p, int nMulti, int fReverse ) +{ + assert( Vec_IntSize(p) % nMulti == 0 ); + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize/nMulti, nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize/nMulti, nMulti*sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompare1 ); +} + +/**Function************************************************************* + + Synopsis [Leaves only unique entries.] + + Description [Returns the number of duplicated entried found.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqify( Vec_Int_t * p ) +{ + int i, k, RetValue; + if ( p->nSize < 2 ) + return 0; + Vec_IntSort( p, 0 ); + for ( i = k = 1; i < p->nSize; i++ ) + if ( p->pArray[i] != p->pArray[i-1] ) + p->pArray[k++] = p->pArray[i]; + RetValue = p->nSize - k; + p->nSize = k; + return RetValue; +} +static inline int Vec_IntCountDuplicates( Vec_Int_t * p ) +{ + int RetValue; + Vec_Int_t * pDup = Vec_IntDup( p ); + Vec_IntUniqify( pDup ); + RetValue = Vec_IntSize(p) - Vec_IntSize(pDup); + Vec_IntFree( pDup ); + return RetValue; +} +static inline int Vec_IntCheckUniqueSmall( Vec_Int_t * p ) +{ + int i, k; + for ( i = 0; i < p->nSize; i++ ) + for ( k = i+1; k < p->nSize; k++ ) + if ( p->pArray[i] == p->pArray[k] ) + return 0; + return 1; +} +static inline int Vec_IntCountUnique( Vec_Int_t * p ) +{ + int i, Count = 0, Max = Vec_IntFindMax(p); + unsigned char * pPres = ABC_CALLOC( unsigned char, Max+1 ); + for ( i = 0; i < p->nSize; i++ ) + if ( pPres[p->pArray[i]] == 0 ) + pPres[p->pArray[i]] = 1, Count++; + ABC_FREE( pPres ); + return Count; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique pairs.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntUniqifyPairs( Vec_Int_t * p ) +{ + int i, k, RetValue; + assert( p->nSize % 2 == 0 ); + if ( p->nSize < 4 ) + return 0; + Vec_IntSortMulti( p, 2, 0 ); + for ( i = k = 1; i < p->nSize/2; i++ ) + if ( p->pArray[2*i] != p->pArray[2*(i-1)] || p->pArray[2*i+1] != p->pArray[2*(i-1)+1] ) + { + p->pArray[2*k] = p->pArray[2*i]; + p->pArray[2*k+1] = p->pArray[2*i+1]; + k++; + } + RetValue = p->nSize/2 - k; + p->nSize = 2*k; + return RetValue; +} + +/**Function************************************************************* + + Synopsis [Counts the number of unique entries.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline unsigned Vec_IntUniqueHashKeyDebug( unsigned char * pStr, int nChars, int TableMask ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + { + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + printf( "%d : ", c ); + printf( "%3d ", pStr[c] ); + printf( "%12u ", Key ); + printf( "%12u ", Key&TableMask ); + printf( "\n" ); + } + return Key; +} +static inline void Vec_IntUniqueProfile( Vec_Int_t * vData, int * pTable, int * pNexts, int TableMask, int nIntSize ) +{ + int i, Key, Counter; + for ( i = 0; i <= TableMask; i++ ) + { + Counter = 0; + for ( Key = pTable[i]; Key != -1; Key = pNexts[Key] ) + Counter++; + if ( Counter < 7 ) + continue; + printf( "%d\n", Counter ); + for ( Key = pTable[i]; Key != -1; Key = pNexts[Key] ) + { +// Extra_PrintBinary( stdout, (unsigned *)Vec_IntEntryP(vData, Key*nIntSize), 40 ), printf( "\n" ); +// Vec_IntUniqueHashKeyDebug( (unsigned char *)Vec_IntEntryP(vData, Key*nIntSize), 4*nIntSize, TableMask ); + } + } + printf( "\n" ); +} + +static inline unsigned Vec_IntUniqueHashKey2( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[4] = {12582917, 25165843, 50331653, 100663319}; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += (unsigned)pStr[c] * s_BigPrimes[c & 3]; + return Key; +} + +static inline unsigned Vec_IntUniqueHashKey( unsigned char * pStr, int nChars ) +{ + static unsigned s_BigPrimes[16] = + { + 0x984b6ad9,0x18a6eed3,0x950353e2,0x6222f6eb,0xdfbedd47,0xef0f9023,0xac932a26,0x590eaf55, + 0x97d0a034,0xdc36cd2e,0x22736b37,0xdc9066b0,0x2eb2f98b,0x5d9c7baf,0x85747c9e,0x8aca1055 + }; + static unsigned s_BigPrimes2[16] = + { + 0x8d8a5ebe,0x1e6a15dc,0x197d49db,0x5bab9c89,0x4b55dea7,0x55dede49,0x9a6a8080,0xe5e51035, + 0xe148d658,0x8a17eb3b,0xe22e4b38,0xe5be2a9a,0xbe938cbb,0x3b981069,0x7f9c0c8e,0xf756df10 + }; + unsigned Key = 0; int c; + for ( c = 0; c < nChars; c++ ) + Key += s_BigPrimes2[(2*c)&15] * s_BigPrimes[(unsigned)pStr[c] & 15] + + s_BigPrimes2[(2*c+1)&15] * s_BigPrimes[(unsigned)pStr[c] >> 4]; + return Key; +} +static inline int * Vec_IntUniqueLookup( Vec_Int_t * vData, int i, int nIntSize, int * pNexts, int * pStart ) +{ + int * pData = Vec_IntEntryP( vData, i*nIntSize ); + for ( ; *pStart != -1; pStart = pNexts + *pStart ) + if ( !memcmp( pData, Vec_IntEntryP(vData, *pStart*nIntSize), sizeof(int) * nIntSize ) ) + return pStart; + return pStart; +} +static inline int Vec_IntUniqueCount( Vec_Int_t * vData, int nIntSize, Vec_Int_t ** pvMap ) +{ + int nEntries = Vec_IntSize(vData) / nIntSize; + int TableMask = (1 << pabc::Abc_Base2Log(nEntries)) - 1; + int * pTable = ABC_FALLOC( int, TableMask+1 ); + int * pNexts = ABC_FALLOC( int, TableMask+1 ); + int * pClass = ABC_ALLOC( int, nEntries ); + int i, Key, * pEnt, nUnique = 0; + assert( nEntries * nIntSize == Vec_IntSize(vData) ); + for ( i = 0; i < nEntries; i++ ) + { + pEnt = Vec_IntEntryP( vData, i*nIntSize ); + Key = TableMask & Vec_IntUniqueHashKey( (unsigned char *)pEnt, 4*nIntSize ); + pEnt = Vec_IntUniqueLookup( vData, i, nIntSize, pNexts, pTable+Key ); + if ( *pEnt == -1 ) + *pEnt = i, nUnique++; + pClass[i] = *pEnt; + } +// Vec_IntUniqueProfile( vData, pTable, pNexts, TableMask, nIntSize ); + ABC_FREE( pTable ); + ABC_FREE( pNexts ); + if ( pvMap ) + *pvMap = Vec_IntAllocArray( pClass, nEntries ); + else + ABC_FREE( pClass ); + return nUnique; +} +static inline Vec_Int_t * Vec_IntUniqifyHash( Vec_Int_t * vData, int nIntSize ) +{ + Vec_Int_t * vMap, * vUnique; + int i, Ent, nUnique = Vec_IntUniqueCount( vData, nIntSize, &vMap ); + vUnique = Vec_IntAlloc( nUnique * nIntSize ); + Vec_IntForEachEntry( vMap, Ent, i ) + { + if ( Ent < i ) continue; + assert( Ent == i ); + Vec_IntPushArray( vUnique, Vec_IntEntryP(vData, i*nIntSize), nIntSize ); + } + assert( Vec_IntSize(vUnique) == nUnique * nIntSize ); + Vec_IntFree( vMap ); + return vUnique; +} + +/**Function************************************************************* + + Synopsis [Comparison procedure for two integers.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntSortCompareUnsigned( unsigned * pp1, unsigned * pp2 ) +{ + if ( *pp1 < *pp2 ) + return -1; + if ( *pp1 > *pp2 ) + return 1; + return 0; +} + +/**Function************************************************************* + + Synopsis [Sorting the entries by their integer value.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSortUnsigned( Vec_Int_t * p ) +{ + qsort( (void *)p->pArray, p->nSize, sizeof(int), + (int (*)(const void *, const void *)) Vec_IntSortCompareUnsigned ); +} + +/**Function************************************************************* + + Synopsis [Returns the number of common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoCountCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int Counter = 0; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++, Counter++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Counter; +} + +/**Function************************************************************* + + Synopsis [Collects common entries.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoFindCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} +static inline int Vec_IntTwoFindCommonReverse( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 > *pBeg2 ) + pBeg1++; + else + pBeg2++; + } + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Collects and removes common entries] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemoveCommon( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + int * pBeg2New = vArr2->pArray; + Vec_IntClear( vArr ); + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1 ), pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + *pBeg2New++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg2New++ = *pBeg2++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + Vec_IntShrink( vArr2, pBeg2New - vArr2->pArray ); + return Vec_IntSize(vArr); +} + +/**Function************************************************************* + + Synopsis [Removes entries of the second one from the first one.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntTwoRemove( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + int * pBeg1New = vArr1->pArray; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg1New++ = *pBeg1++; + else + pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg1New++ = *pBeg1++; + Vec_IntShrink( vArr1, pBeg1New - vArr1->pArray ); + return Vec_IntSize(vArr1); +} + +/**Function************************************************************* + + Synopsis [Returns the result of merging the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoMerge2Int( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + int * pBeg = vArr->pArray; + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + *pBeg++ = *pBeg1++, pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + *pBeg++ = *pBeg1++; + else + *pBeg++ = *pBeg2++; + } + while ( pBeg1 < pEnd1 ) + *pBeg++ = *pBeg1++; + while ( pBeg2 < pEnd2 ) + *pBeg++ = *pBeg2++; + vArr->nSize = pBeg - vArr->pArray; + assert( vArr->nSize <= vArr->nCap ); + assert( vArr->nSize >= vArr1->nSize ); + assert( vArr->nSize >= vArr2->nSize ); +} +static inline Vec_Int_t * Vec_IntTwoMerge( Vec_Int_t * vArr1, Vec_Int_t * vArr2 ) +{ + Vec_Int_t * vArr = Vec_IntAlloc( vArr1->nSize + vArr2->nSize ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); + return vArr; +} +static inline void Vec_IntTwoMerge2( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr ) +{ + Vec_IntGrow( vArr, Vec_IntSize(vArr1) + Vec_IntSize(vArr2) ); + Vec_IntTwoMerge2Int( vArr1, vArr2, vArr ); +} + +/**Function************************************************************* + + Synopsis [Returns the result of splitting of the two vectors.] + + Description [Assumes that the vectors are sorted in the increasing order.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntTwoSplit( Vec_Int_t * vArr1, Vec_Int_t * vArr2, Vec_Int_t * vArr, Vec_Int_t * vArr1n, Vec_Int_t * vArr2n ) +{ + int * pBeg1 = vArr1->pArray; + int * pBeg2 = vArr2->pArray; + int * pEnd1 = vArr1->pArray + vArr1->nSize; + int * pEnd2 = vArr2->pArray + vArr2->nSize; + while ( pBeg1 < pEnd1 && pBeg2 < pEnd2 ) + { + if ( *pBeg1 == *pBeg2 ) + Vec_IntPush( vArr, *pBeg1++ ), pBeg2++; + else if ( *pBeg1 < *pBeg2 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + else + Vec_IntPush( vArr2n, *pBeg2++ ); + } + while ( pBeg1 < pEnd1 ) + Vec_IntPush( vArr1n, *pBeg1++ ); + while ( pBeg2 < pEnd2 ) + Vec_IntPush( vArr2n, *pBeg2++ ); +} + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSort( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] < pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} +static inline void Vec_IntSelectSortReverse( int * pArray, int nSize ) +{ + int temp, i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pArray[j] > pArray[best_i] ) + best_i = j; + temp = pArray[i]; + pArray[i] = pArray[best_i]; + pArray[best_i] = temp; + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntSelectSortCost( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) < Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} +static inline void Vec_IntSelectSortCostReverse( int * pArray, int nSize, Vec_Int_t * vCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( Vec_IntEntry(vCosts, pArray[j]) > Vec_IntEntry(vCosts, pArray[best_i]) ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + } +} + +static inline void Vec_IntSelectSortCost2( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] < pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} +static inline void Vec_IntSelectSortCost2Reverse( int * pArray, int nSize, int * pCosts ) +{ + int i, j, best_i; + for ( i = 0; i < nSize-1; i++ ) + { + best_i = i; + for ( j = i+1; j < nSize; j++ ) + if ( pCosts[j] > pCosts[best_i] ) + best_i = j; + ABC_SWAP( int, pArray[i], pArray[best_i] ); + ABC_SWAP( int, pCosts[i], pCosts[best_i] ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntPrint( Vec_Int_t * vVec ) +{ + int i, Entry; + printf( "Vector has %d entries: {", Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, Entry, i ) + printf( " %d", Entry ); + printf( " }\n" ); +} +static inline void Vec_IntPrintBinary( Vec_Int_t * vVec ) +{ + int i, Entry; + Vec_IntForEachEntry( vVec, Entry, i ) + printf( "%d", (int)(Entry != 0) ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_IntCompareVec( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( p1 == NULL || p2 == NULL ) + return (p1 != NULL) - (p2 != NULL); + if ( Vec_IntSize(p1) != Vec_IntSize(p2) ) + return Vec_IntSize(p1) - Vec_IntSize(p2); + return memcmp( Vec_IntArray(p1), Vec_IntArray(p2), sizeof(int)*Vec_IntSize(p1) ); +} + +/**Function************************************************************* + + Synopsis [Appends the contents of the second vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntAppend( Vec_Int_t * vVec1, Vec_Int_t * vVec2 ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendSkip( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int iVar ) +{ + int Entry, i; + Vec_IntForEachEntry( vVec2, Entry, i ) + if ( i != iVar ) + Vec_IntPush( vVec1, Entry ); +} +static inline void Vec_IntAppendMinus( Vec_Int_t * vVec1, Vec_Int_t * vVec2, int fMinus ) +{ + int Entry, i; + Vec_IntClear( vVec1 ); + Vec_IntForEachEntry( vVec2, Entry, i ) + Vec_IntPush( vVec1, fMinus ? -Entry : Entry ); +} + +/**Function************************************************************* + + Synopsis [Remapping attributes after objects were duplicated.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_IntRemapArray( Vec_Int_t * vOld2New, Vec_Int_t * vOld, Vec_Int_t * vNew, int nNew ) +{ + int iOld, iNew; + if ( Vec_IntSize(vOld) == 0 ) + return; + Vec_IntFill( vNew, nNew, 0 ); + Vec_IntForEachEntry( vOld2New, iNew, iOld ) + if ( iNew > 0 && iNew < nNew && iOld < Vec_IntSize(vOld) && Vec_IntEntry(vOld, iOld) != 0 ) + Vec_IntWriteEntry( vNew, iNew, Vec_IntEntry(vOld, iOld) ); +} + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/bill/bill/sat/solver/abc/vecWec.h b/lib/bill/bill/sat/solver/abc/vecWec.h new file mode 100644 index 0000000..badc653 --- /dev/null +++ b/lib/bill/bill/sat/solver/abc/vecWec.h @@ -0,0 +1,716 @@ +/**CFile**************************************************************** + + FileName [vecWec.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [Resizable arrays.] + + Synopsis [Resizable vector of resizable vectors.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: vecWec.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC__misc__vec__vecWec_h +#define ABC__misc__vec__vecWec_h + + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include + +ABC_NAMESPACE_HEADER_START + + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct Vec_Wec_t_ Vec_Wec_t; +struct Vec_Wec_t_ +{ + int nCap; + int nSize; + Vec_Int_t * pArray; +}; + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +// iterators through levels +#define Vec_WecForEachLevel( vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelVec( vLevels, vGlob, vVec, i ) \ + for ( i = 0; (i < Vec_IntSize(vLevels)) && (((vVec) = Vec_WecEntry(vGlob, Vec_IntEntry(vLevels, i))), 1); i++ ) +#define Vec_WecForEachLevelStart( vGlob, vVec, i, LevelStart ) \ + for ( i = LevelStart; (i < Vec_WecSize(vGlob)) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStop( vGlob, vVec, i, LevelStop ) \ + for ( i = 0; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart; (i < LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i++ ) +#define Vec_WecForEachLevelReverse( vGlob, vVec, i ) \ + for ( i = Vec_WecSize(vGlob)-1; (i >= 0) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelReverseStartStop( vGlob, vVec, i, LevelStart, LevelStop ) \ + for ( i = LevelStart-1; (i >= LevelStop) && (((vVec) = Vec_WecEntry(vGlob, i)), 1); i-- ) +#define Vec_WecForEachLevelTwo( vGlob1, vGlob2, vVec1, vVec2, i ) \ + for ( i = 0; (i < Vec_WecSize(vGlob1)) && (((vVec1) = Vec_WecEntry(vGlob1, i)), 1) && (((vVec2) = Vec_WecEntry(vGlob2, i)), 1); i++ ) + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [Allocates a vector with the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecAlloc( int nCap ) +{ + Vec_Wec_t * p; + p = ABC_ALLOC( Vec_Wec_t, 1 ); + if ( nCap > 0 && nCap < 8 ) + nCap = 8; + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecAllocExact( int nCap ) +{ + Vec_Wec_t * p; + assert( nCap >= 0 ); + p = ABC_ALLOC( Vec_Wec_t, 1 ); + p->nSize = 0; + p->nCap = nCap; + p->pArray = p->nCap? ABC_CALLOC( Vec_Int_t, p->nCap ) : NULL; + return p; +} +static inline Vec_Wec_t * Vec_WecStart( int nSize ) +{ + Vec_Wec_t * p; + p = Vec_WecAlloc( nSize ); + p->nSize = nSize; + return p; +} + +/**Function************************************************************* + + Synopsis [Resizes the vector to the given capacity.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecGrow( Vec_Wec_t * p, int nCapMin ) +{ + if ( p->nCap >= nCapMin ) + return; + p->pArray = ABC_REALLOC( Vec_Int_t, p->pArray, nCapMin ); + memset( p->pArray + p->nCap, 0, sizeof(Vec_Int_t) * (nCapMin - p->nCap) ); + p->nCap = nCapMin; +} +static inline void Vec_WecInit( Vec_Wec_t * p, int nSize ) +{ + Vec_WecGrow( p, nSize ); + p->nSize = nSize; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecEntry( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return p->pArray + i; +} +static inline Vec_Int_t * Vec_WecEntryLast( Vec_Wec_t * p ) +{ + assert( p->nSize > 0 ); + return p->pArray + p->nSize - 1; +} +static inline int Vec_WecEntryEntry( Vec_Wec_t * p, int i, int k ) +{ + return Vec_IntEntry( Vec_WecEntry(p, i), k ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecArray( Vec_Wec_t * p ) +{ + return p->pArray; +} +static inline int Vec_WecLevelId( Vec_Wec_t * p, Vec_Int_t * vLevel ) +{ + assert( p->pArray <= vLevel && vLevel < p->pArray + p->nSize ); + return vLevel - p->pArray; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCap( Vec_Wec_t * p ) +{ + return p->nCap; +} +static inline int Vec_WecSize( Vec_Wec_t * p ) +{ + return p->nSize; +} +static inline int Vec_WecLevelSize( Vec_Wec_t * p, int i ) +{ + assert( i >= 0 && i < p->nSize ); + return Vec_IntSize( p->pArray + i ); +} +static inline int Vec_WecSizeSize( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += Vec_IntSize(vVec); + return Counter; +} +static inline int Vec_WecSizeUsed( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevel( p, vVec, i ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} +static inline int Vec_WecSizeUsedLimits( Vec_Wec_t * p, int iStart, int iStop ) +{ + Vec_Int_t * vVec; + int i, Counter = 0; + Vec_WecForEachLevelStartStop( p, vVec, i, iStart, iStop ) + Counter += (int)(Vec_IntSize(vVec) > 0); + return Counter; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecShrink( Vec_Wec_t * p, int nSizeNew ) +{ + assert( p->nSize >= nSizeNew ); + p->nSize = nSizeNew; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecClear( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i; + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntClear( vVec ); + p->nSize = 0; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPush( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + { + Vec_WecGrow( p, Abc_MaxInt(2*p->nSize, Level + 1) ); + p->nSize = Level + 1; + } + Vec_IntPush( Vec_WecEntry(p, Level), Entry ); +} +static inline Vec_Int_t * Vec_WecPushLevel( Vec_Wec_t * p ) +{ + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + return Vec_WecEntryLast( p ); +} +static inline Vec_Int_t * Vec_WecInsertLevel( Vec_Wec_t * p, int i ) +{ + Vec_Int_t * pTemp; + if ( p->nSize == p->nCap ) + { + if ( p->nCap < 16 ) + Vec_WecGrow( p, 16 ); + else + Vec_WecGrow( p, 2 * p->nCap ); + } + ++p->nSize; + assert( i >= 0 && i < p->nSize ); + for ( pTemp = p->pArray + p->nSize - 2; pTemp >= p->pArray + i; pTemp-- ) + pTemp[1] = pTemp[0]; + Vec_IntZero( p->pArray + i ); + return p->pArray + i; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline double Vec_WecMemory( Vec_Wec_t * p ) +{ + int i; + double Mem; + if ( p == NULL ) return 0.0; + Mem = sizeof(Vec_Int_t) * Vec_WecCap(p); + for ( i = 0; i < p->nSize; i++ ) + Mem += sizeof(int) * Vec_IntCap( Vec_WecEntry(p, i) ); + return Mem; +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecZero( Vec_Wec_t * p ) +{ + p->pArray = NULL; + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecErase( Vec_Wec_t * p ) +{ + int i; + for ( i = 0; i < p->nCap; i++ ) + ABC_FREE( p->pArray[i].pArray ); + ABC_FREE( p->pArray ); + p->nSize = 0; + p->nCap = 0; +} +static inline void Vec_WecFree( Vec_Wec_t * p ) +{ + Vec_WecErase( p ); + ABC_FREE( p ); +} +static inline void Vec_WecFreeP( Vec_Wec_t ** p ) +{ + if ( *p == NULL ) + return; + Vec_WecFree( *p ); + *p = NULL; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPushUnique( Vec_Wec_t * p, int Level, int Entry ) +{ + if ( p->nSize < Level + 1 ) + Vec_WecPush( p, Level, Entry ); + else + Vec_IntPushUnique( Vec_WecEntry(p, Level), Entry ); +} + +/**Function************************************************************* + + Synopsis [Frees the vector.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecDup( Vec_Wec_t * p ) +{ + Vec_Wec_t * vNew; + Vec_Int_t * vVec; + int i, k, Entry; + vNew = Vec_WecAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vVec, i ) + Vec_IntForEachEntry( vVec, Entry, k ) + Vec_WecPush( vNew, i, Entry ); + return vNew; +} + +/**Function************************************************************* + + Synopsis [Sorting by array size.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare1( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare2( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntSize(p1) > Vec_IntSize(p2) ) + return -1; + if ( Vec_IntSize(p1) < Vec_IntSize(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSort( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare2 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare1 ); +} + + +/**Function************************************************************* + + Synopsis [Sorting by the first entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare3( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static int Vec_WecSortCompare4( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntry(p1,0) > Vec_IntEntry(p2,0) ) + return -1; + if ( Vec_IntEntry(p1,0) < Vec_IntEntry(p2,0) ) + return 1; + return 0; +} +static inline void Vec_WecSortByFirstInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare4 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare3 ); +} + +/**Function************************************************************* + + Synopsis [Sorting by the last entry.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static int Vec_WecSortCompare5( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static int Vec_WecSortCompare6( Vec_Int_t * p1, Vec_Int_t * p2 ) +{ + if ( Vec_IntEntryLast(p1) > Vec_IntEntryLast(p2) ) + return -1; + if ( Vec_IntEntryLast(p1) < Vec_IntEntryLast(p2) ) + return 1; + return 0; +} +static inline void Vec_WecSortByLastInt( Vec_Wec_t * p, int fReverse ) +{ + if ( fReverse ) + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare6 ); + else + qsort( (void *)p->pArray, p->nSize, sizeof(Vec_Int_t), + (int (*)(const void *, const void *)) Vec_WecSortCompare5 ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecPrint( Vec_Wec_t * p, int fSkipSingles ) +{ + Vec_Int_t * vVec; + int i, k, Entry; + Vec_WecForEachLevel( p, vVec, i ) + { + if ( fSkipSingles && Vec_IntSize(vVec) == 1 ) + continue; + printf( " %4d : {", i ); + Vec_IntForEachEntry( vVec, Entry, k ) + printf( " %d", Entry ); + printf( " }\n" ); + } +} +static inline void Vec_WecPrintLits( Vec_Wec_t * p ) +{ + Vec_Int_t * vVec; + int i, k, iLit; + Vec_WecForEachLevel( p, vVec, i ) + { + printf( " %4d : %2d {", i, Vec_IntSize(vVec) ); + Vec_IntForEachEntry( vVec, iLit, k ) + printf( " %c%d", Abc_LitIsCompl(iLit) ? '-' : '+', Abc_Lit2Var(iLit) ); + printf( " }\n" ); + } +} + +/**Function************************************************************* + + Synopsis [Derives the set of equivalence classes.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Wec_t * Vec_WecCreateClasses( Vec_Int_t * vMap ) +{ + Vec_Wec_t * vClasses; + int i, Entry; + vClasses = Vec_WecStart( Vec_IntFindMax(vMap) + 1 ); + Vec_IntForEachEntry( vMap, Entry, i ) + Vec_WecPush( vClasses, Entry, i ); + return vClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecCountNonTrivial( Vec_Wec_t * p, int * pnUsed ) +{ + Vec_Int_t * vClass; + int i, nClasses = 0; + *pnUsed = 0; + Vec_WecForEachLevel( p, vClass, i ) + { + if ( Vec_IntSize(vClass) < 2 ) + continue; + nClasses++; + (*pnUsed) += Vec_IntSize(vClass); + } + return nClasses; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline Vec_Int_t * Vec_WecCollectFirsts( Vec_Wec_t * p ) +{ + Vec_Int_t * vFirsts, * vLevel; + int i; + vFirsts = Vec_IntAlloc( Vec_WecSize(p) ); + Vec_WecForEachLevel( p, vLevel, i ) + if ( Vec_IntSize(vLevel) > 0 ) + Vec_IntPush( vFirsts, Vec_IntEntry(vLevel, 0) ); + return vFirsts; +} + + +/**Function************************************************************* + + Synopsis [Temporary vector marking.] + + Description [The vector should be static when the marking is used.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline int Vec_WecIntHasMark( Vec_Int_t * vVec ) { return (vVec->nCap >> 30) & 1; } +static inline void Vec_WecIntSetMark( Vec_Int_t * vVec ) { vVec->nCap |= (1<<30); } +static inline void Vec_WecIntXorMark( Vec_Int_t * vVec ) { vVec->nCap ^= (1<<30); } +static inline void Vec_WecMarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( !Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} +static inline void Vec_WecUnmarkLevels( Vec_Wec_t * vCubes, Vec_Int_t * vLevels ) +{ + Vec_Int_t * vCube; + int i; + Vec_WecForEachLevelVec( vLevels, vCubes, vCube, i ) + { + assert( Vec_WecIntHasMark( vCube ) ); + Vec_WecIntXorMark( vCube ); + } +} + +/**Function************************************************************* + + Synopsis [Removes 0-size vectors.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static inline void Vec_WecRemoveEmpty( Vec_Wec_t * vCubes ) +{ + Vec_Int_t * vCube; + int i, k = 0; + Vec_WecForEachLevel( vCubes, vCube, i ) + if ( Vec_IntSize(vCube) > 0 ) + vCubes->pArray[k++] = *vCube; + else + ABC_FREE( vCube->pArray ); + for ( i = k; i < Vec_WecSize(vCubes); i++ ) + Vec_IntZero( Vec_WecEntry(vCubes, i) ); + Vec_WecShrink( vCubes, k ); +// Vec_WecSortByFirstInt( vCubes, 0 ); +} + + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + diff --git a/lib/bill/bill/sat/solver/ghack.hpp b/lib/bill/bill/sat/solver/ghack.hpp new file mode 100644 index 0000000..44c9bc0 --- /dev/null +++ b/lib/bill/bill/sat/solver/ghack.hpp @@ -0,0 +1,5086 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#pragma once + +#ifndef Ghack_IntTypes_h +#define Ghack_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +# include +# include + +#endif + +#include + +#ifndef PRIu64 +#define PRIu64 "lu" +#define PRIi64 "ld" +#endif +//================================================================================================= + +#endif +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Ghack_XAlloc_h +#define Ghack_XAlloc_h + +#include +#include +#include + +namespace GHack { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else { + return mem; + } +} + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Vec_h +#define Ghack_Vec_h + +#include +#include + + + + +namespace GHack { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but + // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not + // happen given the way capacities are calculated (below). Essentially, all capacities are + // even, but INT_MAX is odd. + + const T& last (void) const { return data[sz-1]; } + T& last (void) { return data[sz-1]; } + + // Vector interface: + const T& operator [] (int index) const { return data[index]; } + T& operator [] (int index) { return data[index]; } + + // Duplicatation (preferred instead): + void copyTo(vec& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Alg_h +#define Ghack_Alg_h + + + +namespace GHack { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Ghack_Alloc_h +#define Ghack_Alloc_h + + + + +namespace GHack { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free (int size) { wasted_ += size; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + //printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + //printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +#endif +/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Heap_h +#define Ghack_Heap_h + + + +namespace GHack { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(vec& ns) { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (int i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (int i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Map_h +#define Ghack_Map_h + + + + +namespace GHack { + +//================================================================================================= +// Default hash/equals functions +// + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Queue_h +#define Ghack_Queue_h + + + +namespace GHack { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int i = 0; + for (int j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (int j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +#endif +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Sort_h +#define Ghack_Sort_h + + + +//================================================================================================= +// Some sorting algorithms for vec's + + +namespace GHack { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +#endif +/************************************************************************************[ParseUtils.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_ParseUtils_h +#define Ghack_ParseUtils_h + +#include +#include +#include + +namespace GHack { + +//------------------------------------------------------------------------------------------------- +// End-of-file detection functions for StreamBuffer and char*: +static inline bool isEof(const char* in) { return *in == '\0'; } + +//------------------------------------------------------------------------------------------------- +// Generic parse functions parametrized over the input-stream type. + + +template +static void skipWhitespace(B& in) { + while ((*in >= 9 && *in <= 13) || *in == 32) + ++in; } + + +template +static void skipLine(B& in) { + for (;;){ + if (isEof(in)) return; + if (*in == '\n') { ++in; return; } + ++in; } } + +template +static double parseDouble(B& in) { // only in the form X.XXXXXe-XX + bool neg= false; + double accu = 0.0; + double currentExponent = 1; + int exponent; + + skipWhitespace(in); + if(*in == EOF) return 0; + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '1' || *in > '9') printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + accu = (double)(*in - '0'); + ++in; + if (*in != '.') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + currentExponent = 0.1; + while (*in >= '0' && *in <= '9') + accu = accu + currentExponent * ((double)(*in - '0')), + currentExponent /= 10, + ++in; + if (*in != 'e') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + exponent = parseInt(in); // read exponent + accu *= pow(10,exponent); + return neg ? -accu:accu; +} + +template +static int parseInt(B& in) { + int val = 0; + bool neg = false; + skipWhitespace(in); + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '0' || *in > '9') fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + while (*in >= '0' && *in <= '9') + val = val*10 + (*in - '0'), + ++in; + return neg ? -val : val; } + + +// String matching: in case of a match the input iterator will be advanced the corresponding +// number of characters. +template +static bool match(B& in, const char* str) { + int i; + for (i = 0; str[i] != '\0'; i++) + if (in[i] != str[i]) + return false; + + in += i; + + return true; +} + +// String matching: consumes characters eagerly, but does not require random access iterator. +template +static bool eagerMatch(B& in, const char* str) { + for (; *str != '\0'; ++str, ++in) + if (*str != *in) + return false; + return true; } + + +//================================================================================================= +} + +#endif +/***************************************************************************************[Options.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_Options_h +#define Ghack_Options_h + +#include +#include +#include +#include + + + + + +namespace GHack { + +//================================================================================================== +// Top-level option parse/help functions: + + +extern void parseOptions (int& argc, char** argv, bool strict = false); +extern void printUsageAndExit(int argc, char** argv, bool verbose = false); +extern void setUsageHelp (const char* str); +extern void setHelpPrefixStr (const char* str); + + +//================================================================================================== +// Options is an abstract class that gives the interface for all types options: + + +class Option +{ + protected: + const char* name; + const char* description; + const char* category; + const char* type_name; + + static vec& getOptionList () { static vec options; return options; } + static const char*& getUsageString() { static const char* usage_str; return usage_str; } + static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; } + + struct OptionLt { + bool operator()(const Option* x, const Option* y) { + int test1 = strcmp(x->category, y->category); + return test1 < 0 || (test1 == 0 && strcmp(x->type_name, y->type_name) < 0); + } + }; + + Option(const char* name_, + const char* desc_, + const char* cate_, + const char* type_) : + name (name_) + , description(desc_) + , category (cate_) + , type_name (type_) + { + getOptionList().push(this); + } + + public: + virtual ~Option() {} + + virtual bool parse (const char* str) = 0; + virtual void help (bool verbose = false) = 0; + + friend void parseOptions (int& argc, char** argv, bool strict); + friend void printUsageAndExit (int argc, char** argv, bool verbose); + friend void setUsageHelp (const char* str); + friend void setHelpPrefixStr (const char* str); +}; + + +//================================================================================================== +// Range classes with specialization for floating types: + + +struct IntRange { + int begin; + int end; + IntRange(int b, int e) : begin(b), end(e) {} +}; + +struct Int64Range { + int64_t begin; + int64_t end; + Int64Range(int64_t b, int64_t e) : begin(b), end(e) {} +}; + +struct DoubleRange { + double begin; + double end; + bool begin_inclusive; + bool end_inclusive; + DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {} +}; + + +//================================================================================================== +// Double options: + + +class DoubleOption : public Option +{ + protected: + DoubleRange range; + double value; + + public: + DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false)) + : Option(n, d, c, ""), range(r), value(def) { + // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly. + } + + operator double (void) const { return value; } + operator double& (void) { return value; } + DoubleOption& operator=(double x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + double tmp = strtod(span, &end); + + if (end == NULL) + return false; + else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + // fprintf(stderr, "READ VALUE: %g\n", value); + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n", + name, type_name, + range.begin_inclusive ? '[' : '(', + range.begin, + range.end, + range.end_inclusive ? ']' : ')', + value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Int options: + + +class IntOption : public Option +{ + protected: + IntRange range; + int32_t value; + + public: + IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int32_t (void) const { return value; } + operator int32_t& (void) { return value; } + IntOption& operator= (int32_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int32_t tmp = strtol(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT32_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4d", range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT32_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4d", range.end); + + fprintf(stderr, "] (default: %d)\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +// Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll. +#ifndef _MSC_VER + +class Int64Option : public Option +{ + protected: + Int64Range range; + int64_t value; + + public: + Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int64_t (void) const { return value; } + operator int64_t& (void) { return value; } + Int64Option& operator= (int64_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int64_t tmp = strtoll(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT64_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4" PRIi64, range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT64_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4" PRIi64, range.end); + + fprintf(stderr, "] (default: %" PRIi64")\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; +#endif + +//================================================================================================== +// String option: + + +class StringOption : public Option +{ + const char* value; + public: + StringOption(const char* c, const char* n, const char* d, const char* def = NULL) + : Option(n, d, c, ""), value(def) {} + + operator const char* (void) const { return value; } + operator const char*& (void) { return value; } + StringOption& operator= (const char* x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + value = span; + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-10s = %8s\n", name, type_name); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Bool option: + + +class BoolOption : public Option +{ + bool value; + + public: + BoolOption(const char* c, const char* n, const char* d, bool v) + : Option(n, d, c, ""), value(v) {} + + operator bool (void) const { return value; } + operator bool& (void) { return value; } + BoolOption& operator=(bool b) { value = b; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (match(span, "-")){ + bool b = !match(span, "no-"); + + if (strcmp(span, name) == 0){ + value = b; + return true; } + } + + return false; + } + + virtual void help (bool verbose = false){ + + fprintf(stderr, " -%s, -no-%s", name, name); + + for (uint32_t i = 0; i < 32 - strlen(name)*2; i++) + fprintf(stderr, " "); + + fprintf(stderr, " "); + fprintf(stderr, "(default: %s)\n", value ? "on" : "off"); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + +//================================================================================================= +} + +#endif +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_System_h +#define Ghack_System_h + + + +//------------------------------------------------------------------------------------------------- + +namespace GHack { + +static inline double cpuTime(void); // CPU-time in seconds. +extern double memUsed(); // Memory in mega bytes (returns 0 for unsupported architectures). +extern double memUsedPeak(); // Peak-memory in mega bytes (returns 0 for unsupported architectures). + +} + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +static inline double GHack::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +#else +#include +#include +#include + +static inline double GHack::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +#endif + +#endif +/***********************************************************************************[SolverTypes.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Ghack_SolverTypes_h +#define Ghack_SolverTypes_h + +#include + + + + + + + +namespace GHack { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); + + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + + +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + +class lbool { + uint8_t value; + +public: + constexpr explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +constexpr auto l_True = GHack::lbool((uint8_t)0); +constexpr auto l_False = GHack::lbool((uint8_t)1); +constexpr auto l_Undef = GHack::lbool((uint8_t)2); + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +struct Clause; +typedef RegionAllocator::Ref CRef; + +struct Clause { + int t; + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned has_extra : 1; + unsigned reloced : 1; + unsigned lbd : 26; + unsigned canbedel : 1; + unsigned size : 32; + unsigned szWithoutSelectors : 32; + + } header; + union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, bool use_extra, bool learnt) { + header.mark = t = 0; + header.learnt = learnt; + header.has_extra = use_extra; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.canbedel = 1; + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.has_extra){ + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); } + } + +public: + void calcAbstraction() { + assert(header.has_extra); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + bool has_extra () const { return header.has_extra; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + float& activity () { assert(header.has_extra); return data[header.size].act; } + uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; } + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + void setLBD(int i) {header.lbd = i;} + // unsigned int& lbd () { return header.lbd; } + unsigned int lbd () const { return header.lbd; } + void setCanBeDel(bool b) {header.canbedel = b;} + bool canBeDel() {return header.canbedel;} + void setSizeWithoutSelectors (unsigned int n) {header.szWithoutSelectors = n; } + unsigned int sizeWithoutSelectors () const { return header.szWithoutSelectors; } + +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + +const CRef CRef_Undef = RegionAllocator::Ref_Undef; +class ClauseAllocator : public RegionAllocator +{ + static int clauseWord32Size(int size, bool has_extra){ + return (sizeof(Clause) + (sizeof(Lit) * (size + (int)has_extra))) / sizeof(uint32_t); } + public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + bool use_extra = learnt | extra_clause_field; + + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), use_extra)); + new (lea(cid)) Clause(ps, use_extra, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free(CRef cid) + { + Clause& c = operator[](cid); + RegionAllocator::free(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()) { + to[cr].t = c.t; + to[cr].activity() = c.activity(); + to[cr].setLBD(c.lbd()); + to[cr].setSizeWithoutSelectors(c.sizeWithoutSelectors()); + to[cr].setCanBeDel(c.canBeDel()); + } + else if (to[cr].has_extra()) to[cr].calcAbstraction(); + } +}; + + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + + public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void clean (const Idx& idx); + void smudge (const Idx& idx){ + if (dirty[toInt(idx)] == 0){ + dirty[toInt(idx)] = 1; + dirties.push(idx); + } + } + + void clear(bool free = true){ + occs .clear(free); + dirty .clear(free); + dirties.clear(free); + } +}; + + +template +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + + public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.has_extra); assert(other.header.has_extra); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + + +#endif +/***********************************************************************************[BoundedQueue.h] + Glucose -- Copyright (c) 2009, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +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. +**************************************************************************************************/ + + +#ifndef BoundedQueue_h +#define BoundedQueue_h + + + +//================================================================================================= + +namespace GHack { + +template +class bqueue { + vec elems; + int first; + int last; + unsigned long long sumofqueue; + int maxsize; + int queuesize; // Number of current elements (must be < maxsize !) + bool expComputed; + double exp,value; +public: + bqueue(void) : first(0), last(0), sumofqueue(0), maxsize(0), queuesize(0),expComputed(false) { } + + void initSize(int size) {growTo(size);exp = 2.0/(size+1);} // Init size of bounded size queue + + void push(T x) { + expComputed = false; + if (queuesize==maxsize) { + assert(last==first); // The queue is full, next value to enter will replace oldest one + sumofqueue -= elems[last]; + if ((++last) == maxsize) last = 0; + } else + queuesize++; + sumofqueue += x; + elems[first] = x; + if ((++first) == maxsize) {first = 0;last = 0;} + } + + T peek() { assert(queuesize>0); return elems[last]; } + void pop() {sumofqueue-=elems[last]; queuesize--; if ((++last) == maxsize) last = 0;} + + unsigned long long getsum() const {return sumofqueue;} + unsigned int getavg() const {return (unsigned int)(sumofqueue/((unsigned long long)queuesize));} + int maxSize() const {return maxsize;} + double getavgDouble() const { + double tmp = 0; + for(int i=0;i& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + void printLit(Lit l); + void printClause(CRef c); + void printInitialClause(CRef c); + // Convenience versions of 'toDimacs()': + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () const; + + // Incremental mode + void setIncrementalMode(); + void initNbInitialVars(int nb); + void printIncrementalStats(); + + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void garbageCollect(); + void checkGarbage(double gf); + void checkGarbage(); + + + + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + int verbosity; + int verbEveryConflicts; + int showModel; + // Constants For restarts + double K; + double R; + double sizeLBDQueue; + double sizeTrailQueue; + + // Constants for reduce DB + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB,I,O,G,H,Y,Z,A,e; + unsigned int lbLBDFrozenClause; + + // Constant for reducing clause + int lbSizeMinimizingClause; + unsigned int lbLBDMinimizingClause; + + double var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + // Certified UNSAT ( Thanks to Marijn Heule) + FILE* certifiedOutput; + bool certifiedUNSAT; + bool vbyte; + + void write_char (unsigned char c); + void write_lit (int n); + + + // Statistics: (read-only member variable) + // + uint64_t nbRemovedClauses,nbReducedClauses,nbDL2,nbBin,nbUn,nbReduceDB,solves, starts, decisions, rnd_decisions, propagations, conflicts,conflictsRestarts,nbstopsrestarts,nbstopsrestartssame,lastblockatrestart; + uint64_t dec_vars, clauses_literals, learnts_literals, max_literals, tot_literals; + +protected: + long curRestart; + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + + // Solver state: + // + int lastIndexRed; + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + double cla_inc; // Amount to bump next clause with. + vec activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + watchesBin; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + vec clauses; // List of problem clauses. + vec learnts,C,T; // List of learnt clauses. + + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec nbpos; + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap; // A priority queue of variables ordered with respect to the variable activity. + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + vec permDiff; // permDiff[var] contains the current conflict number... Used to count the number of LBD + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + vec lastDecisionLevel; +#endif + + ClauseAllocator ca; + + int nbclausesbeforereduce; // To know when it is time to reduce clause database + + bqueue trailQueue,lbdQueue; // Bounded queues for restarts. + float sumLBD; // used to compute the global average of LBD. Restarts... + int sumAssumptions; + + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + unsigned int MYFLAG; + + + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + + // Variables added for incremental mode + int incremental; // Use incremental SAT Solver + int nbVarsInitialFormula; // nb VAR in formula without assumptions (incremental SAT) + double totalTime4Sat,totalTime4Unsat; + int nbSatCalls,nbUnsatCalls; + vec assumptionPositions,initialPositions; + + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, vec & selectors, int& out_btlevel,unsigned int &nblevels,unsigned int &szWithoutSelectors); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int& nof_conflicts); // Search for a given number of conflicts. + lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). + void reduceDB (); // Reduce the set of learnt clauses. + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap (); + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void removeClause (CRef cr); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + unsigned int computeLBD(const vec & lits,int end=-1); + unsigned int computeLBD(const Clause &c); + void minimisationWithBinaryResolution(vec &out_learnt); + + void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + int level (Var x) const; + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + inline bool isSelector(Var v) {return (incremental && v>nbVarsInitialFormula);} + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } +inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } +inline void Solver::varBumpActivity(Var v, double inc) { + if ( (activity[v] += inc) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) + order_heap.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) + ca[learnts[i]].activity() *= 1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect(); } + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } + inline bool Solver::locked (const Clause& c) const { + if(c.size()>2) + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; + return + (value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c) + || + (value(c[1]) == l_True && reason(var(c[1])) != CRef_Undef && ca.lea(reason(var(c[1]))) == &c); + } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) dec_vars++; + else if (!b && decision[v]) dec_vars--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + + +//================================================================================================= +// Debug etc: + + +inline void Solver::printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void Solver::printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } +} + +inline void Solver::printInitialClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + if(!isSelector(var(c[i]))) { + printLit(c[i]); + printf(" "); + } + } +} + + +//================================================================================================= +} + +#endif +/***************************************************************************************[Solver.cc] + Glucose -- Copyright (c) 2013, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose are exactly the same as Minisat on which it is based on. (see below). + +--------------- + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 + + + + + + +#define M mark +#define Q conflicts +#define P push +#define B claBumpActivity + +namespace GHack { + +//================================================================================================= +// Options: + +static const char* _cat = "CORE"; +static const char* _cr = "CORE -- RESTART"; +static const char* _cred = "CORE -- REDUCE"; +static const char* _cm = "CORE -- MINIMIZE"; +static const char* _certified = "CORE -- CERTIFIED UNSAT"; + + + + +static BoolOption opt_incremental (_cat,"incremental", "Use incremental SAT solving",false); +static DoubleOption opt_K (_cr, "K", "The constant used to force restart", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_R (_cr, "R", "The constant used to block restart", 1.4, DoubleRange(1, false, 5, false)); +static IntOption opt_size_lbd_queue (_cr, "szLBDQueue", "The size of moving average for LBD (restarts)", 50, IntRange(10, INT32_MAX)); +static IntOption opt_size_trail_queue (_cr, "szTrailQueue", "The size of moving average for trail (block restarts)", 5000, IntRange(10, INT32_MAX)); + +static IntOption opt_first_reduce_db (_cred, "firstReduceDB", "The number of conflicts before the first reduce DB", 2000, IntRange(0, INT32_MAX)); +static IntOption opt_inc_reduce_db (_cred, "incReduceDB", "Increment for reduce DB", 300, IntRange(0, INT32_MAX)); +static IntOption opt_spec_inc_reduce_db (_cred, "specialIncReduceDB", "Special increment for reduce DB", 1000, IntRange(0, INT32_MAX)); +static IntOption opt_lb_lbd_frozen_clause (_cred, "minLBDFrozenClause", "Protect clauses if their LBD decrease and is lower than (for one turn)", 30, IntRange(0, INT32_MAX)); + +static IntOption opt_lb_size_minimzing_clause (_cm, "minSizeMinimizingClause", "The min size required to minimize clause", 30, IntRange(3, INT32_MAX)); +static IntOption opt_lb_lbd_minimzing_clause (_cm, "minLBDMinimizingClause", "The min LBD required to minimize clause", 6, IntRange(3, INT32_MAX)); + + +static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); +static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, DoubleRange(0, true, 1, true)); +static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); +static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); +static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); +static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); +/* +static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); +static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); +*/ +static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false)); + + +static BoolOption opt_certified (_certified, "certified", "Certified UNSAT using DRUP format", false); +static StringOption opt_certified_file (_certified, "certified-output", "Certified UNSAT output file", "NULL"); +static BoolOption opt_vbyte (_certified, "vbyte", "Emit proof in variable-byte encoding", false); + + +//================================================================================================= +// Constructor/Destructor: + + +inline Solver::Solver() : + + // Parameters (user settable): + // + verbosity (0) + , showModel (0) + , K (opt_K) + , R (opt_R) + , sizeLBDQueue (opt_size_lbd_queue) + , sizeTrailQueue (opt_size_trail_queue) + , firstReduceDB (opt_first_reduce_db) + , incReduceDB (opt_inc_reduce_db) + , specialIncReduceDB (opt_spec_inc_reduce_db) + , lbLBDFrozenClause (opt_lb_lbd_frozen_clause) + , lbSizeMinimizingClause (opt_lb_size_minimzing_clause) + , lbLBDMinimizingClause (opt_lb_lbd_minimzing_clause) + , var_decay (opt_var_decay) + , clause_decay (opt_clause_decay) + , random_var_freq (opt_random_var_freq) + , random_seed (opt_random_seed) + , ccmin_mode (opt_ccmin_mode) + , phase_saving (opt_phase_saving) + , rnd_pol (false) + , rnd_init_act (opt_rnd_init_act) + , garbage_frac (opt_garbage_frac) + , certifiedOutput (NULL) + , certifiedUNSAT (opt_certified) + , vbyte (opt_vbyte) + // Statistics: (formerly in 'SolverStats') + // + , nbRemovedClauses(0),nbReducedClauses(0), nbDL2(0),nbBin(0),nbUn(0) , nbReduceDB(0) + , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0),conflicts(0),conflictsRestarts(0),nbstopsrestarts(0),nbstopsrestartssame(0),lastblockatrestart(0) + , dec_vars(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) + , curRestart(1) + + , ok (true) + , cla_inc (1) + , var_inc (1) + , watches (WatcherDeleted(ca)) + , watchesBin (WatcherDeleted(ca)) + , qhead (0) + , simpDB_assigns (-1) + , simpDB_props (0) + , order_heap (VarOrderLt(activity)) + , progress_estimate (0) + , remove_satisfied (true) + // Resource constraints: + // + , conflict_budget (-1) + , propagation_budget (-1) + , asynch_interrupt (false) + , incremental(opt_incremental) + , nbVarsInitialFormula(INT32_MAX) +{ + MYFLAG=H=Y=O=e=0; + G=1; A=4; Z=15000; + // Initialize only first time. Useful for incremental solving, useless otherwise + lbdQueue.initSize(sizeLBDQueue); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + totalTime4Sat=0;totalTime4Unsat=0; + nbSatCalls=0;nbUnsatCalls=0; + + + if(certifiedUNSAT) { + if(!strcmp(opt_certified_file,"NULL")) { + vbyte = false; // Cannot write binary to stdout + certifiedOutput = fopen("/dev/stdout", "wb"); + } else { + certifiedOutput = fopen(opt_certified_file, "wb"); + } + // fprintf(certifiedOutput,"o proof DRUP\n"); + } +} + + +inline Solver::~Solver() +{ +} + + +/**************************************************************** + Set the incremental mode +****************************************************************/ + +// This function set the incremental mode to true. +// You can add special code for this mode here. + +inline void Solver::setIncrementalMode() { + incremental = true; +} + +// Number of variables without selectors +inline void Solver::initNbInitialVars(int nb) { + nbVarsInitialFormula = nb; +} + + +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// +inline Var Solver::newVar(bool sign, bool dvar) +{ + int v = nVars(); + watches .init(mkLit(v, false)); + watches .init(mkLit(v, true )); + watchesBin .init(mkLit(v, false)); + watchesBin .init(mkLit(v, true )); + assigns .push(l_Undef); + vardata .push(mkVarData(CRef_Undef, 0)); + //activity .push(0); + activity .push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen .push(0); + permDiff .push(0); + polarity .push(sign); + decision .push(); + trail .capacity(v+1); + setDecisionVar(v, dvar); + return v; +} + +inline unsigned char b[2097152]; +inline void Solver::write_char (unsigned char ch) { + b[e++] = ch; } + //if (putc_unlocked ((int) +#define write_char b[e++] = + //= EOF) exit(1); } + +inline void Solver::write_lit (int n) { + for (; n > 127; n >>= 7) + write_char (128 | (n & 127)); + write_char (n); if (e > 1048576) fwrite(b, 1, e, certifiedOutput), e = 0; } + +inline bool Solver::addClause_(vec& ps) +{ + assert(decisionLevel() == 0); + if (!ok) return false; + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + + vec oc; + oc.clear(); + + Lit p; int i, j, flag = 0; + if(certifiedUNSAT) { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) { + oc.push(ps[i]); + if (value(ps[i]) == l_True || ps[i] == ~p || value(ps[i]) == l_False) + flag = 1; + } + } + + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + if (value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if (value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if (i != j && (certifiedUNSAT)) { + if (vbyte) { + write_char('a'); + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + write_lit(2*(var(ps[i])+1) + sign(ps[i])); + write_lit(0); + + write_char('d'); + for (i = j = 0, p = lit_Undef; i < oc.size(); i++) + write_lit(2*(var(oc[i])+1) + sign(oc[i])); + write_lit(0); + } + else { + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + fprintf(certifiedOutput, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + + fprintf(certifiedOutput, "d "); + for (i = j = 0, p = lit_Undef; i < oc.size(); i++) + fprintf(certifiedOutput, "%i ", (var(oc[i]) + 1) * (-2 * sign(oc[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + if (ps.size() == 0) + return ok = false; + else if (ps.size() == 1){ + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + }else{ + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + + +inline void Solver::attachClause(CRef cr) { + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if(c.size()==2) { + watchesBin[~c[0]].push(Watcher(cr, c[1])); + watchesBin[~c[1]].push(Watcher(cr, c[0])); + } else { + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + } + if (c.learnt()) learnts_literals += c.size(); + else clauses_literals += c.size(); } + + + + +inline void Solver::detachClause(CRef cr, bool strict) { + const Clause& c = ca[cr]; + + assert(c.size() > 1); + if(c.size()==2) { + if (strict){ + remove(watchesBin[~c[0]], Watcher(cr, c[1])); + remove(watchesBin[~c[1]], Watcher(cr, c[0])); + }else{ + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watchesBin.smudge(~c[0]); + watchesBin.smudge(~c[1]); + } + } else { + if (strict){ + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + }else{ + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + } + if (c.learnt()) learnts_literals -= c.size(); + else clauses_literals -= c.size(); } + + +inline void Solver::removeClause(CRef cr) { + + Clause& c = ca[cr]; + + if (certifiedUNSAT) { + if (vbyte) { + write_char ('d'); + for (int i = 0; i < c.size(); i++) + write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit (0); + } + else { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + detachClause(cr); + // Don't leave pointers to free'd memory! + if (locked(c)) vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free(cr); +} + + +inline bool Solver::satisfied(const Clause& c) const { + if(incremental) // Check clauses with many selectors is too time consuming + return (value(c[0]) == l_True) || (value(c[1]) == l_True); + + // Default mode. + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) + return true; + return false; +} + +/************************************************************ + * Compute LBD functions + *************************************************************/ + +inline unsigned int Solver::computeLBD(const vec & lits,int end) { + int nblevels = 0; + MYFLAG++; + + if(incremental) { // ----------------- INCREMENTAL MODE + if(end==-1) end = lits.size(); + unsigned int nbDone = 0; + for(int i=0;i=end) break; + if(isSelector(var(lits[i]))) continue; + nbDone++; + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for(int i=0;i=c.sizeWithoutSelectors()) break; + if(isSelector(var(c[i]))) continue; + nbDone++; + int l = level(var(c[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ + for(int i=0;i &out_learnt) { + + // Find the LBD measure + unsigned int lbd = computeLBD(out_learnt); + Lit p = ~out_learnt[0]; + + if(lbd<=lbLBDMinimizingClause){ + MYFLAG++; + + for(int i = 1;i& wbin = watchesBin[p]; + int nb = 0; + for(int k = 0;k0) { + nbReducedClauses++; + for(int i = 1;i level){ + for (int c = trail.size()-1; c >= trail_lim[level]; c--){ + Var x = var(trail[c]); + assigns [x] = l_Undef; + if (phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) + polarity[x] = sign(trail[c]); + insertVarOrder(x); } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + + +//================================================================================================= +// Major methods: + + +inline Lit Solver::pickBranchLit() +{ + Var next = var_Undef; + + // Random decision: + if (drand(random_seed) < random_var_freq && !order_heap.empty()){ + next = order_heap[irand(random_seed,order_heap.size())]; + if (value(next) == l_Undef && decision[next]) + rnd_decisions++; } + + // Activity based decision: + while (next == var_Undef || value(next) != l_Undef || !decision[next]) + if (order_heap.empty()){ + next = var_Undef; + break; + }else + next = order_heap.removeMin(); + + return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); +} + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +inline void Solver::analyze(CRef confl, vec& out_learnt,vec&selectors, int& out_btlevel,unsigned int &lbd,unsigned int &szWithoutSelectors) +{ + int pathC = 0; + Lit p = lit_Undef; + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + + do{ + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause& c = ca[confl]; + + // Special case for binary clauses + // The first one has to be SAT + if( p != lit_Undef && c.size()==2 && value(c[0])==l_False) { + + assert(value(c[1])==l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + if (0 && c.learnt()) + claBumpActivity(c); + +#ifdef DYNAMICNBLEVEL + // DYNAMIC NBLEVEL trick (see competition'09 companion paper) + if(c.learnt() && c.M() != 3) { + unsigned int nblevels = I = computeLBD(c); + if(nblevels 0){ + if(!isSelector(var(q))) + varBumpActivity(var(q)); + seen[var(q)] = 1; + if (level(var(q)) >= decisionLevel()) { + pathC++; +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(!isSelector(var(q)) && (reason(var(q))!= CRef_Undef) && ca[reason(var(q))].learnt()) + lastDecisionLevel.push(q); +#endif + + } else { + if(isSelector(var(q))) { + assert(value(q) == l_False); + selectors.push(q); + } else + out_learnt.push(q); + } + } + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]); + p = trail[index+1]; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + }while (pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + + for(int i = 0;i 0){ + out_learnt[j++] = out_learnt[i]; + break; } + } + } + }else + i = j = out_learnt.size(); + + max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + tot_literals += out_learnt.size(); + + + /* *************************************** + Minimisation with binary clauses of the asserting clause + First of all : we look for small clauses + Then, we reduce clauses with small LBD. + Otherwise, this can be useless + */ + if(!incremental && out_learnt.size()<=lbSizeMinimizingClause) { + minimisationWithBinaryResolution(out_learnt); + } + // Find correct backtrack level: + // + if (out_learnt.size() == 1) + out_btlevel = 0; + else{ + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for (int i = 2; i < out_learnt.size(); i++) + if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } + + + // Compute the size of the clause without selectors (incremental mode) + if(incremental) { + szWithoutSelectors = 0; + for(int i=0;i0) break; + } + } else + szWithoutSelectors = out_learnt.size(); + + // Compute LBD + lbd = computeLBD(out_learnt,out_learnt.size()-selectors.size()); + + +#ifdef UPDATEVARACTIVITY + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(lastDecisionLevel.size()>0) { + for(int i = 0;i 0){ + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause& c = ca[reason(var(analyze_stack.last()))]; analyze_stack.pop(); + if(c.size()==2 && value(c[0])==l_False) { + assert(value(c[1])==l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + for (int i = 1; i < c.size(); i++){ + Lit p = c[i]; + if (!seen[var(p)] && level(var(p)) > 0){ + if (reason(var(p)) != CRef_Undef && (abstractLevel(var(p)) & abstract_levels) != 0){ + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + }else{ + for (int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +inline void Solver::analyzeFinal(Lit p, vec& out_conflict) +{ + out_conflict.clear(); + out_conflict.push(p); + + if (decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for (int i = trail.size()-1; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + if (reason(x) == CRef_Undef){ + assert(level(x) > 0); + out_conflict.push(~trail[i]); + }else{ + Clause& c = ca[reason(x)]; + // for (int j = 1; j < c.size(); j++) Minisat (glucose 2.0) loop + // Bug in case of assumptions due to special data structures for Binary. + // Many thanks to Sam Bayless (sbayless@cs.ubc.ca) for discover this bug. + for (int j = ((c.size()==2) ? 0:1); j < c.size(); j++) + if (level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + + +inline void Solver::uncheckedEnqueue(Lit p, CRef from) +{ + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel()); + trail.push_(p); +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +inline CRef Solver::propagate() +{ + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watchesBin.cleanAll(); + while (qhead < trail.size()){ + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec& ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + + // First, Propagate binary clauses + vec& wbin = watchesBin[p]; + + for(int k = 0;kblocker; + if (value(blocker) == l_True){ + *j++ = *i++; continue; } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True){ + + *j++ = w; continue; } + + // Look for new watch: + if(incremental) { // ----------------- INCREMENTAL MODE + int choosenPos = -1; + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + if(decisionLevel()>assumptions.size()) { + choosenPos = k; + break; + } else { + choosenPos = k; + + if(value(c[k])==l_True || !isSelector(var(c[k]))) { + break; + } + } + + } + } + if(choosenPos!=-1) { + c[1] = c[choosenPos]; c[choosenPos] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } else { // ----------------- DEFAULT MODE (NOT INCREMENTAL) + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + c[1] = c[k]; c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } + } + + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if (value(first) == l_False){ + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + }else { + uncheckedEnqueue(first, cr); + + + } + NextClause:; + } + ws.shrink(i - j); + } + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ +struct reduceDB_lt { + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} + bool operator () (CRef x, CRef y) { +/* + + // Main criteria... Like in MiniSat we keep all binary clauses + if(ca[x].size()> 2 && ca[y].size()==2) return 1; + + if(ca[y].size()>2 && ca[x].size()==2) return 0; + if(ca[x].size()==2 && ca[y].size()==2) return 0; + + // Second one based on literal block distance + if(ca[x].lbd()> ca[y].lbd()) return 1; + if(ca[x].lbd()< ca[y].lbd()) return 0; +*/ + + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + //return x->size() < y->size(); + + //return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + +inline void Solver::reduceDB() +{ + + int i, j; + nbReduceDB++; + sort(learnts, reduceDB_lt(ca)); + + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + //if(ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd()<=3) nbclausesbeforereduce +=specialIncReduceDB; + // Useless :-) + //if(ca[learnts.last()].lbd()<=5) nbclausesbeforereduce +=specialIncReduceDB; + + + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + int limit = learnts.size() / 2; + + for (i = j = 0; i < learnts.size(); i++){ + Clause& c = ca[learnts[i]]; + if (!c.M()) + if (c.lbd()>2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + nbRemovedClauses++; + } + else { + if(!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + checkGarbage(); +} + + +inline void Solver::removeSatisfied(vec& cs) +{ + + int i, j; + for (i = j = 0; i < cs.size(); i++){ + Clause& c = ca[cs[i]]; + if (c.M() == O) + + + if (satisfied(c)) + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + + +inline void Solver::rebuildOrderHeap() +{ + vec vs; + for (Var v = 0; v < nVars(); v++) + if (decision[v] && value(v) == l_Undef) + vs.push(v); + order_heap.build(vs); +} + + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +inline bool Solver::simplify() +{ + assert(decisionLevel() == 0); + + if (!ok || propagate() != CRef_Undef) + return ok = false; + + if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + #define S removeSatisfied + O = 3; S(C); + O = 2; S(T); + O = 0; + removeSatisfied(learnts); + if (remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + checkGarbage(); + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +inline lbool Solver::search(int& n//of_conflicts + ) +{ + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause,selectors; + unsigned int nblevels,szWoutSelectors; + bool blocked=false; + starts++; + for (;;){ + CRef confl = propagate(); + if (confl != CRef_Undef){ + // CONFLICT + conflicts++; conflictC++;conflictsRestarts++; + Y--; Z--; n--; + if (Q == 100000 && C.size() < 100) A = 6; + if(0 && conflicts%5000==0 && var_decay<0.95) + var_decay += 0.01; + + if (0 && verbosity >= 1 && conflicts%verbEveryConflicts==0){ + printf("c | %8d %7d %5d | %7d %8d %8d | %5d %8d %6d %8d | %6.3f %% |\n", + (int)starts,(int)nbstopsrestarts, (int)(conflicts/starts), + (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, + (int)nbReduceDB, nLearnts(), (int)nbDL2,(int)nbRemovedClauses, progressEstimate()*100); + } + if (decisionLevel() == 0) { + return l_False; + + } + + /* + trailQueue.push(trail.size()); + // BLOCK RESTART (CP 2012 paper) + if( conflictsRestarts>LOWER_BOUND_FOR_BLOCKING_RESTART && lbdQueue.isvalid() && trail.size()>R*trailQueue.getavg()) { + lbdQueue.fastclear(); + nbstopsrestarts++; + if(!blocked) {lastblockatrestart=starts;nbstopsrestartssame++;blocked=true;} + } + */ + + learnt_clause.clear(); + selectors.clear(); + analyze(confl, learnt_clause, selectors,backtrack_level,nblevels,szWoutSelectors); + + if (G){ + H++; + lbdQueue.push(nblevels); + sumLBD += nblevels > 50 ? 50 : nblevels; + } + + + cancelUntil(backtrack_level); + + if (certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < learnt_clause.size(); i++) + write_lit(2*(var(learnt_clause[i])+1) + sign(learnt_clause[i])); + write_lit(0); + } + else { + for (int i = 0; i < learnt_clause.size(); i++) + fprintf(certifiedOutput, "%i " , (var(learnt_clause[i]) + 1) * + (-2 * sign(learnt_clause[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (learnt_clause.size() == 1){ + uncheckedEnqueue(learnt_clause[0]);nbUn++; + }else{ + #define EE ca[cr] + CRef cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(I = nblevels); + ca[cr].setSizeWithoutSelectors(szWoutSelectors); + if(nblevels<=2) nbDL2++; // stats + if(ca[cr].size()==2) nbBin++; // stats + if (I < A){ + C.P(cr); + EE.M(3); + }else if (I < 7){ + T.P(cr); + EE.M(2); + EE.t = Q; + }else{ + learnts.push(cr); B(EE); } + attachClause(cr); + + //claBumpActivity(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + } + varDecayActivity(); + claDecayActivity(); + + + }else{ + // Our dynamic restart, see the SAT09 competition compagnion paper + if ((!G && n <= 0) || (G && + ( lbdQueue.isvalid() && ((lbdQueue.getavg()*(n > 0 ? K : .9)) > (sumLBD / H//conflictsRestarts + ))))) { + lbdQueue.fastclear(); + progress_estimate = progressEstimate(); + int bt = 0; + if(incremental) { // DO NOT BACKTRACK UNTIL 0.. USELESS + bt = (decisionLevel()=curRestart* nbclausesbeforereduce) + { + + assert(learnts.size()>0); + curRestart = (conflicts/ nbclausesbeforereduce)+1; + reduceDB(); + nbclausesbeforereduce += incReduceDB; + Z = 15000; + } + + Lit next = lit_Undef; + while (decisionLevel() < assumptions.size()){ + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if (value(p) == l_True){ + // Dummy decision level: + newDecisionLevel(); + }else if (value(p) == l_False){ + analyzeFinal(~p, conflict); + return l_False; + }else{ + next = p; + break; + } + } + + if (next == lit_Undef){ + // New variable decision: + decisions++; + next = pickBranchLit(); + + if (next == lit_Undef){ + // printf("c last restart ## conflicts : %d %d \n",conflictC,decisionLevel()); + // Model found: + return l_True; + } + } + + // Increase decision level and enqueue 'next' + newDecisionLevel(); + uncheckedEnqueue(next); + } + } +} + + +inline double Solver::progressEstimate() const +{ + double progress = 0; + double F = 1.0 / nVars(); + + for (int i = 0; i <= decisionLevel(); i++){ + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + +inline void Solver::printIncrementalStats() { + + printf("c---------- Glucose Stats -------------------------\n"); + printf("c restarts : %lld\n", starts); + printf("c nb ReduceDB : %lld\n", nbReduceDB); + printf("c nb removed Clauses : %lld\n",nbRemovedClauses); + printf("c nb learnts DL2 : %lld\n", nbDL2); + printf("c nb learnts size 2 : %lld\n", nbBin); + printf("c nb learnts size 1 : %lld\n", nbUn); + + printf("c conflicts : %lld \n",conflicts); + printf("c decisions : %lld\n",decisions); + printf("c propagations : %lld\n",propagations); + + printf("c SAT Calls : %d in %g seconds\n",nbSatCalls,totalTime4Sat); + printf("c UNSAT Calls : %d in %g seconds\n",nbUnsatCalls,totalTime4Unsat); + printf("c--------------------------------------------------\n"); + + +} + + +// NOTE: assumptions passed in member-variable 'assumptions'. +inline lbool Solver::solve_() +{ + + if(incremental && certifiedUNSAT) { + printf("Can not use incremental and certified unsat in the same time\n"); + exit(-1); + } + model.clear(); + conflict.clear(); + if (!ok) return l_False; + double curTime = cpuTime(); + + + solves++; + + + + lbool status = l_Undef; + if(!incremental && verbosity>=1) { + printf("c ========================================[ MAGIC CONSTANTS ]==============================================\n"); + printf("c | Constants are supposed to work well together :-) |\n"); + printf("c | however, if you find better choices, please let us known... |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + printf("c | | | |\n"); + printf("c | - Restarts: | - Reduce Clause DB: | - Minimize Asserting: |\n"); + printf("c | * LBD Queue : %6d | * First : %6d | * size < %3d |\n",lbdQueue.maxSize(),nbclausesbeforereduce,lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * Inc : %6d | * lbd < %3d |\n",trailQueue.maxSize(),incReduceDB,lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Special : %6d | |\n",K,specialIncReduceDB); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n",R,lbLBDFrozenClause); + printf("c | | | |\n"); +printf("c ==================================[ Search Statistics (every %6d conflicts) ]=========================\n",verbEveryConflicts); + printf("c | |\n"); + + printf("c | RESTARTS | ORIGINAL | LEARNT | Progress |\n"); + printf("c | NB Blocked Avg Cfc | Vars Clauses Literals | Red Learnts LBD2 Removed | |\n"); + printf("c =========================================================================================================\n"); + } + + // Search: + int curr_restarts = 0, a = 91, w; + while (status == l_Undef){ + w = !H ? 10000 : a + G * a; + + var_decay = G ? .95 : .999; + while (status == l_Undef && w > 0) + status = search(w); // the parameter is useless in glucose, kept to allow modifications + + //if (!withinBudget()) break; + curr_restarts++; + + if (!(G = !G)) a += a / 10; + } + + if (!incremental && verbosity >= 1) + printf("c =========================================================================================================\n"); + + + if (certifiedUNSAT){ // Want certified output + if (status == l_False) { + if (vbyte) { + write_char('a'); + write_lit(0); + fwrite(b, 1, e, certifiedOutput), e = 0; + } + else { + fprintf(certifiedOutput, "0\n"); + } + } + fclose(certifiedOutput); + } + + + if (status == l_True){ + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + }else if (status == l_False && conflict.size() == 0) + ok = false; + + + + cancelUntil(0); + + double finalTime = cpuTime(); + if(status==l_True) { + nbSatCalls++; + totalTime4Sat +=(finalTime-curTime); + } + if(status==l_False) { + nbUnsatCalls++; + totalTime4Unsat +=(finalTime-curTime); + } + + + return status; +} + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec& map, Var& max) +{ + if (map.size() <= x || map[x] == -1){ + map.growTo(x+1, -1); + map[x] = max++; + } + return map[x]; +} + + +inline void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) +{ + if (satisfied(c)) return; + + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1); + fprintf(f, "0\n"); +} + + +inline void Solver::toDimacs(const char *file, const vec& assumps) +{ + FILE* f = fopen(file, "wr"); + if (f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + + +inline void Solver::toDimacs(FILE* f, const vec& assumps) +{ + // Handle case when solver is in contradictory state: + if (!ok){ + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; } + assumps.copyTo(assumptions); + + vec map; Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int cnt = 0; + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) + cnt++; + + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])){ + Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumptions.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for (int i = 0; i < assumptions.size(); i++){ + assert(value(assumptions[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumptions[i]) ? "-" : "", mapVar(var(assumptions[i]), map, max)+1); + } + + for (int i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + if (verbosity > 0) + printf("Wrote %d clauses with %d variables.\n", cnt, max); +} + + +//================================================================================================= +// Garbage Collection methods: + +inline void Solver::relocAll(ClauseAllocator& to) +{ + // All watchers: + // + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watchesBin.cleanAll(); + for (int v = 0; v < nVars(); v++) + for (int s = 0; s < 2; s++){ + Lit p = mkLit(v, s); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec& ws = watches[p]; + for (int j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec& ws2 = watchesBin[p]; + for (int j = 0; j < ws2.size(); j++) + ca.reloc(ws2[j].cref, to); + } + + // All reasons: + // + for (int i = 0; i < trail.size(); i++){ + Var v = var(trail[i]); + + if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + #define X(V) for (I = 0; I < V.size();) ca.reloc(V[I++], to); + X(C); + X(T); + for (int i = 0; i < learnts.size(); i++) + ca.reloc(learnts[i], to); + + // All original: + // + for (int i = 0; i < clauses.size(); i++) + ca.reloc(clauses[i], to); +} + + +inline void Solver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} +} +/************************************************************************************[SimpSolver.h] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Ghack_SimpSolver_h +#define Ghack_SimpSolver_h + + + + + +namespace GHack { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + + // Memory managment: + // + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + int parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + bool use_simplification; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr); + bool strengthenClause (CRef cr, Lit l); + void cleanUpClauses (); + bool implied (const vec& c); + void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +//================================================================================================= +} + +#endif +/***********************************************************************************[SimpSolver.cc] +Copyright (c) 2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + + + + +namespace GHack { + +//================================================================================================= +// Options: + + +static BoolOption opt_use_asymm ("SIMP", "asymm", "Shrink clauses by asymmetric branching.", false); +static BoolOption opt_use_rcheck ("SIMP", "rcheck", "Check if a clause is already implied. (costly)", false); +static BoolOption opt_use_elim ("SIMP", "elim", "Perform variable elimination.", true); +static IntOption opt_grow ("SIMP", "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); +static IntOption opt_clause_lim ("SIMP", "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX)); +static IntOption opt_subsumption_lim ("SIMP", "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX)); +static DoubleOption opt_simp_garbage_frac("SIMP", "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL, false)); + + +//================================================================================================= +// Constructor/Destructor: + + +inline SimpSolver::SimpSolver() : + grow (opt_grow) + , clause_lim (opt_clause_lim) + , subsumption_lim (opt_subsumption_lim) + , simp_garbage_frac (opt_simp_garbage_frac) + , use_asymm (opt_use_asymm) + , use_rcheck (opt_use_rcheck) + , use_elim (opt_use_elim) + , merges (0) + , asymm_lits (0) + , eliminated_vars (0) + , elimorder (1) + , use_simplification (true) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (0) + , n_touched (0) +{ + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + + +inline SimpSolver::~SimpSolver() +{ +} + + +inline Var SimpSolver::newVar(bool sign, bool dvar) { + Var v = Solver::newVar(sign, dvar); + + frozen .push((char)false); + eliminated.push((char)false); + + if (use_simplification){ + n_occ .push(0); + n_occ .push(0); + occurs .init(v); + touched .push(0); + elim_heap .insert(v); + } + return v; } + + + +inline lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + + do_simp &= use_simplification; + + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("===============================================================================\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + return result; +} + + + +inline bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if(!parsing && certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < ps.size(); i++) + write_lit(2*(var(ps[i])+1) + sign(ps[i])); + write_lit(0); + } + else { + for (int i = 0; i < ps.size(); i++) + fprintf(certifiedOutput, "%i " , (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (use_simplification && clauses.size() == nclauses + 1){ + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++){ + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + + +inline void SimpSolver::removeClause(CRef cr) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++){ + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr); +} + + +inline bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < c.size(); i++) + if (c[i] != l) write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + for (int i = 0; i < c.size(); i++) + if (c[i] != l) fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (c.size() == 2){ + removeClause(cr); + c.strengthen(l); + }else{ + if (certifiedUNSAT) { + if (vbyte) { + write_char('d'); + for (int i = 0; i < c.size(); i++) + write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + for (int i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) + if (ps[j] == ~qs[i]) + return false; + else + goto next; + out_clause.push(qs[i]); + } + next:; + } + + for (int i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +// Returns FALSE if clause is always satisfied. +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*)ps; + const Lit* __qs = (const Lit*)qs; + + size = ps.size()-1; + + for (int i = 0; i < qs.size(); i++){ + if (var(__qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + size++; + } + next:; + } + + return true; +} + + +inline void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) return; + + int i,j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0){ + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + + +inline bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True){ + cancelUntil(0); + return false; + }else if (value(c[i]) != l_False){ + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + + +// Backward subsumption + backward subsumption resolution +inline bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt){ + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); } + + CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*)_cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){ + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error){ + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +inline bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v && value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + else + l = c[i]; + + if (propagate() != CRef_Undef){ + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + }else + cancelUntil(0); + + return true; +} + + +inline bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++){ + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + + + +inline bool SimpSolver::eliminateVar(Var v) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (int i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && + (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses: + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()){ + for (int i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + }else{ + for (int i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + } + + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (int i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); + if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + + +inline bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++){ + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++){ + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + + } + + return true; +} + + +inline void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + for (i = elimclauses.size()-1; i > 0; i -= j){ + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + + +inline bool SimpSolver::eliminate(bool turn_off_elim) +{ + if (!simplify()) + return false; + else if (!use_simplification) + return true; + + // Main simplification loop: + // + + int toPerform = 1; clauses.size()<=4800000; + + if(!toPerform) { + printf("c Too many clauses... No preprocessing\n"); + } + + while (toPerform && (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0)){ + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && + !backwardSubsumptionCheck(true)){ + ok = false; goto cleanup; } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt){ + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++){ + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) break; + + if (isEliminated(elim) || value(elim) != l_Undef) continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("elimination left: %10d\r", elim_heap.size()); + + if (use_asymm){ + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim]; + frozen[elim] = true; + if (!asymmVar(elim)){ + ok = false; goto cleanup; } + frozen[elim] = was_frozen; } + + // At this point, the variable may have been set by assymetric branching, so check it + // again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){ + ok = false; goto cleanup; } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } + cleanup: + + // If no more simplification is needed, free all simplification-related data structures: + if (turn_off_elim){ + touched .clear(true); + occurs .clear(true); + n_occ .clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + }else{ + // Cheaper cleanup: + cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? + checkGarbage(); + } + + if (verbosity >= 1 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + + return ok; +} + + +inline void SimpSolver::cleanUpClauses() +{ + occurs.cleanAll(); + int i,j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); +} + + +//================================================================================================= +// Garbage Collection methods: + + +inline void SimpSolver::relocAll(ClauseAllocator& to) +{ + if (!use_simplification) return; + + // All occurs lists: + // + for (int i = 0; i < nVars(); i++){ + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (int i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + + +inline void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + cleanUpClauses(); + to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} +} + +#undef write_char +#undef var_Undef +#undef DYNAMICNBLEVEL +#undef CONSTANTREMOVECLAUSE +#undef UPDATEVARACTIVITY +#undef RATIOREMOVECLAUSES +#undef LOWER_BOUND_FOR_BLOCKING_RESTART +#undef M +#undef Q +#undef P +#undef B +#undef S +#undef EE +#undef X diff --git a/lib/bill/bill/sat/solver/glucose.hpp b/lib/bill/bill/sat/solver/glucose.hpp new file mode 100644 index 0000000..6494557 --- /dev/null +++ b/lib/bill/bill/sat/solver/glucose.hpp @@ -0,0 +1,6178 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#pragma once + +#ifndef Glucose_IntTypes_h +#define Glucose_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +# include +# include + +#endif + +#include + +#ifndef PRIu64 +#define PRIu64 "lu" +#define PRIi64 "ld" +#endif +//================================================================================================= + +#endif +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Glucose_XAlloc_h +#define Glucose_XAlloc_h + +#include +#include +#include + +namespace Glucose { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else { + return mem; + } +} + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Vec_h +#define Glucose_Vec_h + +#include +#include + + + +#include + +namespace Glucose { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + + void remove(const T &elem) { + int tmp; + for(tmp = 0;tmp& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } + void memCopyTo(vec& copy) const{ + copy.capacity(cap); + copy.sz = sz; + memcpy(copy.data,data,sizeof(T)*cap); + } + +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Alg_h +#define Glucose_Alg_h + + + +namespace Glucose { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Glucose_Alloc_h +#define Glucose_Alloc_h + + + + +namespace Glucose { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t getCap () const { return cap;} + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free (int size) { wasted_ += size; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + void copyTo(RegionAllocator& to) const { + // if (to.memory != NULL) ::free(to.memory); + to.memory = (T*)xrealloc(to.memory, sizeof(T)*cap); + memcpy(to.memory,memory,sizeof(T)*cap); + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + } + + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + //printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + //printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +#endif +#ifndef Glucose_Clone_h +#define Glucose_Clone_h + + +namespace Glucose { + + class Clone { + public: + virtual Clone* clone() const = 0; + }; +}; + +#endif/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Heap_h +#define Glucose_Heap_h + + + +namespace Glucose { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + void copyTo(Heap& copy) const {heap.copyTo(copy.heap);indices.copyTo(copy.indices);} + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(vec& ns) { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (int i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (int i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Map_h +#define Glucose_Map_h + + + +#include +#include + +namespace Glucose { + +//================================================================================================= +// Default hash/equals functions +// + +static inline uint32_t hash(std::string x) {std::hash hasher;return static_cast(hasher(x)); } + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; +// if(res==NULL) printf("%s\n",k.c_str()); + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; +// if(res==NULL) printf("%s\n",k.c_str()); + + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Queue_h +#define Glucose_Queue_h + + + +namespace Glucose { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + + + void copyTo(Queue& copy) const { + copy.first = first; + copy.end = end; + buf.memCopyTo(copy.buf); + } + + + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int i = 0; + for (int j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (int j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +#endif +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Sort_h +#define Glucose_Sort_h + + + +//================================================================================================= +// Some sorting algorithms for vec's + + +namespace Glucose { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +#endif +/************************************************************************************[ParseUtils.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_ParseUtils_h +#define Glucose_ParseUtils_h + +#include +#include +#include + +namespace Glucose { + +static inline bool isEof(const char* in) { return *in == '\0'; } + +//------------------------------------------------------------------------------------------------- +// Generic parse functions parametrized over the input-stream type. + + +template +static void skipWhitespace(B& in) { + while ((*in >= 9 && *in <= 13) || *in == 32) + ++in; } + + +template +static void skipLine(B& in) { + for (;;){ + if (isEof(in)) return; + if (*in == '\n') { ++in; return; } + ++in; } } + +template +static double parseDouble(B& in) { // only in the form X.XXXXXe-XX + bool neg= false; + double accu = 0.0; + double currentExponent = 1; + int exponent; + + skipWhitespace(in); + if(*in == EOF) return 0; + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '1' || *in > '9') printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + accu = (double)(*in - '0'); + ++in; + if (*in != '.') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + currentExponent = 0.1; + while (*in >= '0' && *in <= '9') + accu = accu + currentExponent * ((double)(*in - '0')), + currentExponent /= 10, + ++in; + if (*in != 'e') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + exponent = parseInt(in); // read exponent + accu *= pow(10,exponent); + return neg ? -accu:accu; +} + + +template +static int parseInt(B& in) { + int val = 0; + bool neg = false; + skipWhitespace(in); + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '0' || *in > '9') fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + while (*in >= '0' && *in <= '9') + val = val*10 + (*in - '0'), + ++in; + return neg ? -val : val; } + + +// String matching: in case of a match the input iterator will be advanced the corresponding +// number of characters. +template +static bool match(B& in, const char* str) { + int i; + for (i = 0; str[i] != '\0'; i++) + if (in[i] != str[i]) + return false; + + in += i; + + return true; +} + +// String matching: consumes characters eagerly, but does not require random access iterator. +template +static bool eagerMatch(B& in, const char* str) { + for (; *str != '\0'; ++str, ++in) + if (*str != *in) + return false; + return true; } + + +//================================================================================================= +} + +#endif +/***************************************************************************************[Options.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_Options_h +#define Glucose_Options_h + +#include +#include +#include +#include + + + + + +namespace Glucose { + +//================================================================================================== +// Top-level option parse/help functions: + + +extern void parseOptions (int& argc, char** argv, bool strict = false); +extern void printUsageAndExit(int argc, char** argv, bool verbose = false); +extern void setUsageHelp (const char* str); +extern void setHelpPrefixStr (const char* str); + + +//================================================================================================== +// Options is an abstract class that gives the interface for all types options: + + +class Option +{ + protected: + const char* name; + const char* description; + const char* category; + const char* type_name; + + static vec& getOptionList () { static vec options; return options; } + static const char*& getUsageString() { static const char* usage_str; return usage_str; } + static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; } + + struct OptionLt { + bool operator()(const Option* x, const Option* y) { + int test1 = strcmp(x->category, y->category); + return test1 < 0 || (test1 == 0 && strcmp(x->type_name, y->type_name) < 0); + } + }; + + Option(const char* name_, + const char* desc_, + const char* cate_, + const char* type_) : + name (name_) + , description(desc_) + , category (cate_) + , type_name (type_) + { + getOptionList().push(this); + } + + public: + virtual ~Option() {} + + virtual bool parse (const char* str) = 0; + virtual void help (bool verbose = false) = 0; + + friend void parseOptions (int& argc, char** argv, bool strict); + friend void printUsageAndExit (int argc, char** argv, bool verbose); + friend void setUsageHelp (const char* str); + friend void setHelpPrefixStr (const char* str); +}; + + +//================================================================================================== +// Range classes with specialization for floating types: + + +struct IntRange { + int begin; + int end; + IntRange(int b, int e) : begin(b), end(e) {} +}; + +struct Int64Range { + int64_t begin; + int64_t end; + Int64Range(int64_t b, int64_t e) : begin(b), end(e) {} +}; + +struct DoubleRange { + double begin; + double end; + bool begin_inclusive; + bool end_inclusive; + DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {} +}; + + +//================================================================================================== +// Double options: + + +class DoubleOption : public Option +{ + protected: + DoubleRange range; + double value; + + public: + DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false)) + : Option(n, d, c, ""), range(r), value(def) { + // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly. + } + + operator double (void) const { return value; } + operator double& (void) { return value; } + DoubleOption& operator=(double x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + double tmp = strtod(span, &end); + + if (end == NULL) + return false; + else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + // fprintf(stderr, "READ VALUE: %g\n", value); + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n", + name, type_name, + range.begin_inclusive ? '[' : '(', + range.begin, + range.end, + range.end_inclusive ? ']' : ')', + value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Int options: + + +class IntOption : public Option +{ + protected: + IntRange range; + int32_t value; + + public: + IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int32_t (void) const { return value; } + operator int32_t& (void) { return value; } + IntOption& operator= (int32_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int32_t tmp = strtol(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT32_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4d", range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT32_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4d", range.end); + + fprintf(stderr, "] (default: %d)\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +// Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll. +#ifndef _MSC_VER + +class Int64Option : public Option +{ + protected: + Int64Range range; + int64_t value; + + public: + Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int64_t (void) const { return value; } + operator int64_t& (void) { return value; } + Int64Option& operator= (int64_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int64_t tmp = strtoll(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT64_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4" PRIi64, range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT64_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4" PRIi64, range.end); + + fprintf(stderr, "] (default: %" PRIi64")\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; +#endif + +//================================================================================================== +// String option: + + +class StringOption : public Option +{ + const char* value; + public: + StringOption(const char* c, const char* n, const char* d, const char* def = NULL) + : Option(n, d, c, ""), value(def) {} + + operator const char* (void) const { return value; } + operator const char*& (void) { return value; } + StringOption& operator= (const char* x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + value = span; + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-10s = %8s\n", name, type_name); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Bool option: + + +class BoolOption : public Option +{ + bool value; + + public: + BoolOption(const char* c, const char* n, const char* d, bool v) + : Option(n, d, c, ""), value(v) {} + + operator bool (void) const { return value; } + operator bool& (void) { return value; } + BoolOption& operator=(bool b) { value = b; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (match(span, "-")){ + bool b = !match(span, "no-"); + + if (strcmp(span, name) == 0){ + value = b; + return true; } + } + + return false; + } + + virtual void help (bool verbose = false){ + + fprintf(stderr, " -%s, -no-%s", name, name); + + for (uint32_t i = 0; i < 32 - strlen(name)*2; i++) + fprintf(stderr, " "); + + fprintf(stderr, " "); + fprintf(stderr, "(default: %s)\n", value ? "on" : "off"); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + +//================================================================================================= +} + +#endif +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Glucose_System_h +#define Glucose_System_h + + + +//------------------------------------------------------------------------------------------------- + +namespace Glucose { + +static inline double cpuTime(void); // CPU-time in seconds. + +#ifndef _WIN32 +static inline double realTime(void); +#endif +extern double memUsed(); // Memory in mega bytes (returns 0 for unsupported architectures). +extern double memUsedPeak(); // Peak-memory in mega bytes (returns 0 for unsupported architectures). + +} + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +static inline double Glucose::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +#else +#include +#include +#include + +static inline double Glucose::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +#endif + +#ifndef _WIN32 +// Laurent: I know that this will not compile directly under Windows... sorry for that +static inline double Glucose::realTime() { + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double) tv.tv_usec / 1000000; } +#endif +#endif +/***************************************************************************************[SolverTypes.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. + **************************************************************************************************/ + + +#ifndef Glucose_SolverTypes_h +#define Glucose_SolverTypes_h + +#include +#include +#ifndef _WIN32 +#include +#endif + + + + + + + +namespace Glucose { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); + + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + + +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + +class lbool { + uint8_t value; + +public: + constexpr explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +constexpr auto l_True = Glucose::lbool((uint8_t)0); +constexpr auto l_False = Glucose::lbool((uint8_t)1); +constexpr auto l_Undef = Glucose::lbool((uint8_t)2); + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause; +typedef RegionAllocator::Ref CRef; + +#define BITS_LBD 20 +#ifdef INCREMENTAL + #define BITS_SIZEWITHOUTSEL 19 +#endif +#define BITS_REALSIZE 32 +class Clause { + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned canbedel : 1; + unsigned extra_size : 2; // extra size (end of 32bits) 0..3 + unsigned seen : 1; + unsigned reloced : 1; + unsigned exported : 2; // Values to keep track of the clause status for exportations + unsigned oneWatched : 1; + unsigned lbd : BITS_LBD; + + unsigned size : BITS_REALSIZE; + +#ifdef INCREMENTAL + unsigned szWithoutSelectors : BITS_SIZEWITHOUTSEL; +#endif + } header; + + union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, int _extra_size, bool learnt) { + assert(_extra_size < (1<<2)); + header.mark = 0; + header.learnt = learnt; + header.extra_size = _extra_size; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.canbedel = 1; + header.exported = 0; + header.oneWatched = 0; + header.seen = 0; + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.extra_size > 0){ + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); + if (header.extra_size > 1) { + data[header.size+1].abs = 0; // learntFrom + } + } + } + +public: + void calcAbstraction() { + assert(header.extra_size > 0); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); + if (header.extra_size > 0) { + data[header.size-i] = data[header.size]; + if (header.extra_size > 1) { // Special case for imported clauses + data[header.size-i-1] = data[header.size-1]; + } + } + header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + void nolearnt () { header.learnt = false;} + bool has_extra () const { return header.extra_size > 0; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + float& activity () { assert(header.extra_size > 0); return data[header.size].act; } + uint32_t abstraction () const { assert(header.extra_size > 0); return data[header.size].abs; } + + // Handle imported clauses lazy sharing + bool wasImported() const {return header.extra_size > 1;} + uint32_t importedFrom () const { assert(header.extra_size > 1); return data[header.size + 1].abs;} + void setImportedFrom(uint32_t ifrom) {assert(header.extra_size > 1); data[header.size+1].abs = ifrom;} + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + void setLBD(int i) {header.lbd=i; /*if (i < (1<<(BITS_LBD-1))) header.lbd = i; else header.lbd = (1<<(BITS_LBD-1));*/} + // unsigned int& lbd () { return header.lbd; } + unsigned int lbd () const { return header.lbd; } + void setCanBeDel(bool b) {header.canbedel = b;} + bool canBeDel() {return header.canbedel;} + void setSeen(bool b) {header.seen = b;} + bool getSeen() {return header.seen;} + void setExported(unsigned int b) {header.exported = b;} + unsigned int getExported() {return header.exported;} + void setOneWatched(bool b) {header.oneWatched = b;} + bool getOneWatched() {return header.oneWatched;} +#ifdef INCREMNENTAL + void setSizeWithoutSelectors (unsigned int n) {header.szWithoutSelectors = n; } + unsigned int sizeWithoutSelectors () const { return header.szWithoutSelectors; } +#endif + +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + + const CRef CRef_Undef = RegionAllocator::Ref_Undef; + class ClauseAllocator : public RegionAllocator + { + static int clauseWord32Size(int size, int extra_size){ + return (sizeof(Clause) + (sizeof(Lit) * (size + extra_size))) / sizeof(uint32_t); } + public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false, bool imported = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + + bool use_extra = learnt | extra_clause_field; + int extra_size = imported?3:(use_extra?1:0); + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), extra_size)); + new (lea(cid)) Clause(ps, extra_size, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free(CRef cid) + { + Clause& c = operator[](cid); + RegionAllocator::free(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt(), c.wasImported()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()) { + to[cr].activity() = c.activity(); + to[cr].setLBD(c.lbd()); + to[cr].setExported(c.getExported()); + to[cr].setOneWatched(c.getOneWatched()); +#ifdef INCREMENTAL + to[cr].setSizeWithoutSelectors(c.sizeWithoutSelectors()); +#endif + to[cr].setCanBeDel(c.canBeDel()); + if (c.wasImported()) { + to[cr].setImportedFrom(c.importedFrom()); + } + } + else { + to[cr].setSeen(c.getSeen()); + if (to[cr].has_extra()) to[cr].calcAbstraction(); + } + } + }; + + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + + public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void copyTo(OccLists ©) const { + + copy.occs.growTo(occs.size()); + for(int i = 0;i +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + + public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.extra_size > 0); assert(other.header.extra_size > 0); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + + +#endif +/***************************************************************************************[BoundedQueue.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. + **************************************************************************************************/ + + +#ifndef Glucose_BoundedQueue_h +#define Glucose_BoundedQueue_h + + + +//================================================================================================= + +namespace Glucose { + +template +class bqueue { + vec elems; + int first; + int last; + unsigned long long sumofqueue; + int maxsize; + int queuesize; // Number of current elements (must be < maxsize !) + bool expComputed; + double exp,value; +public: + bqueue(void) : first(0), last(0), sumofqueue(0), maxsize(0), queuesize(0),expComputed(false) { } + + void initSize(int size) {growTo(size);exp = 2.0/(size+1);} // Init size of bounded size queue + + void push(T x) { + expComputed = false; + if (queuesize==maxsize) { + assert(last==first); // The queue is full, next value to enter will replace oldest one + sumofqueue -= elems[last]; + if ((++last) == maxsize) last = 0; + } else + queuesize++; + sumofqueue += x; + elems[first] = x; + if ((++first) == maxsize) {first = 0;last = 0;} + } + + T peek() { assert(queuesize>0); return elems[last]; } + void pop() {sumofqueue-=elems[last]; queuesize--; if ((++last) == maxsize) last = 0;} + + unsigned long long getsum() const {return sumofqueue;} + unsigned int getavg() const {return (unsigned int)(sumofqueue/((unsigned long long)queuesize));} + int maxSize() const {return maxsize;} + double getavgDouble() const { + double tmp = 0; + for(int i=0;i +namespace Glucose { + + class SolverStats { + protected: + Map map; + + public: + + SolverStats(std::string all[],int sz) : map() { + addStats(all,sz); + } + + void addStats(std::string names[],int sz) { + for(int i = 0;i map[name]) + map[name] = val; + } + + void minimize(const std::string name,uint64_t val) { + if(val < map[name]) + map[name] = val; + } + +}; + +} + +#endif /* SOLVERSTATS_H */ + +/***************************************************************************************[Solver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. + **************************************************************************************************/ + +#ifndef Glucose_Solver_h +#define Glucose_Solver_h + + + + + + + + + + + +namespace Glucose { +// Core stats + +enum CoreStats { + sumResSeen, + sumRes, + sumTrail, + nbPromoted, + originalClausesSeen, + sumDecisionLevels, + nbPermanentLearnts, + nbRemovedClauses, + nbRemovedUnaryWatchedClauses, + nbReducedClauses, + nbDL2, + nbBin, + nbUn, + nbReduceDB, + rnd_decisions, + nbstopsrestarts, + nbstopsrestartssame, + lastblockatrestart, + dec_vars, + clauses_literals, + learnts_literals, + max_literals, + tot_literals, + noDecisionConflict +} ; + +#define coreStatsSize 24 +//================================================================================================= +// Solver -- the main class: + +class Solver : public Clone { + + friend class SolverConfiguration; + +public: + + // Constructor/Destructor: + // + Solver(); + Solver(const Solver &s); + + virtual ~Solver(); + + /** + * Clone function + */ + virtual Clone* clone() const { + return new Solver(*this); + } + + // Problem specification: + // + virtual Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + bool addClause (const vec& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + virtual bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + // Convenience versions of 'toDimacs()': + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Display clauses and literals + void printLit(Lit l); + void printClause(CRef c); + void printInitialClause(CRef c); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () ; + + inline char valuePhase(Var v) {return polarity[v];} + + // Incremental mode + void setIncrementalMode(); + void initNbInitialVars(int nb); + void printIncrementalStats(); + bool isIncremental(); + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void garbageCollect(); + void checkGarbage(double gf); + void checkGarbage(); + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + int verbosity; + int verbEveryConflicts; + int showModel; + + // Constants For restarts + double K; + double R; + double sizeLBDQueue; + double sizeTrailQueue; + + // Constants for reduce DB + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB; + unsigned int lbLBDFrozenClause; + bool chanseokStrategy; + int coLBDBound; // Keep all learnts with lbd<=coLBDBound + // Constant for reducing clause + int lbSizeMinimizingClause; + unsigned int lbLBDMinimizingClause; + + // Constant for heuristic + double var_decay; + double max_var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + bool randomizeFirstDescent; // the first decisions (until first cnflict) are made randomly + // Useful for syrup! + + // Constant for Memory managment + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + // Certified UNSAT ( Thanks to Marijn Heule + // New in 2016 : proof in DRAT format, possibility to use binary output + FILE* certifiedOutput; + bool certifiedUNSAT; + bool vbyte; + + void write_char (unsigned char c); + void write_lit (int n); + + + // Panic mode. + // Save memory + uint32_t panicModeLastRemoved, panicModeLastRemovedShared; + + bool useUnaryWatched; // Enable unary watched literals + bool promoteOneWatchedClause; // One watched clauses are promotted to two watched clauses if found empty + + // Functions useful for multithread solving + // Useless in the sequential case + // Overide in ParallelSolver + virtual void parallelImportClauseDuringConflictAnalysis(Clause &c,CRef confl); + virtual bool parallelImportClauses(); // true if the empty clause was received + virtual void parallelImportUnaryClauses(); + virtual void parallelExportUnaryClause(Lit p); + virtual void parallelExportClauseDuringSearch(Clause &c); + virtual bool parallelJobIsFinished(); + virtual bool panicModeIsEnabled(); + + + double luby(double y, int x); + + // Statistics + vec stats; + + // Important stats completely related to search. Keep here + uint64_t solves,starts,decisions,propagations,conflicts,conflictsRestarts; + +protected: + + long curRestart; + + // Alpha variables + bool glureduce; + uint32_t restart_inc; + bool luby_restart; + bool adaptStrategies; + uint32_t luby_restart_factor; + bool randomize_on_restarts, fixed_randomize_on_restarts, newDescent; + uint32_t randomDescentAssignments; + bool forceUnsatOnNewDescent; + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } +/* Watcher &operator=(Watcher w) { + this->cref = w.cref; + this->blocker = w.blocker; + return *this; + } +*/ + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + + // Solver state: + // + int lastIndexRed; + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + double cla_inc; // Amount to bump next clause with. + vec activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + watchesBin; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + unaryWatches; // Unary watch scheme (clauses are seen when they become empty + vec clauses; // List of problem clauses. + vec learnts; // List of learnt clauses. + vec permanentLearnts; // The list of learnts clauses kept permanently + vec unaryWatchedClauses; // List of imported clauses (after the purgatory) // TODO put inside ParallelSolver + + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec forceUNSAT; + void bumpForceUNSAT(Lit q); // Handles the forces + + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec nbpos; + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap; // A priority queue of variables ordered with respect to the variable activity. + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + vec permDiff; // permDiff[var] contains the current conflict number... Used to count the number of LBD + + + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + vec lastDecisionLevel; + + ClauseAllocator ca; + + int nbclausesbeforereduce; // To know when it is time to reduce clause database + + // Used for restart strategies + bqueue trailQueue,lbdQueue; // Bounded queues for restarts. + float sumLBD; // used to compute the global average of LBD. Restarts... + int sumAssumptions; + CRef lastLearntClause; + + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + unsigned int MYFLAG; + + // Initial reduceDB strategy + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + // Variables added for incremental mode + int incremental; // Use incremental SAT Solver + int nbVarsInitialFormula; // nb VAR in formula without assumptions (incremental SAT) + double totalTime4Sat,totalTime4Unsat; + int nbSatCalls,nbUnsatCalls; + vec assumptionPositions,initialPositions; + + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + CRef propagateUnaryWatches(Lit p); // Perform propagation on unary watches of p, can find only conflicts + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, vec & selectors, int& out_btlevel,unsigned int &nblevels,unsigned int &szWithoutSelectors); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int nof_conflicts); // Search for a given number of conflicts. + virtual lbool solve_ (bool do_simp = true, bool turn_off_simp = false); // Main solve method (assumptions given in 'assumptions'). + virtual void reduceDB (); // Reduce the set of learnt clauses. + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap (); + + void adaptSolver(); // Adapt solver strategies + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void detachClausePurgatory(CRef cr, bool strict = false); + void attachClausePurgatory(CRef cr); + void removeClause (CRef cr, bool inPurgatory = false); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + template unsigned int computeLBD(const T & lits,int end=-1); + void minimisationWithBinaryResolution(vec &out_learnt); + + virtual void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + int level (Var x) const; + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + inline bool isSelector(Var v) {return (incremental && v>nbVarsInitialFormula);} + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } +inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } +inline void Solver::varBumpActivity(Var v, double inc) { + if ( (activity[v] += inc) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) + order_heap.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) + ca[learnts[i]].activity() *= 1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect(); } + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } + inline bool Solver::locked (const Clause& c) const { + if(c.size()>2) + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; + return + (value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c) + || + (value(c[1]) == l_True && reason(var(c[1])) != CRef_Undef && ca.lea(reason(var(c[1]))) == &c); + } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () { + int a = stats[dec_vars]; + return (int)(a) - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) stats[dec_vars]++; + else if (!b && decision[v]) stats[dec_vars]--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + + + +//================================================================================================= +// Debug etc: + + +inline void Solver::printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void Solver::printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } +} + +inline void Solver::printInitialClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + if(!isSelector(var(c[i]))) { + printLit(c[i]); + printf(" "); + } + } +} + +//================================================================================================= +struct reduceDBAct_lt { + ClauseAllocator& ca; + + reduceDBAct_lt(ClauseAllocator& ca_) : ca(ca_) { + } + + bool operator()(CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + + return ca[x].activity() < ca[y].activity(); + } +}; + +struct reduceDB_lt { + ClauseAllocator& ca; + + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) { + } + + bool operator()(CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + + // Second one based on literal block distance + if (ca[x].lbd() > ca[y].lbd()) return 1; + if (ca[x].lbd() < ca[y].lbd()) return 0; + + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + //return x->size() < y->size(); + + //return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + + +} + + +#endif +/***************************************************************************************[Solver.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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 + + + + + + + +namespace Glucose { + + +//================================================================================================= +// Statistics +//================================================================================================= + + + +//================================================================================================= +// Options: + +static const char *_cat = "CORE"; +static const char *_cr = "CORE -- RESTART"; +static const char *_cred = "CORE -- REDUCE"; +static const char *_cm = "CORE -- MINIMIZE"; + + +static DoubleOption opt_K(_cr, "K", "The constant used to force restart", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_R(_cr, "R", "The constant used to block restart", 1.4, DoubleRange(1, false, 5, false)); +static IntOption opt_size_lbd_queue(_cr, "szLBDQueue", "The size of moving average for LBD (restarts)", 50, IntRange(10, INT32_MAX)); +static IntOption opt_size_trail_queue(_cr, "szTrailQueue", "The size of moving average for trail (block restarts)", 5000, IntRange(10, INT32_MAX)); + +static IntOption opt_first_reduce_db(_cred, "firstReduceDB", "The number of conflicts before the first reduce DB (or the size of leernts if chanseok is used)", + 2000, IntRange(0, INT32_MAX)); +static IntOption opt_inc_reduce_db(_cred, "incReduceDB", "Increment for reduce DB", 300, IntRange(0, INT32_MAX)); +static IntOption opt_spec_inc_reduce_db(_cred, "specialIncReduceDB", "Special increment for reduce DB", 1000, IntRange(0, INT32_MAX)); +static IntOption opt_lb_lbd_frozen_clause(_cred, "minLBDFrozenClause", "Protect clauses if their LBD decrease and is lower than (for one turn)", 30, + IntRange(0, INT32_MAX)); +static BoolOption opt_chanseok_hack(_cred, "chanseok", + "Use Chanseok Oh strategy for LBD (keep all LBD<=co and remove half of firstreduceDB other learnt clauses", false); +static IntOption opt_chanseok_limit(_cred, "co", "Chanseok Oh: all learnt clauses with LBD<=co are permanent", 5, IntRange(2, INT32_MAX)); + + +static IntOption opt_lb_size_minimzing_clause(_cm, "minSizeMinimizingClause", "The min size required to minimize clause", 30, IntRange(3, INT32_MAX)); +static IntOption opt_lb_lbd_minimzing_clause(_cm, "minLBDMinimizingClause", "The min LBD required to minimize clause", 6, IntRange(3, INT32_MAX)); + + +static DoubleOption opt_var_decay(_cat, "var-decay", "The variable activity decay factor (starting point)", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_max_var_decay(_cat, "max-var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); +static DoubleOption opt_clause_decay(_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); +static DoubleOption opt_random_var_freq(_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, + DoubleRange(0, true, 1, true)); +static DoubleOption opt_random_seed(_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); +static IntOption opt_ccmin_mode(_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); +static IntOption opt_phase_saving(_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); +static BoolOption opt_rnd_init_act(_cat, "rnd-init", "Randomize the initial activity", false); +static DoubleOption opt_garbage_frac(_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, + DoubleRange(0, false, HUGE_VAL, false)); +static BoolOption opt_glu_reduction(_cat, "gr", "glucose strategy to fire clause database reduction (must be false to fire Chanseok strategy)", true); +static BoolOption opt_luby_restart(_cat, "luby", "Use the Luby restart sequence", false); +static DoubleOption opt_restart_inc(_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); +static IntOption opt_luby_restart_factor(_cred, "luby-factor", "Luby restart factor", 100, IntRange(1, INT32_MAX)); + +static IntOption opt_randomize_phase_on_restarts(_cat, "phase-restart", + "The amount of randomization for the phase at each restart (0=none, 1=first branch, 2=first branch (no bad clauses), 3=first branch (only initial clauses)", + 0, IntRange(0, 3)); +static BoolOption opt_fixed_randomize_phase_on_restarts(_cat, "fix-phas-rest", "Fixes the first 7 levels at random phase", false); + +static BoolOption opt_adapt(_cat, "adapt", "Adapt dynamically stategies after 100000 conflicts", true); + +static BoolOption opt_forceunsat(_cat,"forceunsat","Force the phase for UNSAT",true); +//================================================================================================= +// Constructor/Destructor: + +inline Solver::Solver() : + +// Parameters (user settable): +// +verbosity(0) +, showModel(0) +, K(opt_K) +, R(opt_R) +, sizeLBDQueue(opt_size_lbd_queue) +, sizeTrailQueue(opt_size_trail_queue) +, firstReduceDB(opt_first_reduce_db) +, incReduceDB(opt_chanseok_hack ? 0 : opt_inc_reduce_db) +, specialIncReduceDB(opt_chanseok_hack ? 0 : opt_spec_inc_reduce_db) +, lbLBDFrozenClause(opt_lb_lbd_frozen_clause) +, chanseokStrategy(opt_chanseok_hack) +, coLBDBound (opt_chanseok_limit) +, lbSizeMinimizingClause(opt_lb_size_minimzing_clause) +, lbLBDMinimizingClause(opt_lb_lbd_minimzing_clause) +, var_decay(opt_var_decay) +, max_var_decay(opt_max_var_decay) +, clause_decay(opt_clause_decay) +, random_var_freq(opt_random_var_freq) +, random_seed(opt_random_seed) +, ccmin_mode(opt_ccmin_mode) +, phase_saving(opt_phase_saving) +, rnd_pol(false) +, rnd_init_act(opt_rnd_init_act) +, randomizeFirstDescent(false) +, garbage_frac(opt_garbage_frac) +, certifiedOutput(NULL) +, certifiedUNSAT(false) // Not in the first parallel version +, vbyte(false) +, panicModeLastRemoved(0), panicModeLastRemovedShared(0) +, useUnaryWatched(false) +, promoteOneWatchedClause(true) +,solves(0),starts(0),decisions(0),propagations(0),conflicts(0),conflictsRestarts(0) +, curRestart(1) +, glureduce(opt_glu_reduction) +, restart_inc(opt_restart_inc) +, luby_restart(opt_luby_restart) +, adaptStrategies(opt_adapt) +, luby_restart_factor(opt_luby_restart_factor) +, randomize_on_restarts(opt_randomize_phase_on_restarts) +, fixed_randomize_on_restarts(opt_fixed_randomize_phase_on_restarts) +, newDescent(0) +, randomDescentAssignments(0) +, forceUnsatOnNewDescent(opt_forceunsat) + +, ok(true) +, cla_inc(1) +, var_inc(1) +, watches(WatcherDeleted(ca)) +, watchesBin(WatcherDeleted(ca)) +, unaryWatches(WatcherDeleted(ca)) +, qhead(0) +, simpDB_assigns(-1) +, simpDB_props(0) +, order_heap(VarOrderLt(activity)) +, progress_estimate(0) +, remove_satisfied(true) +,lastLearntClause(CRef_Undef) +// Resource constraints: +// +, conflict_budget(-1) +, propagation_budget(-1) +, asynch_interrupt(false) +, incremental(false) +, nbVarsInitialFormula(INT32_MAX) +, totalTime4Sat(0.) +, totalTime4Unsat(0.) +, nbSatCalls(0) +, nbUnsatCalls(0) +{ + MYFLAG = 0; + // Initialize only first time. Useful for incremental solving (not in // version), useless otherwise + // Kept here for simplicity + lbdQueue.initSize(sizeLBDQueue); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + stats.growTo(coreStatsSize, 0); +} + +//------------------------------------------------------- +// Special constructor used for cloning solvers +//------------------------------------------------------- + +inline Solver::Solver(const Solver &s) : + verbosity(s.verbosity) +, showModel(s.showModel) +, K(s.K) +, R(s.R) +, sizeLBDQueue(s.sizeLBDQueue) +, sizeTrailQueue(s.sizeTrailQueue) +, firstReduceDB(s.firstReduceDB) +, incReduceDB(s.incReduceDB) +, specialIncReduceDB(s.specialIncReduceDB) +, lbLBDFrozenClause(s.lbLBDFrozenClause) +, chanseokStrategy(opt_chanseok_hack) +, coLBDBound (opt_chanseok_limit) +, lbSizeMinimizingClause(s.lbSizeMinimizingClause) +, lbLBDMinimizingClause(s.lbLBDMinimizingClause) +, var_decay(s.var_decay) +, max_var_decay(s.max_var_decay) +, clause_decay(s.clause_decay) +, random_var_freq(s.random_var_freq) +, random_seed(s.random_seed) +, ccmin_mode(s.ccmin_mode) +, phase_saving(s.phase_saving) +, rnd_pol(s.rnd_pol) +, rnd_init_act(s.rnd_init_act) +, randomizeFirstDescent(s.randomizeFirstDescent) +, garbage_frac(s.garbage_frac) +, certifiedOutput(NULL) +, certifiedUNSAT(false) // Not in the first parallel version +, panicModeLastRemoved(s.panicModeLastRemoved), panicModeLastRemovedShared(s.panicModeLastRemovedShared) +, useUnaryWatched(s.useUnaryWatched) +, promoteOneWatchedClause(s.promoteOneWatchedClause) +// Statistics: (formerly in 'SolverStats') +// +,solves(0),starts(0),decisions(0),propagations(0),conflicts(0),conflictsRestarts(0) + +, curRestart(s.curRestart) +, glureduce(s.glureduce) +, restart_inc(s.restart_inc) +, luby_restart(s.luby_restart) +, adaptStrategies(s.adaptStrategies) +, luby_restart_factor(s.luby_restart_factor) +, randomize_on_restarts(s.randomize_on_restarts) +, fixed_randomize_on_restarts(s.fixed_randomize_on_restarts) +, newDescent(s.newDescent) +, randomDescentAssignments(s.randomDescentAssignments) +, forceUnsatOnNewDescent(s.forceUnsatOnNewDescent) +, ok(true) +, cla_inc(s.cla_inc) +, var_inc(s.var_inc) +, watches(WatcherDeleted(ca)) +, watchesBin(WatcherDeleted(ca)) +, unaryWatches(WatcherDeleted(ca)) +, qhead(s.qhead) +, simpDB_assigns(s.simpDB_assigns) +, simpDB_props(s.simpDB_props) +, order_heap(VarOrderLt(activity)) +, progress_estimate(s.progress_estimate) +, remove_satisfied(s.remove_satisfied) +,lastLearntClause(CRef_Undef) +// Resource constraints: +// +, conflict_budget(s.conflict_budget) +, propagation_budget(s.propagation_budget) +, asynch_interrupt(s.asynch_interrupt) +, incremental(s.incremental) +, nbVarsInitialFormula(s.nbVarsInitialFormula) +, totalTime4Sat(s.totalTime4Sat) +, totalTime4Unsat(s.totalTime4Unsat) +, nbSatCalls(s.nbSatCalls) +, nbUnsatCalls(s.nbUnsatCalls) +{ + // Copy clauses. + s.ca.copyTo(ca); + ca.extra_clause_field = s.ca.extra_clause_field; + + // Initialize other variables + MYFLAG = 0; + // Initialize only first time. Useful for incremental solving (not in // version), useless otherwise + // Kept here for simplicity + sumLBD = s.sumLBD; + nbclausesbeforereduce = s.nbclausesbeforereduce; + + // Copy all search vectors + s.watches.copyTo(watches); + s.watchesBin.copyTo(watchesBin); + s.unaryWatches.copyTo(unaryWatches); + s.assigns.memCopyTo(assigns); + s.vardata.memCopyTo(vardata); + s.activity.memCopyTo(activity); + s.seen.memCopyTo(seen); + s.permDiff.memCopyTo(permDiff); + s.polarity.memCopyTo(polarity); + s.decision.memCopyTo(decision); + s.trail.memCopyTo(trail); + s.order_heap.copyTo(order_heap); + s.clauses.memCopyTo(clauses); + s.learnts.memCopyTo(learnts); + s.permanentLearnts.memCopyTo(permanentLearnts); + + s.lbdQueue.copyTo(lbdQueue); + s.trailQueue.copyTo(trailQueue); + s.forceUNSAT.copyTo(forceUNSAT); + s.stats.copyTo(stats); +} + + +inline Solver::~Solver() { +} + + +/**************************************************************** + Certified UNSAT proof in binary format +****************************************************************/ + +inline void Solver::write_char(unsigned char ch) { +#ifdef _WIN32 + if(putc((int) ch, certifiedOutput) == EOF) + exit(1); +#else + if(putc_unlocked((int) ch, certifiedOutput) == EOF) + exit(1); +#endif +} + + +inline void Solver::write_lit(int n) { + for(; n > 127; n >>= 7) + write_char(128 | (n & 127)); + write_char(n); +} + +/**************************************************************** + Set the incremental mode +****************************************************************/ + +// This function set the incremental mode to true. +// You can add special code for this mode here. + +inline void Solver::setIncrementalMode() { +#ifdef INCREMENTAL + incremental = true; +#else + fprintf(stderr, "c Trying to set incremental mode, but not compiled properly for this.\n"); + exit(1); +#endif +} + + +// Number of variables without selectors +inline void Solver::initNbInitialVars(int nb) { + nbVarsInitialFormula = nb; +} + + +inline bool Solver::isIncremental() { + return incremental; +} + + +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// + +inline Var Solver::newVar(bool sign, bool dvar) { + int v = nVars(); + watches.init(mkLit(v, false)); + watches.init(mkLit(v, true)); + watchesBin.init(mkLit(v, false)); + watchesBin.init(mkLit(v, true)); + unaryWatches.init(mkLit(v, false)); + unaryWatches.init(mkLit(v, true)); + assigns.push(l_Undef); + vardata.push(mkVarData(CRef_Undef, 0)); + activity.push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen.push(0); + permDiff.push(0); + polarity.push(sign); + forceUNSAT.push(0); + decision.push(); + trail.capacity(v + 1); + setDecisionVar(v, dvar); + return v; +} + + +inline bool Solver::addClause_(vec &ps) { + + assert(decisionLevel() == 0); + if(!ok) return false; + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + + vec oc; + oc.clear(); + + Lit p; + int i, j, flag = 0; + if(certifiedUNSAT) { + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) { + oc.push(ps[i]); + if(value(ps[i]) == l_True || ps[i] == ~p || value(ps[i]) == l_False) + flag = 1; + } + } + + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + if(value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if(value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if(flag && (certifiedUNSAT)) { + if(vbyte) { + write_char('a'); + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + write_lit(2 * (var(ps[i]) + 1) + sign(ps[i])); + write_lit(0); + + write_char('d'); + for(i = j = 0, p = lit_Undef; i < oc.size(); i++) + write_lit(2 * (var(oc[i]) + 1) + sign(oc[i])); + write_lit(0); + } + else { + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + fprintf(certifiedOutput, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + + fprintf(certifiedOutput, "d "); + for(i = j = 0, p = lit_Undef; i < oc.size(); i++) + fprintf(certifiedOutput, "%i ", (var(oc[i]) + 1) * (-2 * sign(oc[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + + if(ps.size() == 0) + return ok = false; + else if(ps.size() == 1) { + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + } else { + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + + +inline void Solver::attachClause(CRef cr) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(c.size() == 2) { + watchesBin[~c[0]].push(Watcher(cr, c[1])); + watchesBin[~c[1]].push(Watcher(cr, c[0])); + } else { + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + } + if(c.learnt()) stats[learnts_literals] += c.size(); + else stats[clauses_literals] += c.size(); +} + + +inline void Solver::attachClausePurgatory(CRef cr) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + unaryWatches[~c[0]].push(Watcher(cr, c[1])); + +} + + +inline void Solver::detachClause(CRef cr, bool strict) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(c.size() == 2) { + if(strict) { + remove(watchesBin[~c[0]], Watcher(cr, c[1])); + remove(watchesBin[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watchesBin.smudge(~c[0]); + watchesBin.smudge(~c[1]); + } + } else { + if(strict) { + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + } + if(c.learnt()) stats[learnts_literals] -= c.size(); + else stats[clauses_literals] -= c.size(); +} + + +// The purgatory is the 1-Watched scheme for imported clauses + +inline void Solver::detachClausePurgatory(CRef cr, bool strict) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(strict) + remove(unaryWatches[~c[0]], Watcher(cr, c[1])); + else + unaryWatches.smudge(~c[0]); +} + + +inline void Solver::removeClause(CRef cr, bool inPurgatory) { + + Clause &c = ca[cr]; + + if(certifiedUNSAT) { + if(vbyte) { + write_char('d'); + for(int i = 0; i < c.size(); i++) + write_lit(2 * (var(c[i]) + 1) + sign(c[i])); + write_lit(0); + } + else { + fprintf(certifiedOutput, "d "); + for(int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + if(inPurgatory) + detachClausePurgatory(cr); + else + detachClause(cr); + // Don't leave pointers to free'd memory! + if(locked(c)) vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free(cr); +} + + +inline bool Solver::satisfied(const Clause &c) const { +#ifdef INCREMENTAL + if(incremental) + return (value(c[0]) == l_True) || (value(c[1]) == l_True); +#endif + + // Default mode + for(int i = 0; i < c.size(); i++) + if(value(c[i]) == l_True) + return true; + return false; +} + + +/************************************************************ + * Compute LBD functions + *************************************************************/ + +template inline unsigned int Solver::computeLBD(const T &lits, int end) { + int nblevels = 0; + MYFLAG++; +#ifdef INCREMENTAL + if(incremental) { // ----------------- INCREMENTAL MODE + if(end==-1) end = lits.size(); + int nbDone = 0; + for(int i=0;i=end) break; + if(isSelector(var(lits[i]))) continue; + nbDone++; + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ +#endif + for(int i = 0; i < lits.size(); i++) { + int l = level(var(lits[i])); + if(permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } +#ifdef INCREMENTAL + } +#endif + return nblevels; +} + + + +/****************************************************************** + * Minimisation with binary reolution + ******************************************************************/ +inline void Solver::minimisationWithBinaryResolution(vec &out_learnt) { + + // Find the LBD measure + unsigned int lbd = computeLBD(out_learnt); + Lit p = ~out_learnt[0]; + + if(lbd <= lbLBDMinimizingClause) { + MYFLAG++; + + for(int i = 1; i < out_learnt.size(); i++) { + permDiff[var(out_learnt[i])] = MYFLAG; + } + + vec &wbin = watchesBin[p]; + int nb = 0; + for(int k = 0; k < wbin.size(); k++) { + Lit imp = wbin[k].blocker; + if(permDiff[var(imp)] == MYFLAG && value(imp) == l_True) { + nb++; + permDiff[var(imp)] = MYFLAG - 1; + } + } + int l = out_learnt.size() - 1; + if(nb > 0) { + stats[nbReducedClauses]++; + for(int i = 1; i < out_learnt.size() - nb; i++) { + if(permDiff[var(out_learnt[i])] != MYFLAG) { + Lit p = out_learnt[l]; + out_learnt[l] = out_learnt[i]; + out_learnt[i] = p; + l--; + i--; + } + } + + out_learnt.shrink(nb); + + } + } +} + +// Revert to the state at given level (keeping all assignment at 'level' but not beyond). +// + +inline void Solver::cancelUntil(int level) { + if(decisionLevel() > level) { + for(int c = trail.size() - 1; c >= trail_lim[level]; c--) { + Var x = var(trail[c]); + assigns[x] = l_Undef; + if(phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) { + polarity[x] = sign(trail[c]); + } + insertVarOrder(x); + } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + + +//================================================================================================= +// Major methods: + +inline Lit Solver::pickBranchLit() { + Var next = var_Undef; + + // Random decision: + if(((randomizeFirstDescent && conflicts == 0) || drand(random_seed) < random_var_freq) && !order_heap.empty()) { + next = order_heap[irand(random_seed, order_heap.size())]; + if(value(next) == l_Undef && decision[next]) + stats[rnd_decisions]++; + } + + // Activity based decision: + while(next == var_Undef || value(next) != l_Undef || !decision[next]) + if(order_heap.empty()) { + next = var_Undef; + break; + } else { + next = order_heap.removeMin(); + } + + if(randomize_on_restarts && !fixed_randomize_on_restarts && newDescent && (decisionLevel() % 2 == 0)) { + return mkLit(next, (randomDescentAssignments >> (decisionLevel() % 32)) & 1); + } + + if(fixed_randomize_on_restarts && decisionLevel() < 7) { + return mkLit(next, (randomDescentAssignments >> (decisionLevel() % 32)) & 1); + } + + if(next == var_Undef) return lit_Undef; + + if(forceUnsatOnNewDescent && newDescent) { + if(forceUNSAT[next] != 0) + return mkLit(next, forceUNSAT[next] < 0); + return mkLit(next, polarity[next]); + + } + + return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); +} + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +inline void Solver::analyze(CRef confl, vec &out_learnt, vec &selectors, int &out_btlevel, unsigned int &lbd, unsigned int &szWithoutSelectors) { + int pathC = 0; + Lit p = lit_Undef; + + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + do { + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause &c = ca[confl]; + // Special case for binary clauses + // The first one has to be SAT + if(p != lit_Undef && c.size() == 2 && value(c[0]) == l_False) { + + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + if(c.learnt()) { + parallelImportClauseDuringConflictAnalysis(c, confl); + claBumpActivity(c); + } else { // original clause + if(!c.getSeen()) { + stats[originalClausesSeen]++; + c.setSeen(true); + } + } + + // DYNAMIC NBLEVEL trick (see competition'09 companion paper) + if(c.learnt() && c.lbd() > 2) { + unsigned int nblevels = computeLBD(c); + if(nblevels + 1 < c.lbd()) { // improve the LBD + if(c.lbd() <= lbLBDFrozenClause) { + // seems to be interesting : keep it for the next round + c.setCanBeDel(false); + } + if(chanseokStrategy && nblevels <= coLBDBound) { + c.nolearnt(); + learnts.remove(confl); + permanentLearnts.push(confl); + stats[nbPermanentLearnts]++; + + } else { + c.setLBD(nblevels); // Update it + } + } + } + + + for(int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++) { + Lit q = c[j]; + + if(!seen[var(q)]) { + if(level(var(q)) == 0) { + } else { // Here, the old case + if(!isSelector(var(q))) + varBumpActivity(var(q)); + + // This variable was responsible for a conflict, + // consider it as a UNSAT assignation for this literal + bumpForceUNSAT(~q); // Negation because q is false here + + seen[var(q)] = 1; + if(level(var(q)) >= decisionLevel()) { + pathC++; + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(!isSelector(var(q)) && (reason(var(q)) != CRef_Undef) && ca[reason(var(q))].learnt()) + lastDecisionLevel.push(q); + } else { + if(isSelector(var(q))) { + assert(value(q) == l_False); + selectors.push(q); + } else + out_learnt.push(q); + } + } + } //else stats[sumResSeen]++; + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]); + p = trail[index + 1]; + //stats[sumRes]++; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + } while(pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + + for(int i = 0; i < selectors.size(); i++) + out_learnt.push(selectors[i]); + + out_learnt.copyTo(analyze_toclear); + if(ccmin_mode == 2) { + uint32_t abstract_level = 0; + for(i = 1; i < out_learnt.size(); i++) + abstract_level |= abstractLevel(var(out_learnt[i])); // (maintain an abstraction of levels involved in conflict) + + for(i = j = 1; i < out_learnt.size(); i++) + if(reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i], abstract_level)) + out_learnt[j++] = out_learnt[i]; + + } else if(ccmin_mode == 1) { + for(i = j = 1; i < out_learnt.size(); i++) { + Var x = var(out_learnt[i]); + + if(reason(x) == CRef_Undef) + out_learnt[j++] = out_learnt[i]; + else { + Clause &c = ca[reason(var(out_learnt[i]))]; + // Thanks to Siert Wieringa for this bug fix! + for(int k = ((c.size() == 2) ? 0 : 1); k < c.size(); k++) + if(!seen[var(c[k])] && level(var(c[k])) > 0) { + out_learnt[j++] = out_learnt[i]; + break; + } + } + } + } else + i = j = out_learnt.size(); + + // stats[max_literals]+=out_learnt.size(); + out_learnt.shrink(i - j); + // stats[tot_literals]+=out_learnt.size(); + + + /* *************************************** + Minimisation with binary clauses of the asserting clause + First of all : we look for small clauses + Then, we reduce clauses with small LBD. + Otherwise, this can be useless + */ + if(!incremental && out_learnt.size() <= lbSizeMinimizingClause) { + minimisationWithBinaryResolution(out_learnt); + } + // Find correct backtrack level: + // + if(out_learnt.size() == 1) + out_btlevel = 0; + else { + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for(int i = 2; i < out_learnt.size(); i++) + if(level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } +#ifdef INCREMENTAL + if(incremental) { + szWithoutSelectors = 0; + for(int i=0;i0) break; + } + } else +#endif + szWithoutSelectors = out_learnt.size(); + + // Compute LBD + lbd = computeLBD(out_learnt, out_learnt.size() - selectors.size()); + + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(lastDecisionLevel.size() > 0) { + for(int i = 0; i < lastDecisionLevel.size(); i++) { + if(ca[reason(var(lastDecisionLevel[i]))].lbd() < lbd) + varBumpActivity(var(lastDecisionLevel[i])); + } + lastDecisionLevel.clear(); + } + + + for(int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) + for(int j = 0; j < selectors.size(); j++) seen[var(selectors[j])] = 0; +} + + +// Check if 'p' can be removed. 'abstract_levels' is used to abort early if the algorithm is +// visiting literals at levels that cannot be removed later. + +inline bool Solver::litRedundant(Lit p, uint32_t abstract_levels) { + analyze_stack.clear(); + analyze_stack.push(p); + int top = analyze_toclear.size(); + while(analyze_stack.size() > 0) { + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause &c = ca[reason(var(analyze_stack.last()))]; + analyze_stack.pop(); // + if(c.size() == 2 && value(c[0]) == l_False) { + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + for(int i = 1; i < c.size(); i++) { + Lit p = c[i]; + if(!seen[var(p)]) { + if(level(var(p)) > 0) { + if(reason(var(p)) != CRef_Undef && (abstractLevel(var(p)) & abstract_levels) != 0) { + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + } else { + for(int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +inline void Solver::analyzeFinal(Lit p, vec &out_conflict) { + out_conflict.clear(); + out_conflict.push(p); + + if(decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for(int i = trail.size() - 1; i >= trail_lim[0]; i--) { + Var x = var(trail[i]); + if(seen[x]) { + if(reason(x) == CRef_Undef) { + assert(level(x) > 0); + out_conflict.push(~trail[i]); + } else { + Clause &c = ca[reason(x)]; + // for (int j = 1; j < c.size(); j++) Minisat (glucose 2.0) loop + // Bug in case of assumptions due to special data structures for Binary. + // Many thanks to Sam Bayless (sbayless@cs.ubc.ca) for discover this bug. + for(int j = ((c.size() == 2) ? 0 : 1); j < c.size(); j++) + if(level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + + +inline void Solver::uncheckedEnqueue(Lit p, CRef from) { + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel()); + trail.push_(p); +} + + +inline void Solver::bumpForceUNSAT(Lit q) { + forceUNSAT[var(q)] = sign(q) ? -1 : +1; + return; +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +inline CRef Solver::propagate() { + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watchesBin.cleanAll(); + unaryWatches.cleanAll(); + while(qhead < trail.size()) { + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec &ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + + // First, Propagate binary clauses + vec &wbin = watchesBin[p]; + for(int k = 0; k < wbin.size(); k++) { + + Lit imp = wbin[k].blocker; + + if(value(imp) == l_False) { + return wbin[k].cref; + } + + if(value(imp) == l_Undef) { + uncheckedEnqueue(imp, wbin[k].cref); + } + } + + // Now propagate other 2-watched clauses + for(i = j = (Watcher *) ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if(value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause &c = ca[cr]; + assert(!c.getOneWatched()); + Lit false_lit = ~p; + if(c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if(first != blocker && value(first) == l_True) { + + *j++ = w; + continue; + } +#ifdef INCREMENTAL + if(incremental) { // ----------------- INCREMENTAL MODE + int choosenPos = -1; + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + if(decisionLevel()>assumptions.size()) { + choosenPos = k; + break; + } else { + choosenPos = k; + + if(value(c[k])==l_True || !isSelector(var(c[k]))) { + break; + } + } + + } + } + if(choosenPos!=-1) { + c[1] = c[choosenPos]; c[choosenPos] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } else { // ----------------- DEFAULT MODE (NOT INCREMENTAL) +#endif + for(int k = 2; k < c.size(); k++) { + + if(value(c[k]) != l_False) { + c[1] = c[k]; + c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; + } + } +#ifdef INCREMENTAL + } +#endif + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if(value(first) == l_False) { + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while(i < end) + *j++ = *i++; + } else { + uncheckedEnqueue(first, cr); + + + } + NextClause:; + } + ws.shrink(i - j); + + // unaryWatches "propagation" + if(useUnaryWatched && confl == CRef_Undef) { + confl = propagateUnaryWatches(p); + + } + + } + + + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| propagateUnaryWatches : [Lit] -> [Clause*] +| +| Description: +| Propagates unary watches of Lit p, return a conflict +| otherwise CRef_Undef +| +|________________________________________________________________________________________________@*/ + +inline CRef Solver::propagateUnaryWatches(Lit p) { + CRef confl = CRef_Undef; + Watcher *i, *j, *end; + vec &ws = unaryWatches[p]; + for(i = j = (Watcher *) ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if(value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause &c = ca[cr]; + assert(c.getOneWatched()); + Lit false_lit = ~p; + assert(c[0] == false_lit); // this is unary watch... No other choice if "propagated" + //if (c[0] == false_lit) + //c[0] = c[1], c[1] = false_lit; + //assert(c[1] == false_lit); + i++; + Watcher w = Watcher(cr, c[0]); + for(int k = 1; k < c.size(); k++) { + if(value(c[k]) != l_False) { + c[0] = c[k]; + c[k] = false_lit; + unaryWatches[~c[0]].push(w); + goto NextClauseUnary; + } + } + + // Did not find watch -- clause is empty under assignment: + *j++ = w; + + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while(i < end) + *j++ = *i++; + + // We can add it now to the set of clauses when backtracking + //printf("*"); + if(promoteOneWatchedClause) { + stats[nbPromoted]++; + // Let's find the two biggest decision levels in the clause s.t. it will correctly be propagated when we'll backtrack + int maxlevel = -1; + int index = -1; + for(int k = 1; k < c.size(); k++) { + assert(value(c[k]) == l_False); + assert(level(var(c[k])) <= level(var(c[0]))); + if(level(var(c[k])) > maxlevel) { + index = k; + maxlevel = level(var(c[k])); + } + } + detachClausePurgatory(cr, true); // TODO: check that the cleanAll is ok (use ",true" otherwise) + assert(index != -1); + Lit tmp = c[1]; + c[1] = c[index], c[index] = tmp; + attachClause(cr); + // TODO used in function ParallelSolver::reportProgressArrayImports + //Override :-( + //goodImportsFromThreads[ca[cr].importedFrom()]++; + ca[cr].setOneWatched(false); + ca[cr].setExported(2); + } + NextClauseUnary:; + } + ws.shrink(i - j); + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ + + +inline void Solver::reduceDB() { + + int i, j; + stats[nbReduceDB]++; + if(chanseokStrategy) + sort(learnts, reduceDBAct_lt(ca)); + else { + sort(learnts, reduceDB_lt(ca)); + + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + if(ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd() <= 3) nbclausesbeforereduce += specialIncReduceDB; + // Useless :-) + if(ca[learnts.last()].lbd() <= 5) nbclausesbeforereduce += specialIncReduceDB; + + } + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + int limit = learnts.size() / 2; + + for(i = j = 0; i < learnts.size(); i++) { + Clause &c = ca[learnts[i]]; + if(c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + stats[nbRemovedClauses]++; + } + else { + if(!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + checkGarbage(); +} + + +inline void Solver::removeSatisfied(vec &cs) { + + int i, j; + for(i = j = 0; i < cs.size(); i++) { + Clause &c = ca[cs[i]]; + + + if(satisfied(c)) if(c.getOneWatched()) + removeClause(cs[i], true); + else + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + + +inline void Solver::rebuildOrderHeap() { + vec vs; + for(Var v = 0; v < nVars(); v++) + if(decision[v] && value(v) == l_Undef) + vs.push(v); + order_heap.build(vs); + +} + + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +inline bool Solver::simplify() { + assert(decisionLevel() == 0); + + if(!ok) return ok = false; + else { + CRef cr = propagate(); + if(cr != CRef_Undef) { + return ok = false; + } + } + + + if(nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + removeSatisfied(learnts); + removeSatisfied(permanentLearnts); + removeSatisfied(unaryWatchedClauses); + if(remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + checkGarbage(); + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = stats[clauses_literals] + stats[learnts_literals]; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + + +inline void Solver::adaptSolver() { + bool adjusted = false; + bool reinit = false; + // printf("c\nc Try to adapt solver strategies\nc \n"); + /* printf("c Adjusting solver for the SAT Race 2015 (alpha feature)\n"); + printf("c key successive Conflicts : %" PRIu64"\n",stats[noDecisionConflict]); + printf("c nb unary clauses learnt : %" PRIu64"\n",stats[nbUn]); + printf("c key avg dec per conflicts : %.2f\n", (float)decisions / (float)conflicts);*/ + float decpc = (float) decisions / (float) conflicts; + if(decpc <= 1.2) { + chanseokStrategy = true; + coLBDBound = 4; + glureduce = true; + adjusted = true; + // printf("c Adjusting for low decision levels.\n"); + reinit = true; + firstReduceDB = 2000; + nbclausesbeforereduce = firstReduceDB; + curRestart = (conflicts / nbclausesbeforereduce) + 1; + incReduceDB = 0; + } + if(stats[noDecisionConflict] < 30000) { + luby_restart = true; + luby_restart_factor = 100; + + var_decay = 0.999; + max_var_decay = 0.999; + adjusted = true; + // printf("c Adjusting for low successive conflicts.\n"); + } + if(stats[noDecisionConflict] > 54400) { + // printf("c Adjusting for high successive conflicts.\n"); + chanseokStrategy = true; + glureduce = true; + coLBDBound = 3; + firstReduceDB = 30000; + var_decay = 0.99; + max_var_decay = 0.99; + randomize_on_restarts = 1; + adjusted = true; + } + if(stats[nbDL2] - stats[nbBin] > 20000) { + var_decay = 0.91; + max_var_decay = 0.91; + adjusted = true; + // printf("c Adjusting for a very large number of true glue clauses found.\n"); + } + if(!adjusted) { + // printf("c Nothing extreme in this problem, continue with glucose default strategies.\n"); + } + printf("c\n"); + if(adjusted) { // Let's reinitialize the glucose restart strategy counters + lbdQueue.fastclear(); + sumLBD = 0; + conflictsRestarts = 0; + } + + if(chanseokStrategy && adjusted) { + int moved = 0; + int i, j; + for(i = j = 0; i < learnts.size(); i++) { + Clause &c = ca[learnts[i]]; + if(c.lbd() <= coLBDBound) { + permanentLearnts.push(learnts[i]); + moved++; + } + else { + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + // printf("c Activating Chanseok Strategy: moved %d clauses to the permanent set.\n", moved); + } + + if(reinit) { + assert(decisionLevel() == 0); + for(int i = 0; i < learnts.size(); i++) { + removeClause(learnts[i]); + } + learnts.shrink(learnts.size()); + checkGarbage(); +/* + order_heap.clear(); + for(int i=0;i [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +inline lbool Solver::search(int nof_conflicts) { + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause, selectors; + unsigned int nblevels, szWithoutSelectors = 0; + bool blocked = false; + bool aDecisionWasMade = false; + + starts++; + for(; ;) { + if(decisionLevel() == 0) { // We import clauses FIXME: ensure that we will import clauses enventually (restart after some point) + parallelImportUnaryClauses(); + + if(parallelImportClauses()) + return l_False; + + } + CRef confl = propagate(); + + if(confl != CRef_Undef) { + newDescent = false; + if(parallelJobIsFinished()) + return l_Undef; + + if(!aDecisionWasMade) + stats[noDecisionConflict]++; + aDecisionWasMade = false; + + stats[sumDecisionLevels] += decisionLevel(); + stats[sumTrail] += trail.size(); + // CONFLICT + conflicts++; + conflictC++; + conflictsRestarts++; + if(conflicts % 5000 == 0 && var_decay < max_var_decay) + var_decay += 0.01; + + if(verbosity >= 1 && starts>0 && conflicts % verbEveryConflicts == 0) { + printf("c | %8d %7d %5d | %7d %8d %8d | %5d %8d %6d %8d | %6.3f %% |\n", + (int) starts, (int) stats[nbstopsrestarts], (int) (conflicts / starts), + (int) stats[dec_vars] - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int) stats[clauses_literals], + (int) stats[nbReduceDB], nLearnts(), (int) stats[nbDL2], (int) stats[nbRemovedClauses], progressEstimate() * 100); + } + if(decisionLevel() == 0) { + return l_False; + + } + if(adaptStrategies && conflicts == 100000) { + cancelUntil(0); + adaptSolver(); + adaptStrategies = false; + return l_Undef; + } + + trailQueue.push(trail.size()); + // BLOCK RESTART (CP 2012 paper) + if(conflictsRestarts > LOWER_BOUND_FOR_BLOCKING_RESTART && lbdQueue.isvalid() && trail.size() > R * trailQueue.getavg()) { + lbdQueue.fastclear(); + stats[nbstopsrestarts]++; + if(!blocked) { + stats[lastblockatrestart] = starts; + stats[nbstopsrestartssame]++; + blocked = true; + } + } + + learnt_clause.clear(); + selectors.clear(); + + analyze(confl, learnt_clause, selectors, backtrack_level, nblevels, szWithoutSelectors); + + lbdQueue.push(nblevels); + sumLBD += nblevels; + + cancelUntil(backtrack_level); + + if(certifiedUNSAT) { + if(vbyte) { + write_char('a'); + for(int i = 0; i < learnt_clause.size(); i++) + write_lit(2 * (var(learnt_clause[i]) + 1) + sign(learnt_clause[i])); + write_lit(0); + } + else { + for(int i = 0; i < learnt_clause.size(); i++) + fprintf(certifiedOutput, "%i ", (var(learnt_clause[i]) + 1) * + (-2 * sign(learnt_clause[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + + if(learnt_clause.size() == 1) { + uncheckedEnqueue(learnt_clause[0]); + stats[nbUn]++; + parallelExportUnaryClause(learnt_clause[0]); + } else { + CRef cr; + if(chanseokStrategy && nblevels <= coLBDBound) { + cr = ca.alloc(learnt_clause, false); + permanentLearnts.push(cr); + stats[nbPermanentLearnts]++; + } else { + cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(nblevels); + ca[cr].setOneWatched(false); + learnts.push(cr); + claBumpActivity(ca[cr]); + } +#ifdef INCREMENTAL + ca[cr].setSizeWithoutSelectors(szWithoutSelectors); +#endif + if(nblevels <= 2) { stats[nbDL2]++; } // stats + if(ca[cr].size() == 2) stats[nbBin]++; // stats + attachClause(cr); + lastLearntClause = cr; // Use in multithread (to hard to put inside ParallelSolver) + parallelExportClauseDuringSearch(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + + } + varDecayActivity(); + claDecayActivity(); + + + } else { + // Our dynamic restart, see the SAT09 competition compagnion paper + if((luby_restart && nof_conflicts <= conflictC) || + (!luby_restart && (lbdQueue.isvalid() && ((lbdQueue.getavg() * K) > (sumLBD / conflictsRestarts))))) { + lbdQueue.fastclear(); + progress_estimate = progressEstimate(); + int bt = 0; +#ifdef INCREMENTAL + if(incremental) // DO NOT BACKTRACK UNTIL 0.. USELESS + bt = (decisionLevel() firstReduceDB) || + (glureduce && conflicts >= ((unsigned int) curRestart * nbclausesbeforereduce))) { + + if(learnts.size() > 0) { + curRestart = (conflicts / nbclausesbeforereduce) + 1; + reduceDB(); + if(!panicModeIsEnabled()) + nbclausesbeforereduce += incReduceDB; + } + } + + lastLearntClause = CRef_Undef; + Lit next = lit_Undef; + while(decisionLevel() < assumptions.size()) { + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if(value(p) == l_True) { + // Dummy decision level: + newDecisionLevel(); + } else if(value(p) == l_False) { + analyzeFinal(~p, conflict); + return l_False; + } else { + next = p; + break; + } + } + + if(next == lit_Undef) { + // New variable decision: + decisions++; + next = pickBranchLit(); + if(next == lit_Undef) { + // printf("c last restart ## conflicts : %d %d \n", conflictC, decisionLevel()); + // Model found: + return l_True; + } + } + + // Increase decision level and enqueue 'next' + aDecisionWasMade = true; + newDecisionLevel(); + uncheckedEnqueue(next); + } + } +} + + +inline double Solver::progressEstimate() const { + double progress = 0; + double F = 1.0 / nVars(); + + for(int i = 0; i <= decisionLevel(); i++) { + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + + +inline void Solver::printIncrementalStats() { + + printf("c---------- Glucose Stats -------------------------\n"); + printf("c restarts : %" + PRIu64 + "\n", starts); + printf("c nb ReduceDB : %" + PRIu64 + "\n", stats[nbReduceDB]); + printf("c nb removed Clauses : %" + PRIu64 + "\n", stats[nbRemovedClauses]); + printf("c nb learnts DL2 : %" + PRIu64 + "\n", stats[nbDL2]); + printf("c nb learnts size 2 : %" + PRIu64 + "\n", stats[nbBin]); + printf("c nb learnts size 1 : %" + PRIu64 + "\n", stats[nbUn]); + + printf("c conflicts : %" + PRIu64 + "\n", conflicts); + printf("c decisions : %" + PRIu64 + "\n", decisions); + printf("c propagations : %" + PRIu64 + "\n", propagations); + + printf("\nc SAT Calls : %d in %g seconds\n", nbSatCalls, totalTime4Sat); + printf("c UNSAT Calls : %d in %g seconds\n", nbUnsatCalls, totalTime4Unsat); + + printf("c--------------------------------------------------\n"); +} + + +inline double Solver::luby(double y, int x) { + + // Find the finite subsequence that contains index 'x', and the + // size of that subsequence: + int size, seq; + for(size = 1, seq = 0; size < x + 1; seq++, size = 2 * size + 1); + + while(size - 1 != x) { + size = (size - 1) >> 1; + seq--; + x = x % size; + } + + return pow(y, seq); +} + + +// NOTE: assumptions passed in member-variable 'assumptions'. + +inline lbool Solver::solve_(bool do_simp, bool turn_off_simp) // Parameters are useless in core but useful for SimpSolver.... +{ + + if(incremental && certifiedUNSAT) { + printf("Can not use incremental and certified unsat in the same time\n"); + exit(-1); + } + + model.clear(); + conflict.clear(); + if(!ok) return l_False; + double curTime = cpuTime(); + + solves++; + + + lbool status = l_Undef; + if(!incremental && verbosity >= 1) { + printf("c ========================================[ MAGIC CONSTANTS ]==============================================\n"); + printf("c | Constants are supposed to work well together :-) |\n"); + printf("c | however, if you find better choices, please let us known... |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + if(adaptStrategies) { + printf("c | Adapt dynamically the solver after 100000 conflicts (restarts, reduction strategies...) |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + } + printf("c | | | |\n"); + printf("c | - Restarts: | - Reduce Clause DB: | - Minimize Asserting: |\n"); + if(chanseokStrategy) { + printf("c | * LBD Queue : %6d | chanseok Strategy | * size < %3d |\n", lbdQueue.maxSize(), + lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * learnts size : %6d | * lbd < %3d |\n", trailQueue.maxSize(), + firstReduceDB, lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Bound LBD : %6d | |\n", K, coLBDBound); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n", R, lbLBDFrozenClause); + } else { + printf("c | * LBD Queue : %6d | * First : %6d | * size < %3d |\n", lbdQueue.maxSize(), + nbclausesbeforereduce, lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * Inc : %6d | * lbd < %3d |\n", trailQueue.maxSize(), incReduceDB, + lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Special : %6d | |\n", K, specialIncReduceDB); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n", R, lbLBDFrozenClause); + } + printf("c | | | |\n"); + printf("c ==================================[ Search Statistics (every %6d conflicts) ]=========================\n", verbEveryConflicts); + printf("c | |\n"); + + printf("c | RESTARTS | ORIGINAL | LEARNT | Progress |\n"); + printf("c | NB Blocked Avg Cfc | Vars Clauses Literals | Red Learnts LBD2 Removed | |\n"); + printf("c =========================================================================================================\n"); + } + + // Search: + int curr_restarts = 0; + while(status == l_Undef) { + status = search( + luby_restart ? luby(restart_inc, curr_restarts) * luby_restart_factor : 0); // the parameter is useless in glucose, kept to allow modifications + + if(!withinBudget()) break; + curr_restarts++; + } + + if(!incremental && verbosity >= 1) + printf("c =========================================================================================================\n"); + + if(certifiedUNSAT) { // Want certified output + if(status == l_False) { + if(vbyte) { + write_char('a'); + write_lit(0); + } + else { + fprintf(certifiedOutput, "0\n"); + } + } + fclose(certifiedOutput); + } + + + if(status == l_True) { + // Extend & copy model: + model.growTo(nVars()); + for(int i = 0; i < nVars(); i++) model[i] = value(i); + } else if(status == l_False && conflict.size() == 0) + ok = false; + + + cancelUntil(0); + + + double finalTime = cpuTime(); + if(status == l_True) { + nbSatCalls++; + totalTime4Sat += (finalTime - curTime); + } + if(status == l_False) { + nbUnsatCalls++; + totalTime4Unsat += (finalTime - curTime); + } + + + return status; + +} + + + + + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec &map, Var &max) { + if(map.size() <= x || map[x] == -1) { + map.growTo(x + 1, -1); + map[x] = max++; + } + return map[x]; +} + + +inline void Solver::toDimacs(FILE *f, Clause &c, vec &map, Var &max) { + if(satisfied(c)) return; + + for(int i = 0; i < c.size(); i++) + if(value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max) + 1); + fprintf(f, "0\n"); +} + + +inline void Solver::toDimacs(const char *file, const vec &assumps) { + FILE *f = fopen(file, "wr"); + if(f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + + +inline void Solver::toDimacs(FILE *f, const vec &assumps) { + // Handle case when solver is in contradictory state: + if(!ok) { + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; + } + + vec map; + Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int cnt = 0; + for(int i = 0; i < clauses.size(); i++) + if(!satisfied(ca[clauses[i]])) + cnt++; + + for(int i = 0; i < clauses.size(); i++) + if(!satisfied(ca[clauses[i]])) { + Clause &c = ca[clauses[i]]; + for(int j = 0; j < c.size(); j++) + if(value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumps.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for(int i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + for(int i = 0; i < assumps.size(); i++) { + assert(value(assumps[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumps[i]) ? "-" : "", mapVar(var(assumps[i]), map, max) + 1); + } + + if(verbosity > 0) + printf("Wrote %d clauses with %d variables.\n", cnt, max); +} + + +//================================================================================================= +// Garbage Collection methods: + +inline void Solver::relocAll(ClauseAllocator &to) { + // All watchers: + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watchesBin.cleanAll(); + unaryWatches.cleanAll(); + for(int v = 0; v < nVars(); v++) + for(int s = 0; s < 2; s++) { + Lit p = mkLit(v, s); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec &ws = watches[p]; + for(int j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec &ws2 = watchesBin[p]; + for(int j = 0; j < ws2.size(); j++) + ca.reloc(ws2[j].cref, to); + vec &ws3 = unaryWatches[p]; + for(int j = 0; j < ws3.size(); j++) + ca.reloc(ws3[j].cref, to); + } + + // All reasons: + // + for(int i = 0; i < trail.size(); i++) { + Var v = var(trail[i]); + + if(reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + for(int i = 0; i < learnts.size(); i++) + ca.reloc(learnts[i], to); + + for(int i = 0; i < permanentLearnts.size(); i++) + ca.reloc(permanentLearnts[i], to); + + // All original: + // + for(int i = 0; i < clauses.size(); i++) + ca.reloc(clauses[i], to); + + for(int i = 0; i < unaryWatchedClauses.size(); i++) + ca.reloc(unaryWatchedClauses[i], to); +} + + +inline void Solver::garbageCollect() { + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + relocAll(to); + if(verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, to.size() * ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +//-------------------------------------------------------------- +// Functions related to MultiThread. +// Useless in case of single core solver (aka original glucose) +// Keep them empty if you just use core solver +//-------------------------------------------------------------- + +inline bool Solver::panicModeIsEnabled() { + return false; +} + + +inline void Solver::parallelImportUnaryClauses() { +} + + +inline bool Solver::parallelImportClauses() { + return false; +} + + +inline void Solver::parallelExportUnaryClause(Lit p) { +} + + +inline void Solver::parallelExportClauseDuringSearch(Clause &c) { +} + +inline bool Solver::parallelJobIsFinished() { + // Parallel: another job has finished let's quit + return false; +} + + +inline void Solver::parallelImportClauseDuringConflictAnalysis(Clause &c, CRef confl) { +} +} // using namespace Glucose +/***************************************************************************************[SimpSolver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. + **************************************************************************************************/ + +#ifndef Glucose_SimpSolver_h +#define Glucose_SimpSolver_h + + + + + +namespace Glucose { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + SimpSolver(const SimpSolver &s); + + + /** + * Clone function + */ + virtual Clone* clone() const { + return new SimpSolver(*this); + } + + + // Problem specification: + // + virtual Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + virtual bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + + // Memory managment: + // + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + int parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + bool use_simplification; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + virtual lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr,bool inPurgatory=false); + bool strengthenClause (CRef cr, Lit l); + void cleanUpClauses (); + bool implied (const vec& c); + virtual void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +//================================================================================================= +} + +#endif +/***************************************************************************************[SimpSolver.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, 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 and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. + **************************************************************************************************/ + + + + + +namespace Glucose { + +//================================================================================================= +// Options: + + +static BoolOption opt_use_asymm ("SIMP", "asymm", "Shrink clauses by asymmetric branching.", false); +static BoolOption opt_use_rcheck ("SIMP", "rcheck", "Check if a clause is already implied. (costly)", false); +static BoolOption opt_use_elim ("SIMP", "elim", "Perform variable elimination.", true); +static IntOption opt_grow ("SIMP", "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); +static IntOption opt_clause_lim ("SIMP", "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX)); +static IntOption opt_subsumption_lim ("SIMP", "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX)); +static DoubleOption opt_simp_garbage_frac("SIMP", "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL, false)); + + +//================================================================================================= +// Constructor/Destructor: + + +inline SimpSolver::SimpSolver() : + Solver() + , grow (opt_grow) + , clause_lim (opt_clause_lim) + , subsumption_lim (opt_subsumption_lim) + , simp_garbage_frac (opt_simp_garbage_frac) + , use_asymm (opt_use_asymm) + , use_rcheck (opt_use_rcheck) + , use_elim (opt_use_elim) + , merges (0) + , asymm_lits (0) + , eliminated_vars (0) + , use_simplification (true) + , elimorder (1) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (0) + , n_touched (0) +{ + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + + +inline SimpSolver::~SimpSolver() +{ +} + + + +inline SimpSolver::SimpSolver(const SimpSolver &s) : Solver(s) + , grow (s.grow) + , clause_lim (s.clause_lim) + , subsumption_lim (s.subsumption_lim) + , simp_garbage_frac (s.simp_garbage_frac) + , use_asymm (s.use_asymm) + , use_rcheck (s.use_rcheck) + , use_elim (s.use_elim) + , merges (s.merges) + , asymm_lits (s.asymm_lits) + , eliminated_vars (s.eliminated_vars) + , use_simplification (s.use_simplification) + , elimorder (s.elimorder) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (s.bwdsub_assigns) + , n_touched (s.n_touched) +{ + // TODO: Copy dummy... what is it??? + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; + //End TODO + + + s.elimclauses.memCopyTo(elimclauses); + s.touched.memCopyTo(touched); + s.occurs.copyTo(occurs); + s.n_occ.memCopyTo(n_occ); + s.elim_heap.copyTo(elim_heap); + s.subsumption_queue.copyTo(subsumption_queue); + s.frozen.memCopyTo(frozen); + s.eliminated.memCopyTo(eliminated); + + use_simplification = s.use_simplification; + bwdsub_assigns = s.bwdsub_assigns; + n_touched = s.n_touched; + bwdsub_tmpunit = s.bwdsub_tmpunit; + qhead = s.qhead; + ok = s.ok; +} + + + +inline Var SimpSolver::newVar(bool sign, bool dvar) { + Var v = Solver::newVar(sign, dvar); + frozen .push((char)false); + eliminated.push((char)false); + + if (use_simplification){ + n_occ .push(0); + n_occ .push(0); + occurs .init(v); + touched .push(0); + elim_heap .insert(v); + } + return v; } + +inline lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + do_simp &= use_simplification; + + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("===============================================================================\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + + return result; +} + + + +inline bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if(!parsing && certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < ps.size(); i++) + write_lit(2*(var(ps[i])+1) + sign(ps[i])); + write_lit(0); + } + else { + for (int i = 0; i < ps.size(); i++) + fprintf(certifiedOutput, "%i " , (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (use_simplification && clauses.size() == nclauses + 1){ + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++){ + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + + + +inline void SimpSolver::removeClause(CRef cr,bool inPurgatory) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++){ + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr,inPurgatory); +} + + +inline bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < c.size(); i++) + if (c[i] != l) write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + for (int i = 0; i < c.size(); i++) + if (c[i] != l) fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (c.size() == 2){ + removeClause(cr); + c.strengthen(l); + }else{ + if (certifiedUNSAT) { + if (vbyte) { + write_char('d'); + for (int i = 0; i < c.size(); i++) + write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + for (int i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) + if (ps[j] == ~qs[i]) + return false; + else + goto next; + out_clause.push(qs[i]); + } + next:; + } + + for (int i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +// Returns FALSE if clause is always satisfied. +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*)ps; + const Lit* __qs = (const Lit*)qs; + + size = ps.size()-1; + + for (int i = 0; i < qs.size(); i++){ + if (var(__qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + size++; + } + next:; + } + + return true; +} + + +inline void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) return; + + int i,j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0){ + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + + +inline bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True){ + cancelUntil(0); + return false; + }else if (value(c[i]) != l_False){ + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + + +// Backward subsumption + backward subsumption resolution +inline bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt){ + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); } + + CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*)_cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){ + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error){ + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +inline bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v && value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + else + l = c[i]; + + if (propagate() != CRef_Undef){ + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + }else + cancelUntil(0); + + return true; +} + + +inline bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++){ + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + + + +inline bool SimpSolver::eliminateVar(Var v) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (int i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && + (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()){ + for (int i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + }else{ + for (int i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + } + + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (int i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); + if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + + +inline bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++){ + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++){ + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + + } + + return true; +} + + +inline void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + if(model.size()==0) model.growTo(nVars()); + + for (i = elimclauses.size()-1; i > 0; i -= j){ + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + + +inline bool SimpSolver::eliminate(bool turn_off_elim) +{ + if (!simplify()) { + ok = false; + return false; + } + else if (!use_simplification) + return true; + + // Main simplification loop: + // + + int toPerform = clauses.size()<=4800000; + + if(!toPerform) { + printf("c Too many clauses... No preprocessing\n"); + } + + while (toPerform && (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0)){ + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && + !backwardSubsumptionCheck(true)){ + ok = false; goto cleanup; } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt){ + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++){ + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) break; + + if (isEliminated(elim) || value(elim) != l_Undef) continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("elimination left: %10d\r", elim_heap.size()); + + if (use_asymm){ + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim]; + frozen[elim] = true; + if (!asymmVar(elim)){ + ok = false; goto cleanup; } + frozen[elim] = was_frozen; } + + // At this point, the variable may have been set by assymetric branching, so check it + // again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){ + ok = false; goto cleanup; } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } + cleanup: + + // If no more simplification is needed, free all simplification-related data structures: + if (turn_off_elim){ + touched .clear(true); + occurs .clear(true); + n_occ .clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + }else{ + // Cheaper cleanup: + cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? + checkGarbage(); + } + + if (verbosity >= 0 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + + + return ok; + + +} + + +inline void SimpSolver::cleanUpClauses() +{ + occurs.cleanAll(); + int i,j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); +} + + +//================================================================================================= +// Garbage Collection methods: + + +inline void SimpSolver::relocAll(ClauseAllocator& to) +{ + if (!use_simplification) return; + + // All occurs lists: + // + for (int i = 0; i < nVars(); i++){ + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (int i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + + +inline void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + cleanUpClauses(); + to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} +} // using namespace Glucose + +#undef BITS_LBD +#ifdef INCREMENTAL + #undef BITS_SIZEWITHOUTSEL + #undef INCREMENTAL +#endif +#undef BITS_REALSIZE +#undef DYNAMICNBLEVEL +#undef CONSTANTREMOVECLAUSE +#undef RATIOREMOVECLAUSES +#undef LOWER_BOUND_FOR_BLOCKING_RESTART +#undef coreStatsSize diff --git a/lib/bill/bill/sat/solver/maple.hpp b/lib/bill/bill/sat/solver/maple.hpp new file mode 100644 index 0000000..0572f2a --- /dev/null +++ b/lib/bill/bill/sat/solver/maple.hpp @@ -0,0 +1,5839 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#pragma once + +#ifndef Minisat_IntTypes_h +#define Minisat_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +# include +# include + +#endif + +#include + +//================================================================================================= + +#endif +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Minisat_XAlloc_h +#define Minisat_XAlloc_h + +#include +#include + +namespace Maple { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else + return mem; +} + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Vec_h +#define Minisat_Vec_h + +#include +#include + + + + +namespace Maple { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + // NOTE: it seems possible that overflow can happen in the 'sz+1' expression of 'push()', but + // in fact it can not since it requires that 'cap' is equal to INT_MAX. This in turn can not + // happen given the way capacities are calculated (below). Essentially, all capacities are + // even, but INT_MAX is odd. + + const T& last (void) const { return data[sz-1]; } + T& last (void) { return data[sz-1]; } + + // Vector interface: + const T& operator [] (int index) const { return data[index]; } + T& operator [] (int index) { return data[index]; } + + // Duplicatation (preferred instead): + void copyTo(vec& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || (((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM)) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Alg_h +#define Minisat_Alg_h + + + +namespace Maple { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + + +#ifndef Minisat_Alloc_h +#define Minisat_Alloc_h + + + + +namespace Maple { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free (int size) { wasted_ += size; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + // printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + // printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +#endif +/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Heap_h +#define Minisat_Heap_h + + + +namespace Maple { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(const vec& ns) { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (int i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (int i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +#endif +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Map_h +#define Minisat_Map_h + + + + +namespace Maple { + +//================================================================================================= +// Default hash/equals functions +// + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +#endif +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Queue_h +#define Minisat_Queue_h + + + +namespace Maple { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int i = 0; + for (int j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (int j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +#endif +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Sort_h +#define Minisat_Sort_h + + + +//================================================================================================= +// Some sorting algorithms for vec's + + +namespace Maple { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +#endif +/************************************************************************************[ParseUtils.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_ParseUtils_h +#define Minisat_ParseUtils_h + +#include +#include + +namespace Maple { + +static inline bool isEof(const char* in) { return *in == '\0'; } + +//------------------------------------------------------------------------------------------------- +// Generic parse functions parametrized over the input-stream type. + +template +static void skipWhitespace(B& in) { + while ((*in >= 9 && *in <= 13) || *in == 32) + ++in; } + + +template +static void skipLine(B& in) { + for (;;){ + if (isEof(in)) return; + if (*in == '\n') { ++in; return; } + ++in; } } + + +template +static int parseInt(B& in) { + int val = 0; + bool neg = false; + skipWhitespace(in); + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '0' || *in > '9') fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + while (*in >= '0' && *in <= '9') + val = val*10 + (*in - '0'), + ++in; + return neg ? -val : val; } + + +// String matching: in case of a match the input iterator will be advanced the corresponding +// number of characters. +template +static bool match(B& in, const char* str) { + int i; + for (i = 0; str[i] != '\0'; i++) + if (in[i] != str[i]) + return false; + + in += i; + + return true; +} + +// String matching: consumes characters eagerly, but does not require random access iterator. +template +static bool eagerMatch(B& in, const char* str) { + for (; *str != '\0'; ++str, ++in) + if (*str != *in) + return false; + return true; } + + +//================================================================================================= +} + +#endif +/***************************************************************************************[Options.h] +Copyright (c) 2008-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_Options_h +#define Minisat_Options_h + +#include +#include +#include +#include + + + + + +namespace Maple { + +//================================================================================================== +// Top-level option parse/help functions: + + +extern void parseOptions (int& argc, char** argv, bool strict = false); +extern void printUsageAndExit(int argc, char** argv, bool verbose = false); +extern void setUsageHelp (const char* str); +extern void setHelpPrefixStr (const char* str); + + +//================================================================================================== +// Options is an abstract class that gives the interface for all types options: + + +class Option +{ + protected: + const char* name; + const char* description; + const char* category; + const char* type_name; + + static vec& getOptionList () { static vec options; return options; } + static const char*& getUsageString() { static const char* usage_str; return usage_str; } + static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; } + + struct OptionLt { + bool operator()(const Option* x, const Option* y) { + int test1 = strcmp(x->category, y->category); + return test1 < 0 || (test1 == 0 && strcmp(x->type_name, y->type_name) < 0); + } + }; + + Option(const char* name_, + const char* desc_, + const char* cate_, + const char* type_) : + name (name_) + , description(desc_) + , category (cate_) + , type_name (type_) + { + getOptionList().push(this); + } + + public: + virtual ~Option() {} + + virtual bool parse (const char* str) = 0; + virtual void help (bool verbose = false) = 0; + + friend void parseOptions (int& argc, char** argv, bool strict); + friend void printUsageAndExit (int argc, char** argv, bool verbose); + friend void setUsageHelp (const char* str); + friend void setHelpPrefixStr (const char* str); +}; + + +//================================================================================================== +// Range classes with specialization for floating types: + + +struct IntRange { + int begin; + int end; + IntRange(int b, int e) : begin(b), end(e) {} +}; + +struct Int64Range { + int64_t begin; + int64_t end; + Int64Range(int64_t b, int64_t e) : begin(b), end(e) {} +}; + +struct DoubleRange { + double begin; + double end; + bool begin_inclusive; + bool end_inclusive; + DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {} +}; + + +//================================================================================================== +// Double options: + + +class DoubleOption : public Option +{ + protected: + DoubleRange range; + double value; + + public: + DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false)) + : Option(n, d, c, ""), range(r), value(def) { + // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly. + } + + operator double (void) const { return value; } + operator double& (void) { return value; } + DoubleOption& operator=(double x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + double tmp = strtod(span, &end); + + if (end == NULL) + return false; + else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + // fprintf(stderr, "READ VALUE: %g\n", value); + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n", + name, type_name, + range.begin_inclusive ? '[' : '(', + range.begin, + range.end, + range.end_inclusive ? ']' : ')', + value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Int options: + + +class IntOption : public Option +{ + protected: + IntRange range; + int32_t value; + + public: + IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int32_t (void) const { return value; } + operator int32_t& (void) { return value; } + IntOption& operator= (int32_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int32_t tmp = strtol(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT32_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4d", range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT32_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4d", range.end); + + fprintf(stderr, "] (default: %d)\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +// Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll. +#ifndef _MSC_VER + +class Int64Option : public Option +{ + protected: + Int64Range range; + int64_t value; + + public: + Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int64_t (void) const { return value; } + operator int64_t& (void) { return value; } + Int64Option& operator= (int64_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int64_t tmp = strtoll(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT64_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4" PRIi64, range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT64_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4" PRIi64, range.end); + + fprintf(stderr, "] (default: %" PRIi64")\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; +#endif + +//================================================================================================== +// String option: + + +class StringOption : public Option +{ + const char* value; + public: + StringOption(const char* c, const char* n, const char* d, const char* def = NULL) + : Option(n, d, c, ""), value(def) {} + + operator const char* (void) const { return value; } + operator const char*& (void) { return value; } + StringOption& operator= (const char* x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + value = span; + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-10s = %8s\n", name, type_name); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Bool option: + + +class BoolOption : public Option +{ + bool value; + + public: + BoolOption(const char* c, const char* n, const char* d, bool v) + : Option(n, d, c, ""), value(v) {} + + operator bool (void) const { return value; } + operator bool& (void) { return value; } + BoolOption& operator=(bool b) { value = b; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (match(span, "-")){ + bool b = !match(span, "no-"); + + if (strcmp(span, name) == 0){ + value = b; + return true; } + } + + return false; + } + + virtual void help (bool verbose = false){ + + fprintf(stderr, " -%s, -no-%s", name, name); + + for (uint32_t i = 0; i < 32 - strlen(name)*2; i++) + fprintf(stderr, " "); + + fprintf(stderr, " "); + fprintf(stderr, "(default: %s)\n", value ? "on" : "off"); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + +//================================================================================================= +} + +#endif +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +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. +**************************************************************************************************/ + +#ifndef Minisat_System_h +#define Minisat_System_h + + +//------------------------------------------------------------------------------------------------- + +namespace Maple { + +static inline double cpuTime(void); // CPU-time in seconds. +extern double memUsed(); // Memory in mega bytes (returns 0 for unsupported architectures). +extern double memUsedPeak(); // Peak-memory in mega bytes (returns 0 for unsupported architectures). + +} + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +static inline double Maple::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +#else +#include +#include +#include + +static inline double Maple::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +#endif + +#endif +/***********************************************************************************[SolverTypes.h] +MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson + Copyright (c) 2007-2010, Niklas Sorensson + +Chanseok Oh's MiniSat Patch Series -- Copyright (c) 2015, Chanseok Oh + +Maple_LCM, Based on MapleCOMSPS_DRUP -- Copyright (c) 2017, Mao Luo, Chu-Min LI, Fan Xiao: implementing a learnt clause minimisation approach +Reference: M. Luo, C.-M. Li, F. Xiao, F. Manya, and Z. L. , “An effective learnt clause minimization approach for cdcl sat solvers,” in IJCAI-2017, 2017, pp. to–appear. + +Maple_LCM_Dist, Based on Maple_LCM -- Copyright (c) 2017, Fan Xiao, Chu-Min LI, Mao Luo: using a new branching heuristic called Distance at the beginning of search + + +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. +**************************************************************************************************/ + + +#ifndef Minisat_SolverTypes_h +#define Minisat_SolverTypes_h + +#include + + + + + + +#include + +namespace Maple { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign ); + + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + +inline Lit mkLit (Var var, bool sign= false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + +inline std::ostream& operator<<(std::ostream& out, const Lit& val) +{ + out << (sign(val) ? -var(val) : var(val)) << std::flush; + return out; +} + + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + +class lbool { + uint8_t value; + +public: + constexpr explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +constexpr auto l_True = Maple::lbool((uint8_t)0); +constexpr auto l_False = Maple::lbool((uint8_t)1); +constexpr auto l_Undef = Maple::lbool((uint8_t)2); + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause; +typedef RegionAllocator::Ref CRef; + +class Clause { + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned has_extra : 1; + unsigned reloced : 1; + unsigned lbd : 26; + unsigned removable : 1; + unsigned size : 32; + //simplify + unsigned simplified : 1;} header; + union { Lit lit; float act; uint32_t abs; uint32_t touched; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, bool use_extra, bool learnt) { + header.mark = 0; + header.learnt = learnt; + header.has_extra = learnt | use_extra; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.removable = 1; + //simplify + // + header.simplified = 0; + + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.has_extra){ + if (header.learnt){ + data[header.size].act = 0; + data[header.size+1].touched = 0; + }else + calcAbstraction(); } + } + +public: + void calcAbstraction() { + assert(header.has_extra); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); if (header.has_extra) data[header.size-i] = data[header.size]; header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + bool has_extra () const { return header.has_extra; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + int lbd () const { return header.lbd; } + void set_lbd (int lbd) { header.lbd = lbd; } + bool removable () const { return header.removable; } + void removable (bool b) { header.removable = b; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + uint32_t& touched () { assert(header.has_extra && header.learnt); return data[header.size+1].touched; } + float& activity () { assert(header.has_extra); return data[header.size].act; } + uint32_t abstraction () const { assert(header.has_extra); return data[header.size].abs; } + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + // simplify + // + void setSimplified(bool b) { header.simplified = b; } + bool simplified() { return header.simplified; } +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + +const CRef CRef_Undef = RegionAllocator::Ref_Undef; +class ClauseAllocator : public RegionAllocator +{ + static int clauseWord32Size(int size, int extras){ + return (sizeof(Clause) + (sizeof(Lit) * (size + extras))) / sizeof(uint32_t); } +public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + int extras = learnt ? 2 : (int)extra_clause_field; + + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), extras)); + new (lea(cid)) Clause(ps, extra_clause_field, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free(CRef cid) + { + Clause& c = operator[](cid); + int extras = c.learnt() ? 2 : (int)c.has_extra(); + RegionAllocator::free(clauseWord32Size(c.size(), extras)); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()){ + to[cr].touched() = c.touched(); + to[cr].activity() = c.activity(); + to[cr].set_lbd(c.lbd()); + to[cr].removable(c.removable()); + // simplify + // + to[cr].setSimplified(c.simplified()); + } + else if (to[cr].has_extra()) to[cr].calcAbstraction(); + } +}; + + +inline std::ostream& operator<<(std::ostream& out, const Clause& cls) +{ + for (int i = 0; i < cls.size(); ++i) + { + out << cls[i] << " "; + } + + return out; +} + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + +public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void clean (const Idx& idx); + void smudge (const Idx& idx){ + if (dirty[toInt(idx)] == 0){ + dirty[toInt(idx)] = 1; + dirties.push(idx); + } + } + + void clear(bool free = true){ + occs .clear(free); + dirty .clear(free); + dirties.clear(free); + } +}; + + +template +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + +public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf("c --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.has_extra); assert(other.header.has_extra); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; +ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + +#endif +/****************************************************************************************[Solver.h] +MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson + Copyright (c) 2007-2010, Niklas Sorensson + +Chanseok Oh's MiniSat Patch Series -- Copyright (c) 2015, Chanseok Oh + +Maple_LCM, Based on MapleCOMSPS_DRUP -- Copyright (c) 2017, Mao Luo, Chu-Min LI, Fan Xiao: implementing a learnt clause minimisation approach +Reference: M. Luo, C.-M. Li, F. Xiao, F. Manya, and Z. L. , “An effective learnt clause minimization approach for cdcl sat solvers,” in IJCAI-2017, 2017, pp. to–appear. + +Maple_LCM_Dist, Based on Maple_LCM -- Copyright (c) 2017, Fan Xiao, Chu-Min LI, Mao Luo: using a new branching heuristic called Distance at the beginning of search + + +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. +**************************************************************************************************/ + +#ifndef Minisat_Solver_h +#define Minisat_Solver_h + +#define ANTI_EXPLORATION +#define BIN_DRUP + +#define GLUCOSE23 +//#define INT_QUEUE_AVG +//#define LOOSE_PROP_STAT + +#ifdef GLUCOSE23 +#define INT_QUEUE_AVG +#define LOOSE_PROP_STAT +#endif + + + + + + + + +// Don't change the actual numbers. +#define LOCAL 0 +#define TIER2 2 +#define CORE 3 + +namespace Maple { + +//================================================================================================= +// Solver -- the main class: + +class Solver { +private: + template + class MyQueue { + int max_sz, q_sz; + int ptr; + int64_t sum; + vec q; + public: + MyQueue(int sz) : max_sz(sz), q_sz(0), ptr(0), sum(0) { assert(sz > 0); q.growTo(sz); } + inline bool full () const { return q_sz == max_sz; } +#ifdef INT_QUEUE_AVG + inline T avg () const { assert(full()); return sum / max_sz; } +#else + inline double avg () const { assert(full()); return sum / (double) max_sz; } +#endif + inline void clear() { sum = 0; q_sz = 0; ptr = 0; } + void push(T e) { + if (q_sz < max_sz) q_sz++; + else sum -= q[ptr]; + sum += e; + q[ptr++] = e; + if (ptr == max_sz) ptr = 0; + } + }; + +inline void printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } + printf("\n"); +} +public: + + // Constructor/Destructor: + // + Solver(); + virtual ~Solver(); + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + + bool addClause (const vec& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + + // Convenience versions of 'toDimacs()': + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () const; + + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void garbageCollect(); + void checkGarbage(double gf); + void checkGarbage(); + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + FILE* drup_file; + int verbosity; + double step_size; + double step_size_dec; + double min_step_size; + int timer; + double var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + bool VSIDS; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + int restart_first; // The initial restart limit. (default 100) + double restart_inc; // The factor with which the restart limit is multiplied in each restart. (default 1.5) + double learntsize_factor; // The intitial limit for learnt clauses is a factor of the original clauses. (default 1 / 3) + double learntsize_inc; // The limit for learnt clauses is multiplied with this factor each restart. (default 1.1) + + int learntsize_adjust_start_confl; + double learntsize_adjust_inc; + + // Statistics: (read-only member variable) + // + uint64_t solves, starts, decisions, rnd_decisions, propagations, conflicts, conflicts_VSIDS; + uint64_t dec_vars, clauses_literals, learnts_literals, max_literals, tot_literals; + uint64_t chrono_backtrack, non_chrono_backtrack; + + vec picked; + vec conflicted; + vec almost_conflicted; +#ifdef ANTI_EXPLORATION + vec canceled; +#endif + +protected: + + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + struct ConflictData + { + ConflictData() : + nHighestLevel(-1), + bOnlyOneLitFromHighest(false) + {} + + int nHighestLevel; + bool bOnlyOneLitFromHighest; + }; + + // Solver state: + // + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + vec clauses; // List of problem clauses. + vec learnts_core, // List of learnt clauses. + learnts_tier2, + learnts_local; + double cla_inc; // Amount to bump next clause with. + vec activity_CHB, // A heuristic measurement of the activity of a variable. + activity_VSIDS,activity_distance; + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches_bin, // Watches for binary clauses only. + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap_CHB, // A priority queue of variables ordered with respect to the variable activity. + order_heap_VSIDS,order_heap_distance; + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + + int core_lbd_cut; + float global_lbd_sum; + MyQueue lbd_queue; // For computing moving averages of recent LBD values. + + uint64_t next_T2_reduce, + next_L_reduce; + + ClauseAllocator ca; + + int confl_to_chrono; + int chrono; + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + vec add_oc; + + vec seen2; // Mostly for efficient LBD computation. 'seen2[i]' will indicate if decision level or variable 'i' has been seen. + uint64_t counter; // Simple counter for marking purpose with 'seen2'. + + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, int level = 0, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, int& out_btlevel, int& out_lbd); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int& nof_conflicts); // Search for a given number of conflicts. + lbool solve_ (); // Main solve method (assumptions given in 'assumptions'). + void reduceDB (); // Reduce the set of learnt clauses. + void reduceDB_Tier2 (); + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void safeRemoveSatisfied(vec& cs, unsigned valid_mark); + void rebuildOrderHeap (); + bool binResMinimize (vec& out_learnt); // Further learnt clause minimization by binary resolution. + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double mult); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void removeClause (CRef cr); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + + ConflictData FindConflictLevel(CRef cind); + +public: + int level (Var x) const; +protected: + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + + template int computeLBD(const V& c) { + int lbd = 0; + + counter++; + for (int i = 0; i < c.size(); i++){ + int l = level(var(c[i])); + if (l != 0 && seen2[l] != counter){ + seen2[l] = counter; + lbd++; } } + + return lbd; + } + +#ifdef BIN_DRUP + static inline int buf_len = 0; + static inline unsigned char drup_buf[2 * 1024 * 1024]; + static inline unsigned char* buf_ptr = drup_buf; + + static inline void byteDRUP(Lit l){ + unsigned int u = 2 * (var(l) + 1) + sign(l); + do{ + *buf_ptr++ = (u & 0x7f) | 0x80; buf_len++; + u = u >> 7; + }while (u); + *(buf_ptr - 1) &= 0x7f; // End marker of this unsigned number. + } + + template + static inline void binDRUP(unsigned char op, const V& c, FILE* drup_file){ + assert(op == 'a' || op == 'd'); + *buf_ptr++ = op; buf_len++; + for (int i = 0; i < c.size(); i++) byteDRUP(c[i]); + *buf_ptr++ = 0; buf_len++; + if (buf_len > 1048576) binDRUP_flush(drup_file); + } + + static inline void binDRUP_strengthen(const Clause& c, Lit l, FILE* drup_file){ + *buf_ptr++ = 'a'; buf_len++; + for (int i = 0; i < c.size(); i++) + if (c[i] != l) byteDRUP(c[i]); + *buf_ptr++ = 0; buf_len++; + if (buf_len > 1048576) binDRUP_flush(drup_file); + } + + static inline void binDRUP_flush(FILE* drup_file){ + fwrite(drup_buf, sizeof(unsigned char), buf_len, drup_file); + // fwrite_unlocked(drup_buf, sizeof(unsigned char), buf_len, drup_file); + buf_ptr = drup_buf; buf_len = 0; + } +#endif + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } + + + // simplify + // +public: + bool simplifyAll(); + void simplifyLearnt(Clause& c); + bool simplifyLearnt_x(vec& learnts_x); + bool simplifyLearnt_core(); + bool simplifyLearnt_tier2(); + int trailRecord; + void litsEnqueue(int cutP, Clause& c); + void cancelUntilTrailRecord(); + void simpleUncheckEnqueue(Lit p, CRef from = CRef_Undef); + CRef simplePropagate(); + uint64_t nbSimplifyAll; + uint64_t simplified_length_record, original_length_record; + uint64_t s_propagations; + + vec simp_learnt_clause; + vec simp_reason_clause; + void simpleAnalyze(CRef confl, vec& out_learnt, vec& reason_clause, bool True_confl); + + // in redundant + bool removed(CRef cr); + // adjust simplifyAll occasion + long curSimplify; + int nbconfbeforesimplify; + int incSimplify; + + bool collectFirstUIP(CRef confl); + vec var_iLevel,var_iLevel_tmp; + uint64_t nbcollectfirstuip, nblearntclause, nbDoubleConflicts, nbTripleConflicts; + int uip1, uip2; + vec pathCs; + CRef propagateLits(vec& lits); + uint64_t previousStarts; + double var_iLevel_inc; + vec involved_lits; + double my_var_decay; + bool DISTANCE; +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + // Heap& order_heap = VSIDS ? order_heap_VSIDS : order_heap_CHB; + Heap& order_heap = DISTANCE ? order_heap_distance : ((!VSIDS)? order_heap_CHB:order_heap_VSIDS); + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { + var_inc *= (1 / var_decay); } + +inline void Solver::varBumpActivity(Var v, double mult) { + if ( (activity_VSIDS[v] += var_inc * mult) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity_VSIDS[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap_VSIDS.inHeap(v)) order_heap_VSIDS.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts_local.size(); i++) + ca[learnts_local[i]].activity() *= 1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect(); } + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, decisionLevel(), from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline bool Solver::locked (const Clause& c) const { + int i = c.size() != 2 ? 0 : (value(c[0]) == l_True ? 0 : 1); + return value(c[i]) == l_True && reason(var(c[i])) != CRef_Undef && ca.lea(reason(var(c[i]))) == &c; +} +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts_core.size() + learnts_tier2.size() + learnts_local.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) dec_vars++; + else if (!b && decision[v]) dec_vars--; + + decision[v] = b; + if (b && !order_heap_CHB.inHeap(v)){ + order_heap_CHB.insert(v); + order_heap_VSIDS.insert(v); + order_heap_distance.insert(v);} +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + +//================================================================================================= +// Debug etc: + + +//================================================================================================= +} + +#endif +/***************************************************************************************[Solver.cc] +MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson + Copyright (c) 2007-2010, Niklas Sorensson + +Chanseok Oh's MiniSat Patch Series -- Copyright (c) 2015, Chanseok Oh + +Maple_LCM, Based on MapleCOMSPS_DRUP -- Copyright (c) 2017, Mao Luo, Chu-Min LI, Fan Xiao: implementing a learnt clause minimisation approach +Reference: M. Luo, C.-M. Li, F. Xiao, F. Manya, and Z. L. , “An effective learnt clause minimization approach for cdcl sat solvers,” in IJCAI-2017, 2017, pp. to–appear. + +Maple_LCM_Dist, Based on Maple_LCM -- Copyright (c) 2017, Fan Xiao, Chu-Min LI, Mao Luo: using a new branching heuristic called Distance at the beginning of search + +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 + + + + +namespace Maple { + +//#define PRINT_OUT + +//================================================================================================= +// Options: + + +static const char* _cat = "CORE"; + +static DoubleOption opt_step_size (_cat, "step-size", "Initial step size", 0.40, DoubleRange(0, false, 1, false)); +static DoubleOption opt_step_size_dec (_cat, "step-size-dec","Step size decrement", 0.000001, DoubleRange(0, false, 1, false)); +static DoubleOption opt_min_step_size (_cat, "min-step-size","Minimal step size", 0.06, DoubleRange(0, false, 1, false)); +static DoubleOption opt_var_decay (_cat, "var-decay", "The variable activity decay factor", 0.80, DoubleRange(0, false, 1, false)); +static DoubleOption opt_clause_decay (_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); +static DoubleOption opt_random_var_freq (_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, DoubleRange(0, true, 1, true)); +static DoubleOption opt_random_seed (_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); +static IntOption opt_ccmin_mode (_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); +static IntOption opt_phase_saving (_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); +static BoolOption opt_rnd_init_act (_cat, "rnd-init", "Randomize the initial activity", false); +static IntOption opt_restart_first (_cat, "rfirst", "The base restart interval", 100, IntRange(1, INT32_MAX)); +static DoubleOption opt_restart_inc (_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); +static DoubleOption opt_garbage_frac (_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, DoubleRange(0, false, HUGE_VAL, false)); +static IntOption opt_chrono (_cat, "chrono", "Controls if to perform chrono backtrack", 100, IntRange(-1, INT32_MAX)); +static IntOption opt_conf_to_chrono (_cat, "confl-to-chrono", "Controls number of conflicts to perform chrono backtrack", 4000, IntRange(-1, INT32_MAX)); + + +//================================================================================================= +// Constructor/Destructor: + + +inline Solver::Solver() : + + // Parameters (user settable): + // + drup_file (NULL) + , verbosity (0) + , step_size (opt_step_size) + , step_size_dec (opt_step_size_dec) + , min_step_size (opt_min_step_size) + , timer (5000) + , var_decay (opt_var_decay) + , clause_decay (opt_clause_decay) + , random_var_freq (opt_random_var_freq) + , random_seed (opt_random_seed) + , VSIDS (false) + , ccmin_mode (opt_ccmin_mode) + , phase_saving (opt_phase_saving) + , rnd_pol (false) + , rnd_init_act (opt_rnd_init_act) + , garbage_frac (opt_garbage_frac) + , restart_first (opt_restart_first) + , restart_inc (opt_restart_inc) + + // Parameters (the rest): + // + , learntsize_factor((double)1/(double)3), learntsize_inc(1.1) + + // Parameters (experimental): + // + , learntsize_adjust_start_confl (100) + , learntsize_adjust_inc (1.5) + + // Statistics: (formerly in 'SolverStats') + // + , solves(0), starts(0), decisions(0), rnd_decisions(0), propagations(0), conflicts(0), conflicts_VSIDS(0) + , dec_vars(0), clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) + , chrono_backtrack(0), non_chrono_backtrack(0) + + , ok (true) + , cla_inc (1) + , var_inc (1) + , watches_bin (WatcherDeleted(ca)) + , watches (WatcherDeleted(ca)) + , qhead (0) + , simpDB_assigns (-1) + , simpDB_props (0) + , order_heap_CHB (VarOrderLt(activity_CHB)) + , order_heap_VSIDS (VarOrderLt(activity_VSIDS)) + , progress_estimate (0) + , remove_satisfied (true) + + , core_lbd_cut (3) + , global_lbd_sum (0) + , lbd_queue (50) + , next_T2_reduce (10000) + , next_L_reduce (15000) + , confl_to_chrono (opt_conf_to_chrono) + , chrono (opt_chrono) + + , counter (0) + + // Resource constraints: + // + , conflict_budget (-1) + , propagation_budget (-1) + , asynch_interrupt (false) + + // simplfiy + , nbSimplifyAll(0) + , s_propagations(0) + + // simplifyAll adjust occasion + , curSimplify(1) + , nbconfbeforesimplify(1000) + , incSimplify(1000) + + , my_var_decay (0.6) + , DISTANCE (true) + , var_iLevel_inc (1) + , order_heap_distance(VarOrderLt(activity_distance)) + +{} + + +inline Solver::~Solver() +{ +} + + +// simplify All +// +inline CRef Solver::simplePropagate() +{ + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watches_bin.cleanAll(); + while (qhead < trail.size()) + { + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec& ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + + // First, Propagate binary clauses + vec& wbin = watches_bin[p]; + + for (int k = 0; kblocker; + if (value(blocker) == l_True) + { + *j++ = *i++; continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + // i++; + + // If 0th watch is true, then clause is already satisfied. + // However, 0th watch is not the blocker, make it blocker using a new watcher w + // why not simply do i->blocker=first in this case? + Lit first = c[0]; + // Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True) + { + i->blocker = first; + *j++ = *i++; continue; + } + + // Look for new watch: + //if (incremental) + //{ // ----------------- INCREMENTAL MODE + // int choosenPos = -1; + // for (int k = 2; k < c.size(); k++) + // { + // if (value(c[k]) != l_False) + // { + // if (decisionLevel()>assumptions.size()) + // { + // choosenPos = k; + // break; + // } + // else + // { + // choosenPos = k; + + // if (value(c[k]) == l_True || !isSelector(var(c[k]))) { + // break; + // } + // } + + // } + // } + // if (choosenPos != -1) + // { + // // watcher i is abandonned using i++, because cr watches now ~c[k] instead of p + // // the blocker is first in the watcher. However, + // // the blocker in the corresponding watcher in ~first is not c[1] + // Watcher w = Watcher(cr, first); i++; + // c[1] = c[choosenPos]; c[choosenPos] = false_lit; + // watches[~c[1]].push(w); + // goto NextClause; + // } + //} + else + { // ----------------- DEFAULT MODE (NOT INCREMENTAL) + for (int k = 2; k < c.size(); k++) + { + + if (value(c[k]) != l_False) + { + // watcher i is abandonned using i++, because cr watches now ~c[k] instead of p + // the blocker is first in the watcher. However, + // the blocker in the corresponding watcher in ~first is not c[1] + Watcher w = Watcher(cr, first); i++; + c[1] = c[k]; c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; + } + } + } + + // Did not find watch -- clause is unit under assignment: + i->blocker = first; + *j++ = *i++; + if (value(first) == l_False) + { + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + else + { + simpleUncheckEnqueue(first, cr); + } +NextClause:; + } + ws.shrink(i - j); + } + + s_propagations += num_props; + + return confl; +} + +inline void Solver::simpleUncheckEnqueue(Lit p, CRef from){ + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); // this makes a lbool object whose value is sign(p) + vardata[var(p)].reason = from; + trail.push_(p); +} + +inline void Solver::cancelUntilTrailRecord() +{ + for (int c = trail.size() - 1; c >= trailRecord; c--) + { + Var x = var(trail[c]); + assigns[x] = l_Undef; + + } + qhead = trailRecord; + trail.shrink(trail.size() - trailRecord); + +} + +inline void Solver::litsEnqueue(int cutP, Clause& c) +{ + for (int i = cutP; i < c.size(); i++) + { + simpleUncheckEnqueue(~c[i]); + } +} + +inline bool Solver::removed(CRef cr) { + return ca[cr].mark() == 1; +} + +inline void Solver::simpleAnalyze(CRef confl, vec& out_learnt, vec& reason_clause, bool True_confl) +{ + int pathC = 0; + Lit p = lit_Undef; + int index = trail.size() - 1; + + do{ + if (confl != CRef_Undef){ + reason_clause.push(confl); + Clause& c = ca[confl]; + // Special case for binary clauses + // The first one has to be SAT + if (p != lit_Undef && c.size() == 2 && value(c[0]) == l_False) { + + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + // if True_confl==true, then choose p begin with the 1th index of c; + for (int j = (p == lit_Undef && True_confl == false) ? 0 : 1; j < c.size(); j++){ + Lit q = c[j]; + if (!seen[var(q)]){ + seen[var(q)] = 1; + pathC++; + } + } + } + else if (confl == CRef_Undef){ + out_learnt.push(~p); + } + // if not break, while() will come to the index of trail blow 0, and fatal error occur; + if (pathC == 0) break; + // Select next clause to look at: + while (!seen[var(trail[index--])]); + // if the reason cr from the 0-level assigned var, we must break avoid move forth further; + // but attention that maybe seen[x]=1 and never be clear. However makes no matter; + if (trailRecord > index + 1) break; + p = trail[index + 1]; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + } while (pathC >= 0); +} + +inline void Solver::simplifyLearnt(Clause& c) +{ + //// + original_length_record += c.size(); + + trailRecord = trail.size();// record the start pointer + + vec falseLit; + falseLit.clear(); + + //sort(&c[0], c.size(), VarOrderLevelLt(vardata)); + + bool True_confl = false; + int beforeSize, afterSize; + beforeSize = c.size(); + int i, j; + CRef confl; + + for (i = 0, j = 0; i < c.size(); i++){ + if (value(c[i]) == l_Undef){ + //printf("///@@@ uncheckedEnqueue:index = %d. l_Undef\n", i); + simpleUncheckEnqueue(~c[i]); + c[j++] = c[i]; + confl = simplePropagate(); + if (confl != CRef_Undef){ + break; + } + } + else{ + if (value(c[i]) == l_True){ + //printf("///@@@ uncheckedEnqueue:index = %d. l_True\n", i); + c[j++] = c[i]; + True_confl = true; + confl = reason(var(c[i])); + break; + } + else{ + //printf("///@@@ uncheckedEnqueue:index = %d. l_False\n", i); + falseLit.push(c[i]); + } + } + } + c.shrink(c.size() - j); + afterSize = c.size(); + //printf("\nbefore : %d, after : %d ", beforeSize, afterSize); + + + if (confl != CRef_Undef || True_confl == true){ + simp_learnt_clause.clear(); + simp_reason_clause.clear(); + if (True_confl == true){ + simp_learnt_clause.push(c.last()); + } + simpleAnalyze(confl, simp_learnt_clause, simp_reason_clause, True_confl); + + if (simp_learnt_clause.size() < c.size()){ + for (i = 0; i < simp_learnt_clause.size(); i++){ + c[i] = simp_learnt_clause[i]; + } + c.shrink(c.size() - i); + } + } + + cancelUntilTrailRecord(); + + //// + simplified_length_record += c.size(); + +} + +inline bool Solver::simplifyLearnt_x(vec& learnts_x) +{ + int beforeSize, afterSize; + int learnts_x_size_before = learnts_x.size(); + + int ci, cj, li, lj; + bool sat, false_lit; + unsigned int nblevels; + //// + //printf("learnts_x size : %d\n", learnts_x.size()); + + //// + int nbSimplified = 0; + int nbSimplifing = 0; + + for (ci = 0, cj = 0; ci < learnts_x.size(); ci++){ + CRef cr = learnts_x[ci]; + Clause& c = ca[cr]; + + if (removed(cr)) continue; + else if (c.simplified()){ + learnts_x[cj++] = learnts_x[ci]; + //// + nbSimplified++; + } + else{ + //// + nbSimplifing++; + sat = false_lit = false; + for (int i = 0; i < c.size(); i++){ + if (value(c[i]) == l_True){ + sat = true; + break; + } + else if (value(c[i]) == l_False){ + false_lit = true; + } + } + if (sat){ + removeClause(cr); + } + else{ + detachClause(cr, true); + + if (false_lit){ + for (li = lj = 0; li < c.size(); li++){ + if (value(c[li]) != l_False){ + c[lj++] = c[li]; + } + } + c.shrink(li - lj); + } + + beforeSize = c.size(); + assert(c.size() > 1); + // simplify a learnt clause c + simplifyLearnt(c); + assert(c.size() > 0); + afterSize = c.size(); + + //printf("beforeSize: %2d, afterSize: %2d\n", beforeSize, afterSize); + + if (c.size() == 1){ + // when unit clause occur, enqueue and propagate + uncheckedEnqueue(c[0]); + if (propagate() != CRef_Undef){ + ok = false; + return false; + } + // delete the clause memory in logic + c.mark(1); + ca.free(cr); + } + else{ + attachClause(cr); + learnts_x[cj++] = learnts_x[ci]; + + nblevels = computeLBD(c); + if (nblevels < c.lbd()){ + //printf("lbd-before: %d, lbd-after: %d\n", c.lbd(), nblevels); + c.set_lbd(nblevels); + } + if (c.mark() != CORE){ + if (c.lbd() <= core_lbd_cut){ + //if (c.mark() == LOCAL) local_learnts_dirty = true; + //else tier2_learnts_dirty = true; + cj--; + learnts_core.push(cr); + c.mark(CORE); + } + else if (c.mark() == LOCAL && c.lbd() <= 6){ + //local_learnts_dirty = true; + cj--; + learnts_tier2.push(cr); + c.mark(TIER2); + } + } + + c.setSimplified(true); + } + } + } + } + learnts_x.shrink(ci - cj); + + // printf("c nbLearnts_x %d / %d, nbSimplified: %d, nbSimplifing: %d\n", + // learnts_x_size_before, learnts_x.size(), nbSimplified, nbSimplifing); + + return true; +} + +inline bool Solver::simplifyLearnt_core() +{ + int beforeSize, afterSize; + int learnts_core_size_before = learnts_core.size(); + + int ci, cj, li, lj; + bool sat, false_lit; + unsigned int nblevels; + //// + //printf("learnts_x size : %d\n", learnts_x.size()); + + //// + int nbSimplified = 0; + int nbSimplifing = 0; + + for (ci = 0, cj = 0; ci < learnts_core.size(); ci++){ + CRef cr = learnts_core[ci]; + Clause& c = ca[cr]; + + if (removed(cr)) continue; + else if (c.simplified()){ + learnts_core[cj++] = learnts_core[ci]; + //// + nbSimplified++; + } + else{ + int saved_size=c.size(); + // if (drup_file){ + // add_oc.clear(); + // for (int i = 0; i < c.size(); i++) add_oc.push(c[i]); } + //// + nbSimplifing++; + sat = false_lit = false; + for (int i = 0; i < c.size(); i++){ + if (value(c[i]) == l_True){ + sat = true; + break; + } + else if (value(c[i]) == l_False){ + false_lit = true; + } + } + if (sat){ + removeClause(cr); + } + else{ + detachClause(cr, true); + + if (false_lit){ + for (li = lj = 0; li < c.size(); li++){ + if (value(c[li]) != l_False){ + c[lj++] = c[li]; + } + } + c.shrink(li - lj); + } + + beforeSize = c.size(); + assert(c.size() > 1); + // simplify a learnt clause c + simplifyLearnt(c); + assert(c.size() > 0); + afterSize = c.size(); + + if(drup_file && saved_size !=c.size()){ +#ifdef BIN_DRUP + binDRUP('a', c , drup_file); + // binDRUP('d', add_oc, drup_file); +#else + for (int i = 0; i < c.size(); i++) + fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(drup_file, "0\n"); + + // fprintf(drup_file, "d "); + // for (int i = 0; i < add_oc.size(); i++) + // fprintf(drup_file, "%i ", (var(add_oc[i]) + 1) * (-2 * sign(add_oc[i]) + 1)); + // fprintf(drup_file, "0\n"); +#endif + } + + //printf("beforeSize: %2d, afterSize: %2d\n", beforeSize, afterSize); + + if (c.size() == 1){ + // when unit clause occur, enqueue and propagate + uncheckedEnqueue(c[0]); + if (propagate() != CRef_Undef){ + ok = false; + return false; + } + // delete the clause memory in logic + c.mark(1); + ca.free(cr); +//#ifdef BIN_DRUP +// binDRUP('d', c, drup_file); +//#else +// fprintf(drup_file, "d "); +// for (int i = 0; i < c.size(); i++) +// fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); +// fprintf(drup_file, "0\n"); +//#endif + } + else{ + attachClause(cr); + learnts_core[cj++] = learnts_core[ci]; + + nblevels = computeLBD(c); + if (nblevels < c.lbd()){ + //printf("lbd-before: %d, lbd-after: %d\n", c.lbd(), nblevels); + c.set_lbd(nblevels); + } + + c.setSimplified(true); + } + } + } + } + learnts_core.shrink(ci - cj); + + // printf("c nbLearnts_core %d / %d, nbSimplified: %d, nbSimplifing: %d\n", + // learnts_core_size_before, learnts_core.size(), nbSimplified, nbSimplifing); + + return true; + +} + +inline bool Solver::simplifyLearnt_tier2() +{ + int beforeSize, afterSize; + int learnts_tier2_size_before = learnts_tier2.size(); + + int ci, cj, li, lj; + bool sat, false_lit; + unsigned int nblevels; + //// + //printf("learnts_x size : %d\n", learnts_x.size()); + + //// + int nbSimplified = 0; + int nbSimplifing = 0; + + for (ci = 0, cj = 0; ci < learnts_tier2.size(); ci++){ + CRef cr = learnts_tier2[ci]; + Clause& c = ca[cr]; + + if (removed(cr)) continue; + else if (c.simplified()){ + learnts_tier2[cj++] = learnts_tier2[ci]; + //// + nbSimplified++; + } + else{ + int saved_size=c.size(); + // if (drup_file){ + // add_oc.clear(); + // for (int i = 0; i < c.size(); i++) add_oc.push(c[i]); } + //// + nbSimplifing++; + sat = false_lit = false; + for (int i = 0; i < c.size(); i++){ + if (value(c[i]) == l_True){ + sat = true; + break; + } + else if (value(c[i]) == l_False){ + false_lit = true; + } + } + if (sat){ + removeClause(cr); + } + else{ + detachClause(cr, true); + + if (false_lit){ + for (li = lj = 0; li < c.size(); li++){ + if (value(c[li]) != l_False){ + c[lj++] = c[li]; + } + } + c.shrink(li - lj); + } + + beforeSize = c.size(); + assert(c.size() > 1); + // simplify a learnt clause c + simplifyLearnt(c); + assert(c.size() > 0); + afterSize = c.size(); + + if(drup_file && saved_size!=c.size()){ + +#ifdef BIN_DRUP + binDRUP('a', c , drup_file); + // binDRUP('d', add_oc, drup_file); +#else + for (int i = 0; i < c.size(); i++) + fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(drup_file, "0\n"); + + // fprintf(drup_file, "d "); + // for (int i = 0; i < add_oc.size(); i++) + // fprintf(drup_file, "%i ", (var(add_oc[i]) + 1) * (-2 * sign(add_oc[i]) + 1)); + // fprintf(drup_file, "0\n"); +#endif + } + + //printf("beforeSize: %2d, afterSize: %2d\n", beforeSize, afterSize); + + if (c.size() == 1){ + // when unit clause occur, enqueue and propagate + uncheckedEnqueue(c[0]); + if (propagate() != CRef_Undef){ + ok = false; + return false; + } + // delete the clause memory in logic + c.mark(1); + ca.free(cr); +//#ifdef BIN_DRUP +// binDRUP('d', c, drup_file); +//#else +// fprintf(drup_file, "d "); +// for (int i = 0; i < c.size(); i++) +// fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); +// fprintf(drup_file, "0\n"); +//#endif + } + else{ + attachClause(cr); + learnts_tier2[cj++] = learnts_tier2[ci]; + + nblevels = computeLBD(c); + if (nblevels < c.lbd()){ + //printf("lbd-before: %d, lbd-after: %d\n", c.lbd(), nblevels); + c.set_lbd(nblevels); + } + + if (c.lbd() <= core_lbd_cut){ + cj--; + learnts_core.push(cr); + c.mark(CORE); + } + + c.setSimplified(true); + } + } + } + } + learnts_tier2.shrink(ci - cj); + + // printf("c nbLearnts_tier2 %d / %d, nbSimplified: %d, nbSimplifing: %d\n", + // learnts_tier2_size_before, learnts_tier2.size(), nbSimplified, nbSimplifing); + + return true; + +} + +inline bool Solver::simplifyAll() +{ + //// + simplified_length_record = original_length_record = 0; + + if (!ok || propagate() != CRef_Undef) + return ok = false; + + //// cleanLearnts(also can delete these code), here just for analyzing + //if (local_learnts_dirty) cleanLearnts(learnts_local, LOCAL); + //if (tier2_learnts_dirty) cleanLearnts(learnts_tier2, TIER2); + //local_learnts_dirty = tier2_learnts_dirty = false; + + if (!simplifyLearnt_core()) return ok = false; + if (!simplifyLearnt_tier2()) return ok = false; + //if (!simplifyLearnt_x(learnts_local)) return ok = false; + + checkGarbage(); + + //// + // printf("c size_reduce_ratio : %4.2f%%\n", + // original_length_record == 0 ? 0 : (original_length_record - simplified_length_record) * 100 / (double)original_length_record); + + return true; +} +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// +inline Var Solver::newVar(bool sign, bool dvar) +{ + int v = nVars(); + watches_bin.init(mkLit(v, false)); + watches_bin.init(mkLit(v, true )); + watches .init(mkLit(v, false)); + watches .init(mkLit(v, true )); + assigns .push(l_Undef); + vardata .push(mkVarData(CRef_Undef, 0)); + activity_CHB .push(0); + activity_VSIDS.push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + + picked.push(0); + conflicted.push(0); + almost_conflicted.push(0); +#ifdef ANTI_EXPLORATION + canceled.push(0); +#endif + + seen .push(0); + seen2 .push(0); + polarity .push(sign); + decision .push(); + trail .capacity(v+1); + setDecisionVar(v, dvar); + + activity_distance.push(0); + var_iLevel.push(0); + var_iLevel_tmp.push(0); + pathCs.push(0); + return v; +} + + +inline bool Solver::addClause_(vec& ps) +{ + assert(decisionLevel() == 0); + if (!ok) return false; + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + Lit p; int i, j; + + if (drup_file){ + add_oc.clear(); + for (int i = 0; i < ps.size(); i++) add_oc.push(ps[i]); } + + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) + if (value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if (value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if (drup_file && i != j){ +#ifdef BIN_DRUP + binDRUP('a', ps, drup_file); + binDRUP('d', add_oc, drup_file); +#else + for (int i = 0; i < ps.size(); i++) + fprintf(drup_file, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(drup_file, "0\n"); + + fprintf(drup_file, "d "); + for (int i = 0; i < add_oc.size(); i++) + fprintf(drup_file, "%i ", (var(add_oc[i]) + 1) * (-2 * sign(add_oc[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + } + + if (ps.size() == 0) + return ok = false; + else if (ps.size() == 1){ + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + }else{ + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + + +inline void Solver::attachClause(CRef cr) { + const Clause& c = ca[cr]; + assert(c.size() > 1); + OccLists, WatcherDeleted>& ws = c.size() == 2 ? watches_bin : watches; + ws[~c[0]].push(Watcher(cr, c[1])); + ws[~c[1]].push(Watcher(cr, c[0])); + if (c.learnt()) learnts_literals += c.size(); + else clauses_literals += c.size(); } + + +inline void Solver::detachClause(CRef cr, bool strict) { + const Clause& c = ca[cr]; + assert(c.size() > 1); + OccLists, WatcherDeleted>& ws = c.size() == 2 ? watches_bin : watches; + + if (strict){ + remove(ws[~c[0]], Watcher(cr, c[1])); + remove(ws[~c[1]], Watcher(cr, c[0])); + }else{ + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + ws.smudge(~c[0]); + ws.smudge(~c[1]); + } + + if (c.learnt()) learnts_literals -= c.size(); + else clauses_literals -= c.size(); } + + +inline void Solver::removeClause(CRef cr) { + Clause& c = ca[cr]; + + if (drup_file){ + if (c.mark() != 1){ +#ifdef BIN_DRUP + binDRUP('d', c, drup_file); +#else + fprintf(drup_file, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + }else + printf("c Bug. I don't expect this to happen.\n"); + } + + detachClause(cr); + // Don't leave pointers to free'd memory! + if (locked(c)){ + Lit implied = c.size() != 2 ? c[0] : (value(c[0]) == l_True ? c[0] : c[1]); + vardata[var(implied)].reason = CRef_Undef; } + c.mark(1); + ca.free(cr); +} + + +inline bool Solver::satisfied(const Clause& c) const { + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) + return true; + return false; } + + +// Revert to the state at given level (keeping all assignment at 'level' but not beyond). +// +inline void Solver::cancelUntil(int bLevel) { + + if (decisionLevel() > bLevel){ +#ifdef PRINT_OUT + std::cout << "bt " << bLevel << "\n"; +#endif + add_tmp.clear(); + for (int c = trail.size()-1; c >= trail_lim[bLevel]; c--) + { + Var x = var(trail[c]); + + if (level(x) <= bLevel) + { + add_tmp.push(trail[c]); + } + else + { + if (!VSIDS){ + uint32_t age = conflicts - picked[x]; + if (age > 0){ + double adjusted_reward = ((double) (conflicted[x] + almost_conflicted[x])) / ((double) age); + double old_activity = activity_CHB[x]; + activity_CHB[x] = step_size * adjusted_reward + ((1 - step_size) * old_activity); + if (order_heap_CHB.inHeap(x)){ + if (activity_CHB[x] > old_activity) + order_heap_CHB.decrease(x); + else + order_heap_CHB.increase(x); + } + } +#ifdef ANTI_EXPLORATION + canceled[x] = conflicts; +#endif + } + + assigns [x] = l_Undef; +#ifdef PRINT_OUT + std::cout << "undo " << x << "\n"; +#endif + if (phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) + polarity[x] = sign(trail[c]); + insertVarOrder(x); + } + } + qhead = trail_lim[bLevel]; + trail.shrink(trail.size() - trail_lim[bLevel]); + trail_lim.shrink(trail_lim.size() - bLevel); + for (int nLitId = add_tmp.size() - 1; nLitId >= 0; --nLitId) + { + trail.push_(add_tmp[nLitId]); + } + + add_tmp.clear(); + } } + + +//================================================================================================= +// Major methods: + + +inline Lit Solver::pickBranchLit() +{ + Var next = var_Undef; + // Heap& order_heap = VSIDS ? order_heap_VSIDS : order_heap_CHB; + Heap& order_heap = DISTANCE ? order_heap_distance : ((!VSIDS)? order_heap_CHB:order_heap_VSIDS); + + // Random decision: + /*if (drand(random_seed) < random_var_freq && !order_heap.empty()){ + next = order_heap[irand(random_seed,order_heap.size())]; + if (value(next) == l_Undef && decision[next]) + rnd_decisions++; }*/ + + // Activity based decision: + while (next == var_Undef || value(next) != l_Undef || !decision[next]) + if (order_heap.empty()) + return lit_Undef; + else{ +#ifdef ANTI_EXPLORATION + if (!VSIDS){ + Var v = order_heap_CHB[0]; + uint32_t age = conflicts - canceled[v]; + while (age > 0){ + double decay = pow(0.95, age); + activity_CHB[v] *= decay; + if (order_heap_CHB.inHeap(v)) + order_heap_CHB.increase(v); + canceled[v] = conflicts; + v = order_heap_CHB[0]; + age = conflicts - canceled[v]; + } + } +#endif + next = order_heap.removeMin(); + } + + return mkLit(next, polarity[next]); +} + +inline Solver::ConflictData Solver::FindConflictLevel(CRef cind) +{ + ConflictData data; + Clause& conflCls = ca[cind]; + data.nHighestLevel = level(var(conflCls[0])); + if (data.nHighestLevel == decisionLevel() && level(var(conflCls[1])) == decisionLevel()) + { + return data; + } + + int highestId = 0; + data.bOnlyOneLitFromHighest = true; + // find the largest decision level in the clause + for (int nLitId = 1; nLitId < conflCls.size(); ++nLitId) + { + int nLevel = level(var(conflCls[nLitId])); + if (nLevel > data.nHighestLevel) + { + highestId = nLitId; + data.nHighestLevel = nLevel; + data.bOnlyOneLitFromHighest = true; + } + else if (nLevel == data.nHighestLevel && data.bOnlyOneLitFromHighest == true) + { + data.bOnlyOneLitFromHighest = false; + } + } + + if (highestId != 0) + { + std::swap(conflCls[0], conflCls[highestId]); + if (highestId > 1) + { + OccLists, WatcherDeleted>& ws = conflCls.size() == 2 ? watches_bin : watches; + //ws.smudge(~conflCls[highestId]); + remove(ws[~conflCls[highestId]], Watcher(cind, conflCls[1])); + ws[~conflCls[0]].push(Watcher(cind, conflCls[1])); + } + } + + return data; +} + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +inline void Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel, int& out_lbd) +{ + int pathC = 0; + Lit p = lit_Undef; + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + int nDecisionLevel = level(var(ca[confl][0])); + assert(nDecisionLevel == level(var(ca[confl][0]))); + + do{ + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause& c = ca[confl]; + + // For binary clauses, we don't rearrange literals in propagate(), so check and make sure the first is an implied lit. + if (p != lit_Undef && c.size() == 2 && value(c[0]) == l_False){ + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; } + + // Update LBD if improved. + if (c.learnt() && c.mark() != CORE){ + int lbd = computeLBD(c); + if (lbd < c.lbd()){ + if (c.lbd() <= 30) c.removable(false); // Protect once from reduction. + c.set_lbd(lbd); + if (lbd <= core_lbd_cut){ + learnts_core.push(confl); + c.mark(CORE); + }else if (lbd <= 6 && c.mark() == LOCAL){ + // Bug: 'cr' may already be in 'learnts_tier2', e.g., if 'cr' was demoted from TIER2 + // to LOCAL previously and if that 'cr' is not cleaned from 'learnts_tier2' yet. + learnts_tier2.push(confl); + c.mark(TIER2); } + } + + if (c.mark() == TIER2) + c.touched() = conflicts; + else if (c.mark() == LOCAL) + claBumpActivity(c); + } + + for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){ + Lit q = c[j]; + + if (!seen[var(q)] && level(var(q)) > 0){ + if (VSIDS){ + varBumpActivity(var(q), .5); + add_tmp.push(q); + }else + conflicted[var(q)]++; + seen[var(q)] = 1; + if (level(var(q)) >= nDecisionLevel){ + pathC++; + }else + out_learnt.push(q); + } + } + + // Select next clause to look at: + do { + while (!seen[var(trail[index--])]); + p = trail[index+1]; + } while (level(var(p)) < nDecisionLevel); + + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + }while (pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + out_learnt.copyTo(analyze_toclear); + if (ccmin_mode == 2){ + uint32_t abstract_level = 0; + for (i = 1; i < out_learnt.size(); i++) + abstract_level |= abstractLevel(var(out_learnt[i])); // (maintain an abstraction of levels involved in conflict) + + for (i = j = 1; i < out_learnt.size(); i++) + if (reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i], abstract_level)) + out_learnt[j++] = out_learnt[i]; + + }else if (ccmin_mode == 1){ + for (i = j = 1; i < out_learnt.size(); i++){ + Var x = var(out_learnt[i]); + + if (reason(x) == CRef_Undef) + out_learnt[j++] = out_learnt[i]; + else{ + Clause& c = ca[reason(var(out_learnt[i]))]; + for (int k = c.size() == 2 ? 0 : 1; k < c.size(); k++) + if (!seen[var(c[k])] && level(var(c[k])) > 0){ + out_learnt[j++] = out_learnt[i]; + break; } + } + } + }else + i = j = out_learnt.size(); + + max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + tot_literals += out_learnt.size(); + + out_lbd = computeLBD(out_learnt); + if (out_lbd <= 6 && out_learnt.size() <= 30) // Try further minimization? + if (binResMinimize(out_learnt)) + out_lbd = computeLBD(out_learnt); // Recompute LBD if minimized. + + // Find correct backtrack level: + // + if (out_learnt.size() == 1) + out_btlevel = 0; + else{ + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for (int i = 2; i < out_learnt.size(); i++) + if (level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } + + if (VSIDS){ + for (int i = 0; i < add_tmp.size(); i++){ + Var v = var(add_tmp[i]); + if (level(v) >= out_btlevel - 1) + varBumpActivity(v, 1); + } + add_tmp.clear(); + }else{ + seen[var(p)] = true; + for(int i = out_learnt.size() - 1; i >= 0; i--){ + Var v = var(out_learnt[i]); + CRef rea = reason(v); + if (rea != CRef_Undef){ + const Clause& reaC = ca[rea]; + for (int i = 0; i < reaC.size(); i++){ + Lit l = reaC[i]; + if (!seen[var(l)]){ + seen[var(l)] = true; + almost_conflicted[var(l)]++; + analyze_toclear.push(l); } } } } } + + for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) +} + + +// Try further learnt clause minimization by means of binary clause resolution. +inline bool Solver::binResMinimize(vec& out_learnt) +{ + // Preparation: remember which false variables we have in 'out_learnt'. + counter++; + for (int i = 1; i < out_learnt.size(); i++) + seen2[var(out_learnt[i])] = counter; + + // Get the list of binary clauses containing 'out_learnt[0]'. + const vec& ws = watches_bin[~out_learnt[0]]; + + int to_remove = 0; + for (int i = 0; i < ws.size(); i++){ + Lit the_other = ws[i].blocker; + // Does 'the_other' appear negatively in 'out_learnt'? + if (seen2[var(the_other)] == counter && value(the_other) == l_True){ + to_remove++; + seen2[var(the_other)] = counter - 1; // Remember to remove this variable. + } + } + + // Shrink. + if (to_remove > 0){ + int last = out_learnt.size() - 1; + for (int i = 1; i < out_learnt.size() - to_remove; i++) + if (seen2[var(out_learnt[i])] != counter) + out_learnt[i--] = out_learnt[last--]; + out_learnt.shrink(to_remove); + } + return to_remove != 0; +} + + +// Check if 'p' can be removed. 'abstract_levels' is used to abort early if the algorithm is +// visiting literals at levels that cannot be removed later. +inline bool Solver::litRedundant(Lit p, uint32_t abstract_levels) +{ + analyze_stack.clear(); analyze_stack.push(p); + int top = analyze_toclear.size(); + while (analyze_stack.size() > 0){ + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause& c = ca[reason(var(analyze_stack.last()))]; analyze_stack.pop(); + + // Special handling for binary clauses like in 'analyze()'. + if (c.size() == 2 && value(c[0]) == l_False){ + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; } + + for (int i = 1; i < c.size(); i++){ + Lit p = c[i]; + if (!seen[var(p)] && level(var(p)) > 0){ + if (reason(var(p)) != CRef_Undef && (abstractLevel(var(p)) & abstract_levels) != 0){ + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + }else{ + for (int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +inline void Solver::analyzeFinal(Lit p, vec& out_conflict) +{ + out_conflict.clear(); + out_conflict.push(p); + + if (decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for (int i = trail.size()-1; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + if (reason(x) == CRef_Undef){ + assert(level(x) > 0); + out_conflict.push(~trail[i]); + }else{ + Clause& c = ca[reason(x)]; + for (int j = c.size() == 2 ? 0 : 1; j < c.size(); j++) + if (level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + + +inline void Solver::uncheckedEnqueue(Lit p, int level, CRef from) +{ + assert(value(p) == l_Undef); + Var x = var(p); + if (!VSIDS){ + picked[x] = conflicts; + conflicted[x] = 0; + almost_conflicted[x] = 0; +#ifdef ANTI_EXPLORATION + uint32_t age = conflicts - canceled[var(p)]; + if (age > 0){ + double decay = pow(0.95, age); + activity_CHB[var(p)] *= decay; + if (order_heap_CHB.inHeap(var(p))) + order_heap_CHB.increase(var(p)); + } +#endif + } + + assigns[x] = lbool(!sign(p)); + vardata[x] = mkVarData(from, level); + trail.push_(p); +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +inline CRef Solver::propagate() +{ + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watches_bin.cleanAll(); + + while (qhead < trail.size()){ + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + int currLevel = level(var(p)); + vec& ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + vec& ws_bin = watches_bin[p]; // Propagate binary clauses first. + for (int k = 0; k < ws_bin.size(); k++){ + Lit the_other = ws_bin[k].blocker; + if (value(the_other) == l_False){ + confl = ws_bin[k].cref; +#ifdef LOOSE_PROP_STAT + return confl; +#else + goto ExitProp; +#endif + }else if(value(the_other) == l_Undef) + { + uncheckedEnqueue(the_other, currLevel, ws_bin[k].cref); +#ifdef PRINT_OUT + std::cout << "i " << the_other << " l " << currLevel << "\n"; +#endif + } + } + + for (i = j = (Watcher*)ws, end = i + ws.size(); i != end;){ + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if (value(blocker) == l_True){ + *j++ = *i++; continue; } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause& c = ca[cr]; + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if (first != blocker && value(first) == l_True){ + *j++ = w; continue; } + + // Look for new watch: + for (int k = 2; k < c.size(); k++) + if (value(c[k]) != l_False){ + c[1] = c[k]; c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if (value(first) == l_False){ + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + }else + { + if (currLevel == decisionLevel()) + { + uncheckedEnqueue(first, currLevel, cr); +#ifdef PRINT_OUT + std::cout << "i " << first << " l " << currLevel << "\n"; +#endif + } + else + { + int nMaxLevel = currLevel; + int nMaxInd = 1; + // pass over all the literals in the clause and find the one with the biggest level + for (int nInd = 2; nInd < c.size(); ++nInd) + { + int nLevel = level(var(c[nInd])); + if (nLevel > nMaxLevel) + { + nMaxLevel = nLevel; + nMaxInd = nInd; + } + } + + if (nMaxInd != 1) + { + std::swap(c[1], c[nMaxInd]); + *j--; // undo last watch + watches[~c[1]].push(w); + } + + uncheckedEnqueue(first, nMaxLevel, cr); +#ifdef PRINT_OUT + std::cout << "i " << first << " l " << nMaxLevel << "\n"; +#endif + } + } + +NextClause:; + } + ws.shrink(i - j); + } + +ExitProp:; + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ +struct reduceDB_lt { + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} + bool operator () (CRef x, CRef y) const { return ca[x].activity() < ca[y].activity(); } +}; +inline void Solver::reduceDB() +{ + int i, j; + //if (local_learnts_dirty) cleanLearnts(learnts_local, LOCAL); + //local_learnts_dirty = false; + + sort(learnts_local, reduceDB_lt(ca)); + + int limit = learnts_local.size() / 2; + for (i = j = 0; i < learnts_local.size(); i++){ + Clause& c = ca[learnts_local[i]]; + if (c.mark() == LOCAL) + if (c.removable() && !locked(c) && i < limit) + removeClause(learnts_local[i]); + else{ + if (!c.removable()) limit++; + c.removable(true); + learnts_local[j++] = learnts_local[i]; } + } + learnts_local.shrink(i - j); + + checkGarbage(); +} +inline void Solver::reduceDB_Tier2() +{ + int i, j; + for (i = j = 0; i < learnts_tier2.size(); i++){ + Clause& c = ca[learnts_tier2[i]]; + if (c.mark() == TIER2) + if (!locked(c) && c.touched() + 30000 < conflicts){ + learnts_local.push(learnts_tier2[i]); + c.mark(LOCAL); + //c.removable(true); + c.activity() = 0; + claBumpActivity(c); + }else + learnts_tier2[j++] = learnts_tier2[i]; + } + learnts_tier2.shrink(i - j); +} + + +inline void Solver::removeSatisfied(vec& cs) +{ + int i, j; + for (i = j = 0; i < cs.size(); i++){ + Clause& c = ca[cs[i]]; + if (satisfied(c)) + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + +inline void Solver::safeRemoveSatisfied(vec& cs, unsigned valid_mark) +{ + int i, j; + for (i = j = 0; i < cs.size(); i++){ + Clause& c = ca[cs[i]]; + if (c.mark() == valid_mark) + if (satisfied(c)) + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + +inline void Solver::rebuildOrderHeap() +{ + vec vs; + for (Var v = 0; v < nVars(); v++) + if (decision[v] && value(v) == l_Undef) + vs.push(v); + + order_heap_CHB .build(vs); + order_heap_VSIDS.build(vs); + order_heap_distance.build(vs); +} + + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +inline bool Solver::simplify() +{ + assert(decisionLevel() == 0); + + if (!ok || propagate() != CRef_Undef) + return ok = false; + + if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + removeSatisfied(learnts_core); // Should clean core first. + safeRemoveSatisfied(learnts_tier2, TIER2); + safeRemoveSatisfied(learnts_local, LOCAL); + if (remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + checkGarbage(); + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = clauses_literals + learnts_literals; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + +// pathCs[k] is the number of variables assigned at level k, +// it is initialized to 0 at the begining and reset to 0 after the function execution +inline bool Solver::collectFirstUIP(CRef confl){ + involved_lits.clear(); + int max_level=1; + Clause& c=ca[confl]; int minLevel=decisionLevel(); + for(int i=0; i0) { + seen[v]=1; + var_iLevel_tmp[v]=1; + pathCs[level(v)]++; + if (minLevel>level(v)) { + minLevel=level(v); + assert(minLevel>0); + } + // varBumpActivity(v); + } + } + + int limit=trail_lim[minLevel-1]; + for(int i=trail.size()-1; i>=limit; i--) { + Lit p=trail[i]; Var v=var(p); + if (seen[v]) { + int currentDecLevel=level(v); + // if (currentDecLevel==decisionLevel()) + // varBumpActivity(v); + seen[v]=0; + if (--pathCs[currentDecLevel]!=0) { + Clause& rc=ca[reason(v)]; + int reasonVarLevel=var_iLevel_tmp[v]+1; + if(reasonVarLevel>max_level) max_level=reasonVarLevel; + if (rc.size()==2 && value(rc[0])==l_False) { + // Special case for binary clauses + // The first one has to be SAT + assert(value(rc[1]) != l_False); + Lit tmp = rc[0]; + rc[0] = rc[1], rc[1] = tmp; + } + for (int j = 1; j < rc.size(); j++){ + Lit q = rc[j]; Var v1=var(q); + if (level(v1) > 0) { + if (minLevel>level(v1)) { + minLevel=level(v1); limit=trail_lim[minLevel-1]; assert(minLevel>0); + } + if (seen[v1]) { + if (var_iLevel_tmp[v1] level_incs; level_incs.clear(); + for(int i=0;i1e100){ + for(int vv=0;vv& var_iLevel; + bool operator () (Lit x, Lit y) const + { + return var_iLevel[var(x)] < var_iLevel[var(y)] || + (var_iLevel[var(x)]==var_iLevel[var(y)]&& solver.level(var(x))>solver.level(var(y))); + } + UIPOrderByILevel_Lt(const vec& iLevel, Solver& para_solver) : solver(para_solver), var_iLevel(iLevel) { } +}; + +inline CRef Solver::propagateLits(vec& lits) { + Lit lit; + int i; + + for(i=lits.size()-1; i>=0; i--) { + lit=lits[i]; + if (value(lit) == l_Undef) { + newDecisionLevel(); + uncheckedEnqueue(lit); + CRef confl = propagate(); + if (confl != CRef_Undef) { + return confl; + } + } + } + return CRef_Undef; +} +/*_________________________________________________________________________________________________ +| +| search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +inline lbool Solver::search(int& nof_conflicts) +{ + assert(ok); + int backtrack_level; + int lbd; + vec learnt_clause; + bool cached = false; + starts++; + + // simplify + // + if (conflicts >= curSimplify * nbconfbeforesimplify){ + // printf("c ### simplifyAll on conflict : %lld\n", conflicts); + //printf("nbClauses: %d, nbLearnts_core: %d, nbLearnts_tier2: %d, nbLearnts_local: %d, nbLearnts: %d\n", + // clauses.size(), learnts_core.size(), learnts_tier2.size(), learnts_local.size(), + // learnts_core.size() + learnts_tier2.size() + learnts_local.size()); + nbSimplifyAll++; + if (!simplifyAll()){ + return l_False; + } + curSimplify = (conflicts / nbconfbeforesimplify) + 1; + nbconfbeforesimplify += incSimplify; + } + + for (;;){ + CRef confl = propagate(); + + if (confl != CRef_Undef){ + // CONFLICT + if (VSIDS){ + if (--timer == 0 && var_decay < 0.95) timer = 5000, var_decay += 0.01; + }else + if (step_size > min_step_size) step_size -= step_size_dec; + + conflicts++; nof_conflicts--; + if (conflicts == 100000 && learnts_core.size() < 100) core_lbd_cut = 5; + ConflictData data = FindConflictLevel(confl); + if (data.nHighestLevel == 0) return l_False; + if (data.bOnlyOneLitFromHighest) + { + cancelUntil(data.nHighestLevel - 1); + continue; + } + + learnt_clause.clear(); + if(conflicts>50000) DISTANCE=0; + else DISTANCE=1; + if(VSIDS && DISTANCE) + collectFirstUIP(confl); + + analyze(confl, learnt_clause, backtrack_level, lbd); + // check chrono backtrack condition + if ((confl_to_chrono < 0 || confl_to_chrono <= conflicts) && chrono > -1 && (decisionLevel() - backtrack_level) >= chrono) + { + ++chrono_backtrack; + cancelUntil(data.nHighestLevel -1); + } + else // default behavior + { + ++non_chrono_backtrack; + cancelUntil(backtrack_level); + } + + lbd--; + if (VSIDS){ + cached = false; + conflicts_VSIDS++; + lbd_queue.push(lbd); + global_lbd_sum += (lbd > 50 ? 50 : lbd); } + + if (learnt_clause.size() == 1){ + uncheckedEnqueue(learnt_clause[0]); + }else{ + CRef cr = ca.alloc(learnt_clause, true); + ca[cr].set_lbd(lbd); + if (lbd <= core_lbd_cut){ + learnts_core.push(cr); + ca[cr].mark(CORE); + }else if (lbd <= 6){ + learnts_tier2.push(cr); + ca[cr].mark(TIER2); + ca[cr].touched() = conflicts; + }else{ + learnts_local.push(cr); + claBumpActivity(ca[cr]); } + attachClause(cr); + + uncheckedEnqueue(learnt_clause[0], backtrack_level, cr); +#ifdef PRINT_OUT + std::cout << "new " << ca[cr] << "\n"; + std::cout << "ci " << learnt_clause[0] << " l " << backtrack_level << "\n"; +#endif + } + if (drup_file){ +#ifdef BIN_DRUP + binDRUP('a', learnt_clause, drup_file); +#else + for (int i = 0; i < learnt_clause.size(); i++) + fprintf(drup_file, "%i ", (var(learnt_clause[i]) + 1) * (-2 * sign(learnt_clause[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + } + + if (VSIDS) varDecayActivity(); + claDecayActivity(); + + /*if (--learntsize_adjust_cnt == 0){ + learntsize_adjust_confl *= learntsize_adjust_inc; + learntsize_adjust_cnt = (int)learntsize_adjust_confl; + max_learnts *= learntsize_inc; + + if (verbosity >= 1) + printf("c | %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", + (int)conflicts, + (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, + (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100); + }*/ + + }else{ + // NO CONFLICT + bool restart = false; + if (!VSIDS) + restart = nof_conflicts <= 0; + else if (!cached){ + restart = lbd_queue.full() && (lbd_queue.avg() * 0.8 > global_lbd_sum / conflicts_VSIDS); + cached = true; + } + if (restart /*|| !withinBudget()*/){ + lbd_queue.clear(); + cached = false; + // Reached bound on number of conflicts: + progress_estimate = progressEstimate(); + cancelUntil(0); + return l_Undef; } + + // Simplify the set of problem clauses: + if (decisionLevel() == 0 && !simplify()) + return l_False; + + if (conflicts >= next_T2_reduce){ + next_T2_reduce = conflicts + 10000; + reduceDB_Tier2(); } + if (conflicts >= next_L_reduce){ + next_L_reduce = conflicts + 15000; + reduceDB(); } + + Lit next = lit_Undef; + while (decisionLevel() < assumptions.size()){ + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if (value(p) == l_True){ + // Dummy decision level: + newDecisionLevel(); + }else if (value(p) == l_False){ + analyzeFinal(~p, conflict); + return l_False; + }else{ + next = p; + break; + } + } + + if (next == lit_Undef){ + // New variable decision: + decisions++; + next = pickBranchLit(); + + if (next == lit_Undef) + // Model found: + return l_True; + } + + // Increase decision level and enqueue 'next' + newDecisionLevel(); + uncheckedEnqueue(next, decisionLevel()); +#ifdef PRINT_OUT + std::cout << "d " << next << " l " << decisionLevel() << "\n"; +#endif + } + } +} + + +inline double Solver::progressEstimate() const +{ + double progress = 0; + double F = 1.0 / nVars(); + + for (int i = 0; i <= decisionLevel(); i++){ + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + +/* + Finite subsequences of the Luby-sequence: + + 0: 1 + 1: 1 1 2 + 2: 1 1 2 1 1 2 4 + 3: 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8 + ... + + + */ + +static double luby(double y, int x){ + + // Find the finite subsequence that contains index 'x', and the + // size of that subsequence: + int size, seq; + for (size = 1, seq = 0; size < x+1; seq++, size = 2*size+1); + + while (size-1 != x){ + size = (size-1)>>1; + seq--; + x = x % size; + } + + return pow(y, seq); +} + +static bool switch_mode = false; +static void SIGALRM_switch(int signum) { switch_mode = true; } + +// NOTE: assumptions passed in member-variable 'assumptions'. +inline lbool Solver::solve_() +{ + ::signal(SIGALRM, SIGALRM_switch); + alarm(2500); + + model.clear(); + conflict.clear(); + if (!ok) return l_False; + + solves++; + + max_learnts = nClauses() * learntsize_factor; + learntsize_adjust_confl = learntsize_adjust_start_confl; + learntsize_adjust_cnt = (int)learntsize_adjust_confl; + lbool status = l_Undef; + + if (verbosity >= 1){ + printf("c ============================[ Search Statistics ]==============================\n"); + printf("c | Conflicts | ORIGINAL | LEARNT | Progress |\n"); + printf("c | | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n"); + printf("c ===============================================================================\n"); + } + + add_tmp.clear(); + + VSIDS = true; + int init = 10000; + while (status == l_Undef && init > 0 /*&& withinBudget()*/) + status = search(init); + VSIDS = false; + + // Search: + int curr_restarts = 0; + while (status == l_Undef /*&& withinBudget()*/){ + if (VSIDS){ + int weighted = INT32_MAX; + status = search(weighted); + }else{ + int nof_conflicts = luby(restart_inc, curr_restarts) * restart_first; + curr_restarts++; + status = search(nof_conflicts); + } + if (!VSIDS && switch_mode){ + VSIDS = true; + printf("c Switched to VSIDS.\n"); + fflush(stdout); + picked.clear(); + conflicted.clear(); + almost_conflicted.clear(); +#ifdef ANTI_EXPLORATION + canceled.clear(); +#endif + } + } + + if (verbosity >= 1) + printf("c ===============================================================================\n"); + +#ifdef BIN_DRUP + if (drup_file && status == l_False) binDRUP_flush(drup_file); +#endif + + if (status == l_True){ + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + }else if (status == l_False && conflict.size() == 0) + ok = false; + + cancelUntil(0); + return status; +} + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec& map, Var& max) +{ + if (map.size() <= x || map[x] == -1){ + map.growTo(x+1, -1); + map[x] = max++; + } + return map[x]; +} + + +inline void Solver::toDimacs(FILE* f, Clause& c, vec& map, Var& max) +{ + if (satisfied(c)) return; + + for (int i = 0; i < c.size(); i++) + if (value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max)+1); + fprintf(f, "0\n"); +} + + +inline void Solver::toDimacs(const char *file, const vec& assumps) +{ + FILE* f = fopen(file, "wr"); + if (f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + + +inline void Solver::toDimacs(FILE* f, const vec& assumps) +{ + // Handle case when solver is in contradictory state: + if (!ok){ + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; } + + vec map; Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int cnt = 0; + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])) + cnt++; + + for (int i = 0; i < clauses.size(); i++) + if (!satisfied(ca[clauses[i]])){ + Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumptions.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for (int i = 0; i < assumptions.size(); i++){ + assert(value(assumptions[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumptions[i]) ? "-" : "", mapVar(var(assumptions[i]), map, max)+1); + } + + for (int i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + if (verbosity > 0) + printf("c Wrote %d clauses with %d variables.\n", cnt, max); +} + + +//================================================================================================= +// Garbage Collection methods: + +inline void Solver::relocAll(ClauseAllocator& to) +{ + // All watchers: + // + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watches_bin.cleanAll(); + for (int v = 0; v < nVars(); v++) + for (int s = 0; s < 2; s++){ + Lit p = mkLit(v, s); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec& ws = watches[p]; + for (int j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec& ws_bin = watches_bin[p]; + for (int j = 0; j < ws_bin.size(); j++) + ca.reloc(ws_bin[j].cref, to); + } + + // All reasons: + // + for (int i = 0; i < trail.size(); i++){ + Var v = var(trail[i]); + + if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + for (int i = 0; i < learnts_core.size(); i++) + ca.reloc(learnts_core[i], to); + for (int i = 0; i < learnts_tier2.size(); i++) + ca.reloc(learnts_tier2[i], to); + for (int i = 0; i < learnts_local.size(); i++) + ca.reloc(learnts_local[i], to); + + // All original: + // + int i, j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() != 1){ + ca.reloc(clauses[i], to); + clauses[j++] = clauses[i]; } + clauses.shrink(i - j); +} + + +inline void Solver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + relocAll(to); + if (verbosity >= 2) + printf("c | Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} +} +/************************************************************************************[SimpSolver.h] +MiniSat -- Copyright (c) 2006, Niklas Een, Niklas Sorensson + Copyright (c) 2007-2010, Niklas Sorensson + +Chanseok Oh's MiniSat Patch Series -- Copyright (c) 2015, Chanseok Oh + +Maple_LCM, Based on MapleCOMSPS_DRUP -- Copyright (c) 2017, Mao Luo, Chu-Min LI, Fan Xiao: implementing a learnt clause minimisation approach +Reference: M. Luo, C.-M. Li, F. Xiao, F. Manya, and Z. L. , “An effective learnt clause minimization approach for cdcl sat solvers,” in IJCAI-2017, 2017, pp. to–appear. + +Maple_LCM_Dist, Based on Maple_LCM -- Copyright (c) 2017, Fan Xiao, Chu-Min LI, Mao Luo: using a new branching heuristic called Distance at the beginning of search + + +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. +**************************************************************************************************/ + +#ifndef Minisat_SimpSolver_h +#define Minisat_SimpSolver_h + + + + + +namespace Maple { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + bool eliminate_ (); + void removeSatisfied(); + + // Memory managment: + // + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + bool parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + bool use_simplification; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr); + bool strengthenClause (CRef cr, Lit l); + bool implied (const vec& c); + void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +//================================================================================================= +} + +#endif +/***********************************************************************************[SimpSolver.cc] +MiniSat -- Copyright (c) 2006, Niklas Een, Niklas Sorensson + Copyright (c) 2007-2010, Niklas Sorensson + +Chanseok Oh's MiniSat Patch Series -- Copyright (c) 2015, Chanseok Oh + +Maple_LCM, Based on MapleCOMSPS_DRUP -- Copyright (c) 2017, Mao Luo, Chu-Min LI, Fan Xiao: implementing a learnt clause minimisation approach +Reference: M. Luo, C.-M. Li, F. Xiao, F. Manya, and Z. L. , “An effective learnt clause minimization approach for cdcl sat solvers,” in IJCAI-2017, 2017, pp. to–appear. + +Maple_LCM_Dist, Based on Maple_LCM -- Copyright (c) 2017, Fan Xiao, Chu-Min LI, Mao Luo: using a new branching heuristic called Distance at the beginning of search + + +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. +**************************************************************************************************/ + + + + + +namespace Maple { + +//================================================================================================= +// Options: + + +static const char* _simp_cat = "SIMP"; + +static BoolOption opt_use_asymm (_simp_cat, "asymm", "Shrink clauses by asymmetric branching.", false); +static BoolOption opt_use_rcheck (_simp_cat, "rcheck", "Check if a clause is already implied. (costly)", false); +static BoolOption opt_use_elim (_simp_cat, "elim", "Perform variable elimination.", true); +static IntOption opt_grow (_simp_cat, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); +static IntOption opt_clause_lim (_simp_cat, "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX)); +static IntOption opt_subsumption_lim (_simp_cat, "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX)); +static DoubleOption opt_simp_garbage_frac(_simp_cat, "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL, false)); + + +//================================================================================================= +// Constructor/Destructor: + + +inline SimpSolver::SimpSolver() : + parsing (false) + , grow (opt_grow) + , clause_lim (opt_clause_lim) + , subsumption_lim (opt_subsumption_lim) + , simp_garbage_frac (opt_simp_garbage_frac) + , use_asymm (opt_use_asymm) + , use_rcheck (opt_use_rcheck) + , use_elim (opt_use_elim) + , merges (0) + , asymm_lits (0) + , eliminated_vars (0) + , elimorder (1) + , use_simplification (true) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (0) + , n_touched (0) +{ + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + + +inline SimpSolver::~SimpSolver() +{ +} + + +inline Var SimpSolver::newVar(bool sign, bool dvar) { + Var v = Solver::newVar(sign, dvar); + + frozen .push((char)false); + eliminated.push((char)false); + + if (use_simplification){ + n_occ .push(0); + n_occ .push(0); + occurs .init(v); + touched .push(0); + elim_heap .insert(v); + } + return v; } + + + +inline lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + + do_simp &= use_simplification; + + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("c ===============================================================================\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + return result; +} + + + +inline bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if (!parsing && drup_file) { +#ifdef BIN_DRUP + binDRUP('a', ps, drup_file); +#else + for (int i = 0; i < ps.size(); i++) + fprintf(drup_file, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + } + + if (use_simplification && clauses.size() == nclauses + 1){ + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++){ + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + + +inline void SimpSolver::removeClause(CRef cr) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++){ + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr); +} + + +inline bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (drup_file){ +#ifdef BIN_DRUP + binDRUP_strengthen(c, l, drup_file); +#else + for (int i = 0; i < c.size(); i++) + if (c[i] != l) fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + } + + if (c.size() == 2){ + removeClause(cr); + c.strengthen(l); + }else{ + if (drup_file){ +#ifdef BIN_DRUP + binDRUP('d', c, drup_file); +#else + fprintf(drup_file, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(drup_file, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(drup_file, "0\n"); +#endif + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + for (int i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) + if (ps[j] == ~qs[i]) + return false; + else + goto next; + out_clause.push(qs[i]); + } + next:; + } + + for (int i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +// Returns FALSE if clause is always satisfied. +inline bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*)ps; + const Lit* __qs = (const Lit*)qs; + + size = ps.size()-1; + + for (int i = 0; i < qs.size(); i++){ + if (var(__qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + size++; + } + next:; + } + + return true; +} + + +inline void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) return; + + int i,j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0){ + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + + +inline bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True){ + cancelUntil(0); + return true; + }else if (value(c[i]) != l_False){ + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + + +// Backward subsumption + backward subsumption resolution +inline bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt){ + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); } + + CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("c subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*)_cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){ + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error){ + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +inline bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v){ + if (value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + }else + l = c[i]; + + if (propagate() != CRef_Undef){ + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + }else + cancelUntil(0); + + return true; +} + + +inline bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++){ + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + + + +inline bool SimpSolver::eliminateVar(Var v) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (int i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && + (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses: + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()){ + for (int i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + }else{ + for (int i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + } + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (int i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + watches_bin[ mkLit(v)].clear(true); + watches_bin[~mkLit(v)].clear(true); + watches[ mkLit(v)].clear(true); + watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + + +inline bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++){ + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++){ + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + } + + return true; +} + + +inline void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + for (i = elimclauses.size()-1; i > 0; i -= j){ + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + +// Almost duplicate of Solver::removeSatisfied. Didn't want to make the base method 'virtual'. +inline void SimpSolver::removeSatisfied() +{ + int i, j; + for (i = j = 0; i < clauses.size(); i++){ + const Clause& c = ca[clauses[i]]; + if (c.mark() == 0) + if (satisfied(c)) + removeClause(clauses[i]); + else + clauses[j++] = clauses[i]; + } + clauses.shrink(i - j); +} + +// The technique and code are by the courtesy of the GlueMiniSat team. Thank you! +// It helps solving certain types of huge problems tremendously. +inline bool SimpSolver::eliminate(bool turn_off_elim) +{ + bool res = true; + int iter = 0; + int n_cls, n_cls_init, n_vars; + + if (nVars() == 0) goto cleanup; // User disabling preprocessing. + + // Get an initial number of clauses (more accurately). + if (trail.size() != 0) removeSatisfied(); + n_cls_init = nClauses(); + + res = eliminate_(); // The first, usual variable elimination of MiniSat. + if (!res) goto cleanup; + + n_cls = nClauses(); + n_vars = nFreeVars(); + + // printf("c Reduced to %d vars, %d cls (grow=%d)\n", n_vars, n_cls, grow); + + if ((double)n_cls / n_vars >= 10 || n_vars < 10000){ + // printf("c No iterative elimination performed. (vars=%d, c/v ratio=%.1f)\n", n_vars, (double)n_cls / n_vars); + goto cleanup; } + + grow = grow ? grow * 2 : 8; + for (; grow < 10000; grow *= 2){ + // Rebuild elimination variable heap. + for (int i = 0; i < clauses.size(); i++){ + const Clause& c = ca[clauses[i]]; + for (int j = 0; j < c.size(); j++) + if (!elim_heap.inHeap(var(c[j]))) + elim_heap.insert(var(c[j])); + else + elim_heap.update(var(c[j])); } + + int n_cls_last = nClauses(); + int n_vars_last = nFreeVars(); + + res = eliminate_(); + if (!res || n_vars_last == nFreeVars()) break; + iter++; + + int n_cls_now = nClauses(); + int n_vars_now = nFreeVars(); + + double cl_inc_rate = (double)n_cls_now / n_cls_last; + double var_dec_rate = (double)n_vars_last / n_vars_now; + + printf("c Reduced to %d vars, %d cls (grow=%d)\n", n_vars_now, n_cls_now, grow); + printf("c cl_inc_rate=%.3f, var_dec_rate=%.3f\n", cl_inc_rate, var_dec_rate); + + if (n_cls_now > n_cls_init || cl_inc_rate > var_dec_rate) break; + } + printf("c No. effective iterative eliminations: %d\n", iter); + +cleanup: + touched .clear(true); + occurs .clear(true); + n_occ .clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + + return res; +} + + +inline bool SimpSolver::eliminate_() +{ + if (!simplify()) + return false; + else if (!use_simplification) + return true; + + int trail_size_last = trail.size(); + + // Main simplification loop: + // + while (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0){ + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && + !backwardSubsumptionCheck(true)){ + ok = false; goto cleanup; } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt){ + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++){ + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) break; + + if (isEliminated(elim) || value(elim) != l_Undef) continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("c elimination left: %10d\r", elim_heap.size()); + + if (use_asymm){ + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim]; + frozen[elim] = true; + if (!asymmVar(elim)){ + ok = false; goto cleanup; } + frozen[elim] = was_frozen; } + + // At this point, the variable may have been set by assymetric branching, so check it + // again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){ + ok = false; goto cleanup; } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } + cleanup: + // To get an accurate number of clauses. + if (trail_size_last != trail.size()) + removeSatisfied(); + else{ + int i,j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); + } + checkGarbage(); + + if (verbosity >= 1 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + + return ok; +} + + +//================================================================================================= +// Garbage Collection methods: + + +inline void SimpSolver::relocAll(ClauseAllocator& to) +{ + if (!use_simplification) return; + + // All occurs lists: + // + occurs.cleanAll(); + for (int i = 0; i < nVars(); i++){ + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (int i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + + +inline void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("c | Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} +} + +#undef ANTI_EXPLORATION +#undef BIN_DRUP +#undef INT_QUEUE_AVG +#undef LOOSE_PROP_STAT +#undef LOCAL +#undef TIER2 +#undef COR diff --git a/lib/bill/bill/sat/tseytin.hpp b/lib/bill/bill/sat/tseytin.hpp new file mode 100644 index 0000000..c94d229 --- /dev/null +++ b/lib/bill/bill/sat/tseytin.hpp @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "interface/types.hpp" +#include + +namespace bill { + +/*! \brief Adds CNF clauses for `y = (a and b)` to the solver. + * + * \param solver Solver + * \param a Literal + * \param b Literal + * \return Literal y + */ +template +lit_type add_tseytin_and(Solver& solver, lit_type const& a, lit_type const& b) +{ + auto const r = solver.add_variable(); + solver.add_clause(std::vector{~a, ~b, lit_type(r, lit_type::polarities::positive)}); + solver.add_clause(std::vector{a, lit_type(r, lit_type::polarities::negative)}); + solver.add_clause(std::vector{b, lit_type(r, lit_type::polarities::negative)}); + return lit_type(r, lit_type::polarities::positive); +} + +/*! \brief Adds CNF clauses for `y = (l_0 and ... and l_{n-1})` to the solver. + * + * \param solver Solver + * \param ls List of literals + * \return Literal y + */ +template +lit_type add_tseytin_and(Solver& solver, std::vector const& ls) +{ + auto const r = solver.add_variable(); + std::vector cls; + for (const auto& l : ls) + cls.emplace_back(~l); + cls.emplace_back(lit_type(r, lit_type::polarities::positive)); + solver.add_clause(cls); + for (const auto& l : ls) + solver.add_clause(std::vector{l, lit_type(r, lit_type::polarities::negative)}); + return lit_type(r, lit_type::polarities::positive); +} + +/*! \brief Adds CNF clauses for `y = a or b` to the solver. + * + * \param solver Solver + * \param a Literal + * \param b Literal + * \return Literal y + */ +template +lit_type add_tseytin_or(Solver& solver, lit_type const& a, lit_type const& b) +{ + auto const r = solver.add_variable(); + solver.add_clause(std::vector{a, b, lit_type(r, lit_type::polarities::negative)}); + solver.add_clause(std::vector{~a, lit_type(r, lit_type::polarities::positive)}); + solver.add_clause(std::vector{~b, lit_type(r, lit_type::polarities::positive)}); + return lit_type(r, lit_type::polarities::positive); +} + +/*! \brief Adds CNF clauses for `y = (l_0 or ... or l_{n-1})` to the solver. + * + * \param solver Solver + * \param ls List of literals + * \return Literal y + */ +template +lit_type add_tseytin_or(Solver& solver, std::vector const& ls) +{ + auto const r = solver.add_variable(); + std::vector cls(ls); + cls.emplace_back(lit_type(r, lit_type::polarities::negative)); + solver.add_clause(cls); + for (const auto& l : ls) + solver.add_clause(std::vector{~l, lit_type(r, lit_type::polarities::positive)}); + return lit_type(r, lit_type::polarities::positive); +} + +/*! \brief Adds CNF clauses for `y = (a xor b)` to the solver. + * + * \param solver Solver + * \param a Literal + * \param b Literal + * \return Literal y + */ +template +lit_type add_tseytin_xor(Solver& solver, lit_type const& a, lit_type const& b) +{ + auto const r = solver.add_variable(); + solver.add_clause(std::vector{~a, ~b, lit_type(r, lit_type::polarities::negative)}); + solver.add_clause(std::vector{~a, b, lit_type(r, lit_type::polarities::positive)}); + solver.add_clause(std::vector{a, ~b, lit_type(r, lit_type::polarities::positive)}); + solver.add_clause(std::vector{a, b, lit_type(r, lit_type::polarities::negative)}); + return lit_type(r, lit_type::polarities::positive); +} + +/*! \brief Adds CNF clauses for `y = (a == b)` to the solver. + * + * \param solver Solver + * \param a Literal + * \param b Literal + * \return Literal y + */ +template +lit_type add_tseytin_equals(Solver& solver, lit_type const& a, lit_type const& b) +{ + auto const r = solver.add_variable(); + solver.add_clause(std::vector{~a, ~b, lit_type(r, lit_type::polarities::positive)}); + solver.add_clause(std::vector{~a, b, lit_type(r, lit_type::polarities::negative)}); + solver.add_clause(std::vector{a, ~b, lit_type(r, lit_type::polarities::negative)}); + solver.add_clause(std::vector{a, b, lit_type(r, lit_type::polarities::positive)}); + return lit_type(r, lit_type::polarities::positive); +} + +} /* namespace bill */ diff --git a/lib/bill/bill/sat/unsat_cores.hpp b/lib/bill/bill/sat/unsat_cores.hpp new file mode 100644 index 0000000..a08a636 --- /dev/null +++ b/lib/bill/bill/sat/unsat_cores.hpp @@ -0,0 +1,73 @@ +#pragma once + +namespace bill { + +namespace detail { + +template +inline std::vector copy_vector_without_index(std::vector const& vs, uint32_t index) +{ + assert(index < vs.size()); + std::vector copy(vs); + copy.erase(std::begin(copy) + index); + return copy; +} + +} // namespace detail + +template +inline result::clause_type trim_core_copy(Solver& solver, result::clause_type const& core, + uint32_t num_tries = 8u) +{ + auto current = core; + + uint32_t counter = 0u; + while (counter++ < num_tries && solver.solve(current) == result::states::unsatisfiable) { + auto const new_core = solver.get_core().core(); + if (new_core.size() == current.size()) + break; + + current = new_core; + } + + return current; +} + +template +inline void trim_core(Solver& solver, result::clause_type& core, uint32_t num_tries = 0u) +{ + core = trim_core_copy(solver, core, num_tries); +} + +template +inline result::clause_type minimize_core_copy(Solver& solver, result::clause_type& core, + int64_t budget = 1000) +{ + auto pos = 0u; + auto current = core; + + while (pos < current.size()) { + auto temp = detail::copy_vector_without_index(current, pos); + + auto result = solver.solve(temp, budget); + if (result == result::states::unsatisfiable) { + current = temp; + } else { + ++pos; + } + } + + if (current.size() < core.size()) { + return current; + } else { + return core; + } +} + +template +inline void minimize_core(Solver& solver, result::clause_type& core, int64_t budget = 1000) +{ + core = minimize_core_copy(solver, core, budget); +} + +} // namespace bill diff --git a/lib/bill/bill/sat/xor_clauses.hpp b/lib/bill/bill/sat/xor_clauses.hpp new file mode 100644 index 0000000..b6794b3 --- /dev/null +++ b/lib/bill/bill/sat/xor_clauses.hpp @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#include "interface/types.hpp" + +#include +#include + +namespace bill { + +/*! \brief Adds CNF clauses for `y = ((l_0 ^ ... ^ l_{n-1}) == pol)` to the solver. + * + * \param solver Solver + * \param clause List of literals + * \param pol Clause polarity + * \return Literal y + */ +template +lit_type add_xor_clause(Solver& solver, std::vector const& clause, + lit_type::polarities pol = lit_type::polarities::positive) +{ + std::queue lits; + bool first = pol == lit_type::polarities::negative; + for (const auto& l : clause) { + if (first) { + lits.push(~l); + first = false; + } else { + lits.push(l); + } + } + + while (lits.size() > 1) { + auto const a = lits.front(); + lits.pop(); + auto const b = lits.front(); + lits.pop(); + + lits.push(add_tseytin_xor(solver, a, b)); + } + + assert(lits.size() == 1u); + return lits.front(); +} + +} /* namespace bill */ diff --git a/lib/bill/bill/utils/hash.hpp b/lib/bill/bill/utils/hash.hpp new file mode 100644 index 0000000..fa401b6 --- /dev/null +++ b/lib/bill/bill/utils/hash.hpp @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*-------------------------------------------------------------------------------------------------*/ +#pragma once + +#include +#include + + +// TODO: this is a bit of a hack! + +namespace std { + +template +inline void hash_combine(std::size_t& seed, T const& v) +{ + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +template +struct hash> { + using argument_type = set; + using result_type = size_t; + result_type operator()(argument_type const& in) const + { + result_type seed = 0; + for (auto& element : in) { + hash_combine(seed, element); + } + return seed; + } +}; + +template<> +struct hash> { + using argument_type = pair; + using result_type = size_t; + result_type operator()(argument_type const& in) const + { + result_type seed = 0; + hash_combine(seed, in.first); + hash_combine(seed, in.second); + return seed; + } +}; + +} // namespace std diff --git a/lib/bill/bill/utils/platforms.hpp b/lib/bill/bill/utils/platforms.hpp new file mode 100644 index 0000000..2c31736 --- /dev/null +++ b/lib/bill/bill/utils/platforms.hpp @@ -0,0 +1,9 @@ +/*------------------------------------------------------------------------------------------------- +| This file is distributed under the MIT License. +| See accompanying file /LICENSE for details. +*------------------------------------------------------------------------------------------------*/ +#pragma once + +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +#define BILL_WINDOWS_PLATFORM +#endif diff --git a/lib/fmt/LICENSE.rst b/lib/fmt/LICENSE.rst new file mode 100644 index 0000000..eb6be65 --- /dev/null +++ b/lib/fmt/LICENSE.rst @@ -0,0 +1,23 @@ +Copyright (c) 2012 - 2016, Victor Zverovich + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/fmt/fmt/chrono.h b/lib/fmt/fmt/chrono.h new file mode 100644 index 0000000..421d464 --- /dev/null +++ b/lib/fmt/fmt/chrono.h @@ -0,0 +1,1119 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include +#include + +#include "format.h" +#include "locale.h" + +FMT_BEGIN_NAMESPACE + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (F::digits <= T::digits) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < T::min() || from > T::max()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (F::is_signed && !T::is_signed) { + // From may be negative, not allowed! + if (fmt::internal::is_negative(from)) { + ec = 1; + return {}; + } + + // From is positive. Can it always fit in To? + if (F::digits <= T::digits) { + // yes, From always fits in To. + } else { + // from may not fit in To, we have to do a dynamic check + if (from > static_cast(T::max())) { + ec = 1; + return {}; + } + } + } + + if (!F::is_signed && T::is_signed) { + // can from be held in To? + if (F::digits < T::digits) { + // yes, From always fits in To. + } else { + // from may not fit in To, we have to do a dynamic check + if (from > static_cast(T::max())) { + // outside range. + ec = 1; + return {}; + } + } + } + + // reaching here means all is ok for lossless conversion. + return static_cast(from); + +} // function + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= T::max()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) { + return {}; + } + // multiply with Factor::num without overflow or underflow + if (Factor::num != 1) { + const auto max1 = internal::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = std::numeric_limits::min() / Factor::num; + if (count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + // this can't go wrong, right? den>0 is checked earlier. + if (Factor::den != 1) { + count /= Factor::den; + } + // convert to the to type, safely + using ToRep = typename To::rep; + const ToRep tocount = lossless_integral_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (Factor::num != 1) { + constexpr auto max1 = internal::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (Factor::den != 1) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace internal { +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } +} // namespace internal + +// Thread-safe replacement for std::localtime +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::internal; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(internal::null<>) { + using namespace fmt::internal; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(internal::null<>) { + using namespace fmt::internal; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +// Thread-safe replacement for std::gmtime +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::internal; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(internal::null<>) { + using namespace fmt::internal; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(internal::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +namespace internal { +inline std::size_t strftime(char* str, std::size_t count, const char* format, + const std::tm* time) { + return std::strftime(str, count, format, time); +} + +inline std::size_t strftime(wchar_t* str, std::size_t count, + const wchar_t* format, const std::tm* time) { + return std::wcsftime(str, count, format, time); +} +} // namespace internal + +template struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + tm_format.reserve(internal::to_unsigned(end - it + 1)); + tm_format.append(it, end); + tm_format.push_back('\0'); + return end; + } + + template + auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { + basic_memory_buffer buf; + std::size_t start = buf.size(); + for (;;) { + std::size_t size = buf.capacity() - start; + std::size_t count = + internal::strftime(&buf[start], size, &tm_format[0], &tm); + if (count != 0) { + buf.resize(start + count); + break; + } + if (size >= tm_format.size() * 256) { + // If the buffer is 256 times larger than the format string, assume + // that `strftime` gives an empty result. There doesn't seem to be a + // better way to distinguish the two cases: + // https://github.com/fmtlib/fmt/issues/367 + break; + } + const std::size_t MIN_GROWTH = 10; + buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); + } + return std::copy(buf.begin(), buf.end(), ctx.out()); + } + + basic_memory_buffer tm_format; +}; + +namespace internal { +template FMT_CONSTEXPR const char* get_units() { + return nullptr; +} +template <> FMT_CONSTEXPR const char* get_units() { return "as"; } +template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } +template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } +template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } +template <> FMT_CONSTEXPR const char* get_units() { return "das"; } +template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } +template <> FMT_CONSTEXPR const char* get_units>() { + return "m"; +} +template <> FMT_CONSTEXPR const char* get_units>() { + return "h"; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +struct chrono_format_checker { + FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } + + template void on_text(const Char*, const Char*) {} + FMT_NORETURN void on_abbr_weekday() { report_no_date(); } + FMT_NORETURN void on_full_weekday() { report_no_date(); } + FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_abbr_month() { report_no_date(); } + FMT_NORETURN void on_full_month() { report_no_date(); } + void on_24_hour(numeric_system) {} + void on_12_hour(numeric_system) {} + void on_minute(numeric_system) {} + void on_second(numeric_system) {} + FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } + FMT_NORETURN void on_us_date() { report_no_date(); } + FMT_NORETURN void on_iso_date() { report_no_date(); } + void on_12_hour_time() {} + void on_24_hour_time() {} + void on_iso_time() {} + void on_am_pm() {} + void on_duration_value() {} + void on_duration_unit() {} + FMT_NORETURN void on_utc_offset() { report_no_date(); } + FMT_NORETURN void on_tz_name() { report_no_date(); } +}; + +template ::value)> +inline bool isnan(T) { + return false; +} +template ::value)> +inline bool isnan(T value) { + return std::isnan(value); +} + +template ::value)> +inline bool isfinite(T) { + return true; +} +template ::value)> +inline bool isfinite(T value) { + return std::isfinite(value); +} + +// Converts value to int and checks that it's in the range [0, upper). +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT( + std::isnan(value) || (value >= 0 && value <= static_cast(upper)), + "invalid value"); + (void)upper; + return static_cast(value); +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + using common_type = typename std::common_type::type; + auto ms = mod(d.count() * static_cast(Period::num) / + static_cast(Period::den) * 1000, + 1000); + return std::chrono::duration(static_cast(ms)); +} + +template +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; + if (precision >= 0) return format_to(out, pr_f, val, precision); + const Char fp_f[] = {'{', ':', 'g', '}', 0}; + const Char format[] = {'{', '}', 0}; + return format_to(out, std::is_floating_point::value ? fp_f : format, + val); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) { + string_view s(unit); + if (const_check(std::is_same())) { + utf8_to_utf16 u(s); + return std::copy(u.c_str(), u.c_str() + u.size(), out); + } + return std::copy(s.begin(), s.end(), out); + } + const Char num_f[] = {'[', '{', '}', ']', 's', 0}; + if (Period::den == 1) return format_to(out, num_f, Period::num); + const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; + return format_to(out, num_def_f, Period::num, Period::den); +} + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + + explicit chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = internal::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal(out, n, num_digits); + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + void format_localized(const tm& time, char format, char modifier = 0) { + if (isnan(val)) return write_nan(); + auto locale = context.locale().template get(); + auto& facet = std::use_facet>(locale); + std::basic_ostringstream os; + os.imbue(locale); + facet.put(os, os, ' ', &time, format, modifier); + auto str = os.str(); + std::copy(str.begin(), str.end(), out); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_localized(time, 'H', 'O'); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_localized(time, 'I', 'O'); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_localized(time, 'M', 'O'); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + write(second(), 2); +#if FMT_SAFE_DURATION_CAST + // convert rep->Rep + using duration_rep = std::chrono::duration; + using duration_Rep = std::chrono::duration; + auto tmpval = fmt_safe_duration_cast(duration_rep{val}); +#else + auto tmpval = std::chrono::duration(val); +#endif + auto ms = get_milliseconds(tmpval); + if (ms != std::chrono::milliseconds(0)) { + *out++ = '.'; + write(ms.count(), 3); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_localized(time, 'S', 'O'); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_localized(time(), 'r'); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + write(second(), 2); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_localized(time(), 'p'); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; +} // namespace internal + +template +struct formatter, Char> { + private: + basic_format_specs specs; + int precision; + using arg_ref_type = internal::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + mutable basic_string_view format_str; + using duration = std::chrono::duration; + + struct spec_handler { + formatter& f; + basic_format_parse_context& context; + basic_string_view format_str; + + template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + void on_fill(basic_string_view fill) { f.specs.fill = fill; } + void on_align(align_t align) { f.specs.align = align; } + void on_width(int width) { f.specs.width = width; } + void on_precision(int _precision) { f.precision = _precision; } + void end_precision() {} + + template void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = internal::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = internal::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point::value) + begin = internal::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + end = parse_chrono_format(begin, end, internal::chrono_format_checker()); + return {begin, end}; + } + + public: + formatter() : precision(-1) {} + + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, internal::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + using range = internal::output_range; + internal::basic_writer w(range(ctx.out())); + internal::handle_dynamic_spec(specs.width, + width_ref, ctx); + internal::handle_dynamic_spec( + precision, precision_ref, ctx); + if (begin == end || *begin == '}') { + out = internal::format_duration_value(out, d.count(), precision); + internal::format_duration_unit(out); + } else { + internal::chrono_formatter f( + ctx, out, d); + f.precision = precision; + parse_chrono_format(begin, end, f); + } + w.write(buf.data(), buf.size(), specs); + return w.out(); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/lib/fmt/fmt/color.h b/lib/fmt/fmt/color.h new file mode 100644 index 0000000..96d9ab6 --- /dev/null +++ b/lib/fmt/fmt/color.h @@ -0,0 +1,568 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + italic = 1 << 1, + underline = 1 << 2, + strikethrough = 1 << 3 +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +namespace internal { + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), + value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), + value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; +} // namespace internal + +// Experimental text formatting support. +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + background_color.value.rgb_color &= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator&(text_style lhs, + const text_style& rhs) { + return lhs &= rhs; + } + + FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + internal::color_type text_color) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) + FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background) + FMT_NOEXCEPT; + + internal::color_type foreground_color; + internal::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/true, foreground); +} + +FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/false, background); +} + +FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { + return text_style(lhs) | rhs; +} + +namespace internal { + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, + const char* esc) FMT_NOEXCEPT { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == internal::data::background_color; + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + std::size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + uint8_t em_codes[4] = {}; + uint8_t em_bits = static_cast(em); + if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; + if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; + if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; + if (em_bits & static_cast(emphasis::strikethrough)) + em_codes[3] = 9; + + std::size_t index = 0; + for (int i = 0; i < 4; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } + + FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { + return buffer + std::char_traits::length(buffer); + } + + private: + Char buffer[7u + 3u * 4u + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) FMT_NOEXCEPT { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } +}; + +template +FMT_CONSTEXPR ansi_color_escape make_foreground_color( + internal::color_type foreground) FMT_NOEXCEPT { + return ansi_color_escape(foreground, internal::data::foreground_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_background_color( + internal::color_type background) FMT_NOEXCEPT { + return ansi_color_escape(background, internal::data::background_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { + return ansi_color_escape(em); +} + +template +inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { + std::fputs(chars, stream); +} + +template <> +inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { + std::fputws(chars, stream); +} + +template inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(internal::data::reset_color, stream); +} + +template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(internal::data::wreset_color, stream); +} + +template +inline void reset_color(basic_memory_buffer& buffer) FMT_NOEXCEPT { + const char* begin = data::reset_color; + const char* end = begin + sizeof(data::reset_color) - 1; + buffer.append(begin, end); +} + +template +void vformat_to(basic_memory_buffer& buf, const text_style& ts, + basic_string_view format_str, + basic_format_args> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = internal::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + internal::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = + internal::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + internal::vformat_to(buf, format_str, args); + if (has_style) internal::reset_color(buf); +} +} // namespace internal + +template > +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args> args) { + basic_memory_buffer buf; + internal::vformat_to(buf, ts, to_string_view(format), args); + buf.push_back(Char(0)); + internal::fputs(buf.data(), f); +} + +/** + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + Example: + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + */ +template ::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + internal::check_format_string(format_str); + using context = buffer_context>; + format_arg_store as{args...}; + vprint(f, ts, format_str, basic_format_args(as)); +} + +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + Example: + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + */ +template ::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template > +inline std::basic_string vformat( + const text_style& ts, const S& format_str, + basic_format_args>> args) { + basic_memory_buffer buf; + internal::vformat_to(buf, ts, to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template > +inline std::basic_string format(const text_style& ts, const S& format_str, + const Args&... args) { + return vformat(ts, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); +} + +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/lib/fmt/fmt/compile.h b/lib/fmt/fmt/compile.h new file mode 100644 index 0000000..e4b12f3 --- /dev/null +++ b/lib/fmt/fmt/compile.h @@ -0,0 +1,595 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace internal { + +// Part of a compiled format string. It can be either literal text or a +// replacement field. +template struct format_part { + enum class kind { arg_index, arg_name, text, replacement }; + + struct replacement { + arg_ref arg_id; + dynamic_format_specs specs; + }; + + kind part_kind; + union value { + int arg_index; + basic_string_view str; + replacement repl; + + FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} + FMT_CONSTEXPR value(basic_string_view s) : str(s) {} + FMT_CONSTEXPR value(replacement r) : repl(r) {} + } val; + // Position past the end of the argument id. + const Char* arg_id_end = nullptr; + + FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) + : part_kind(k), val(v) {} + + static FMT_CONSTEXPR format_part make_arg_index(int index) { + return format_part(kind::arg_index, index); + } + static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { + return format_part(kind::arg_name, name); + } + static FMT_CONSTEXPR format_part make_text(basic_string_view text) { + return format_part(kind::text, text); + } + static FMT_CONSTEXPR format_part make_replacement(replacement repl) { + return format_part(kind::replacement, repl); + } +}; + +template struct part_counter { + unsigned num_parts = 0; + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) ++num_parts; + } + + FMT_CONSTEXPR void on_arg_id() { ++num_parts; } + FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; } + FMT_CONSTEXPR void on_arg_id(basic_string_view) { ++num_parts; } + + FMT_CONSTEXPR void on_replacement_field(const Char*) {} + + FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, + const Char* end) { + // Find the matching brace. + unsigned brace_counter = 0; + for (; begin != end; ++begin) { + if (*begin == '{') { + ++brace_counter; + } else if (*begin == '}') { + if (brace_counter == 0u) break; + --brace_counter; + } + } + return begin; + } + + FMT_CONSTEXPR void on_error(const char*) {} +}; + +// Counts the number of parts in a format string. +template +FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { + part_counter counter; + parse_format_string(format_str, counter); + return counter.num_parts; +} + +template +class format_string_compiler : public error_handler { + private: + using part = format_part; + + PartHandler handler_; + part part_; + basic_string_view format_str_; + basic_format_parse_context parse_context_; + + public: + FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, + PartHandler handler) + : handler_(handler), + format_str_(format_str), + parse_context_(format_str) {} + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) + handler_(part::make_text({begin, to_unsigned(end - begin)})); + } + + FMT_CONSTEXPR void on_arg_id() { + part_ = part::make_arg_index(parse_context_.next_arg_id()); + } + + FMT_CONSTEXPR void on_arg_id(int id) { + parse_context_.check_arg_id(id); + part_ = part::make_arg_index(id); + } + + FMT_CONSTEXPR void on_arg_id(basic_string_view id) { + part_ = part::make_arg_name(id); + } + + FMT_CONSTEXPR void on_replacement_field(const Char* ptr) { + part_.arg_id_end = ptr; + handler_(part_); + } + + FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, + const Char* end) { + auto repl = typename part::replacement(); + dynamic_specs_handler> handler( + repl.specs, parse_context_); + auto it = parse_format_specs(begin, end, handler); + if (*it != '}') on_error("missing '}' in format string"); + repl.arg_id = part_.part_kind == part::kind::arg_index + ? arg_ref(part_.val.arg_index) + : arg_ref(part_.val.str); + auto part = part::make_replacement(repl); + part.arg_id_end = begin; + handler_(part); + return it; + } +}; + +// Compiles a format string and invokes handler(part) for each parsed part. +template +FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, + PartHandler handler) { + parse_format_string( + format_str, + format_string_compiler(format_str, handler)); +} + +template +void format_arg( + basic_format_parse_context& parse_ctx, + Context& ctx, Id arg_id) { + ctx.advance_to( + visit_format_arg(arg_formatter(ctx, &parse_ctx), ctx.arg(arg_id))); +} + +// vformat_to is defined in a subnamespace to prevent ADL. +namespace cf { +template +auto vformat_to(Range out, CompiledFormat& cf, basic_format_args args) + -> typename Context::iterator { + using char_type = typename Context::char_type; + basic_format_parse_context parse_ctx( + to_string_view(cf.format_str_)); + Context ctx(out.begin(), args); + + const auto& parts = cf.parts(); + for (auto part_it = std::begin(parts); part_it != std::end(parts); + ++part_it) { + const auto& part = *part_it; + const auto& value = part.val; + + using format_part_t = format_part; + switch (part.part_kind) { + case format_part_t::kind::text: { + const auto text = value.str; + auto output = ctx.out(); + auto&& it = reserve(output, text.size()); + it = std::copy_n(text.begin(), text.size(), it); + ctx.advance_to(output); + break; + } + + case format_part_t::kind::arg_index: + advance_to(parse_ctx, part.arg_id_end); + internal::format_arg(parse_ctx, ctx, value.arg_index); + break; + + case format_part_t::kind::arg_name: + advance_to(parse_ctx, part.arg_id_end); + internal::format_arg(parse_ctx, ctx, value.str); + break; + + case format_part_t::kind::replacement: { + const auto& arg_id_value = value.repl.arg_id.val; + const auto arg = value.repl.arg_id.kind == arg_id_kind::index + ? ctx.arg(arg_id_value.index) + : ctx.arg(arg_id_value.name); + + auto specs = value.repl.specs; + + handle_dynamic_spec(specs.width, specs.width_ref, ctx); + handle_dynamic_spec(specs.precision, + specs.precision_ref, ctx); + + error_handler h; + numeric_specs_checker checker(h, arg.type()); + if (specs.align == align::numeric) checker.require_numeric_argument(); + if (specs.sign != sign::none) checker.check_sign(); + if (specs.alt) checker.require_numeric_argument(); + if (specs.precision >= 0) checker.check_precision(); + + advance_to(parse_ctx, part.arg_id_end); + ctx.advance_to( + visit_format_arg(arg_formatter(ctx, nullptr, &specs), arg)); + break; + } + } + } + return ctx.out(); +} +} // namespace cf + +struct basic_compiled_format {}; + +template +struct compiled_format_base : basic_compiled_format { + using char_type = char_t; + using parts_container = std::vector>; + + parts_container compiled_parts; + + explicit compiled_format_base(basic_string_view format_str) { + compile_format_string(format_str, + [this](const format_part& part) { + compiled_parts.push_back(part); + }); + } + + const parts_container& parts() const { return compiled_parts; } +}; + +template struct format_part_array { + format_part data[N] = {}; + FMT_CONSTEXPR format_part_array() = default; +}; + +template +FMT_CONSTEXPR format_part_array compile_to_parts( + basic_string_view format_str) { + format_part_array parts; + unsigned counter = 0; + // This is not a lambda for compatibility with older compilers. + struct { + format_part* parts; + unsigned* counter; + FMT_CONSTEXPR void operator()(const format_part& part) { + parts[(*counter)++] = part; + } + } collector{parts.data, &counter}; + compile_format_string(format_str, collector); + if (counter < N) { + parts.data[counter] = + format_part::make_text(basic_string_view()); + } + return parts; +} + +template constexpr const T& constexpr_max(const T& a, const T& b) { + return (a < b) ? b : a; +} + +template +struct compiled_format_base::value>> + : basic_compiled_format { + using char_type = char_t; + + FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} + +// Workaround for old compilers. Format string compilation will not be +// performed there anyway. +#if FMT_USE_CONSTEXPR + static FMT_CONSTEXPR_DECL const unsigned num_format_parts = + constexpr_max(count_parts(to_string_view(S())), 1u); +#else + static const unsigned num_format_parts = 1; +#endif + + using parts_container = format_part[num_format_parts]; + + const parts_container& parts() const { + static FMT_CONSTEXPR_DECL const auto compiled_parts = + compile_to_parts( + internal::to_string_view(S())); + return compiled_parts.data; + } +}; + +template +class compiled_format : private compiled_format_base { + public: + using typename compiled_format_base::char_type; + + private: + basic_string_view format_str_; + + template + friend auto cf::vformat_to(Range out, CompiledFormat& cf, + basic_format_args args) -> + typename Context::iterator; + + public: + compiled_format() = delete; + explicit constexpr compiled_format(basic_string_view format_str) + : compiled_format_base(format_str), format_str_(format_str) {} +}; + +#ifdef __cpp_if_constexpr +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get(const T& first, const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return get(rest...); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&...) const { + // TODO: reserve + return copy_str(data.begin(), data.end(), out); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template , int> = 0> +OutputIt format_default(OutputIt out, T value) { + // TODO: reserve + format_int fi(value); + return std::copy(fi.data(), fi.data() + fi.size(), out); +} + +template +OutputIt format_default(OutputIt out, double value) { + writer w(out); + w.write(value); + return w.out(); +} + +template +OutputIt format_default(OutputIt out, Char value) { + *out++ = value; + return out; +} + +template +OutputIt format_default(OutputIt out, const Char* value) { + auto length = std::char_traits::length(value); + return copy_str(value, value + length, out); +} + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&... args) const { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + return format_default(out, arg); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != to_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr basic_string_view str = format_str; + if constexpr (str[POS] == '{') { + if (POS + 1 == str.size()) + throw format_error("unmatched '{' in format string"); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}') { + using type = get_type; + if constexpr (std::is_same::value) { + return parse_tail(field(), + format_str); + } else { + return unknown_format(); + } + } else { + return unknown_format(); + } + } else if constexpr (str[POS] == '}') { + if (POS + 1 == str.size()) + throw format_error("unmatched '}' in format string"); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + return parse_tail(make_text(str, POS, end - POS), + format_str); + } +} +#endif // __cpp_if_constexpr +} // namespace internal + +#if FMT_USE_CONSTEXPR +# ifdef __cpp_if_constexpr +template ::value)> +constexpr auto compile(S format_str) { + constexpr basic_string_view str = format_str; + if constexpr (str.size() == 0) { + return internal::make_text(str, 0, 0); + } else { + constexpr auto result = + internal::compile_format_string, 0, 0>( + format_str); + if constexpr (std::is_same, + internal::unknown_format>()) { + return internal::compiled_format(to_string_view(format_str)); + } else { + return result; + } + } +} + +template ::value)> +std::basic_string format(const CompiledFormat& cf, const Args&... args) { + basic_memory_buffer buffer; + cf.format(std::back_inserter(buffer), args...); + return to_string(buffer); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} +# else +template ::value)> +constexpr auto compile(S format_str) -> internal::compiled_format { + return internal::compiled_format(to_string_view(format_str)); +} +# endif // __cpp_if_constexpr +#endif // FMT_USE_CONSTEXPR + +// Compiles the format string which must be a string literal. +template +auto compile(const Char (&format_str)[N]) + -> internal::compiled_format { + return internal::compiled_format( + basic_string_view(format_str, N - 1)); +} + +template ::value)> +std::basic_string format(const CompiledFormat& cf, const Args&... args) { + basic_memory_buffer buffer; + using range = buffer_range; + using context = buffer_context; + internal::cf::vformat_to(range(buffer), cf, + make_format_args(args...)); + return to_string(buffer); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + using char_type = typename CompiledFormat::char_type; + using range = internal::output_range; + using context = format_context_t; + return internal::cf::vformat_to(range(out), cf, + make_format_args(args...)); +} + +template ::value)> +format_to_n_result format_to_n(OutputIt out, size_t n, + const CompiledFormat& cf, + const Args&... args) { + auto it = + format_to(internal::truncating_iterator(out, n), cf, args...); + return {it.base(), it.count()}; +} + +template +std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { + return format_to(internal::counting_iterator(), cf, args...).count(); +} + +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/lib/fmt/fmt/core.h b/lib/fmt/fmt/core.h new file mode 100644 index 0000000..6df2875 --- /dev/null +++ b/lib/fmt/fmt/core.h @@ -0,0 +1,1789 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::FILE +#include +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 60200 + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if defined(__has_include) && !defined(__INTELLISENSE__) && \ + !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#ifdef __clang__ +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +# define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef __NVCC__ +# define FMT_NVCC __NVCC__ +#else +# define FMT_NVCC 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +#else +# define FMT_MSC_VER 0 +#endif + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +# if FMT_HAS_FEATURE(cxx_override) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_OVERRIDE override +# else +# define FMT_OVERRIDE +# endif +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if defined(__GNUC__) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + FMT_MSC_VER >= 1900 +# define FMT_INLINE_NAMESPACE inline namespace +# define FMT_END_NAMESPACE \ + } \ + } +# else +# define FMT_INLINE_NAMESPACE namespace +# define FMT_END_NAMESPACE \ + } \ + using namespace v6; \ + } +# endif +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v6 { +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# if FMT_MSC_VER +# define FMT_NO_W4275 __pragma(warning(suppress : 4275)) +# else +# define FMT_NO_W4275 +# endif +# define FMT_CLASS_API FMT_NO_W4275 +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# endif +#endif +#ifndef FMT_CLASS_API +# define FMT_CLASS_API +#endif +#ifndef FMT_API +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_API __attribute__((visibility("default"))) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# define FMT_INSTANTIATION_DEF_API +# else +# define FMT_API +# endif +#endif +#ifndef FMT_EXTERN_TEMPLATE_API +# define FMT_EXTERN_TEMPLATE_API +#endif +#ifndef FMT_INSTANTIATION_DEF_API +# define FMT_INSTANTIATION_DEF_API FMT_API +#endif + +#ifndef FMT_HEADER_ONLY +# define FMT_EXTERN extern +#else +# define FMT_EXTERN +#endif + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VER +#endif +#if FMT_UNICODE && FMT_MSC_VER +# pragma execution_character_set("utf-8") +#endif + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; + +struct monostate {}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 + +namespace internal { + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template FMT_CONSTEXPR T const_check(T value) { return value; } + +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Werror=empty-body. +# define FMT_ASSERT(condition, message) ((void)0) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC +# define FMT_USE_INT128 1 +using int128_t = __int128_t; +using uint128_t = __uint128_t; +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +struct int128_t {}; +struct uint128_t {}; +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +constexpr unsigned char micro[] = "\u00B5"; + +template constexpr bool is_unicode() { + return FMT_UNICODE || sizeof(Char) != 1 || + (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif +} // namespace internal + +template +using void_t = typename internal::void_t_impl::type; + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using char_type FMT_DEPRECATED_ALIAS = Char; + using value_type = Char; + using iterator = const Char*; + + FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + FMT_CONSTEXPR basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif + basic_string_view(const Char* s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} + + template < + typename S, + FMT_ENABLE_IF(std::is_same>::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} + + /** Returns a pointer to the string data. */ + FMT_CONSTEXPR const Char* data() const { return data_; } + + /** Returns the string size. */ + FMT_CONSTEXPR size_t size() const { return size_; } + + FMT_CONSTEXPR iterator begin() const { return data_; } + FMT_CONSTEXPR iterator end() const { return data_ + size_; } + + FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; } + + FMT_CONSTEXPR void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; +using wstring_view = basic_string_view; + +#ifndef __cpp_char8_t +// char8_t is deprecated; use char instead. +using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type; +#endif + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; + +/** + \rst + Returns a string view of `s`. In order to add custom string type support to + {fmt} provide an overload of `to_string_view` for it in the same namespace as + the type for the argument-dependent lookup to work. + + **Example**:: + + namespace my_ns { + inline string_view to_string_view(const my_string& s) { + return {s.data(), s.length()}; + } + } + std::string message = fmt::format(my_string("The answer is {}"), 42); + \endrst + */ +template ::value)> +inline basic_string_view to_string_view(const Char* s) { + return s; +} + +template +inline basic_string_view to_string_view( + const std::basic_string& s) { + return s; +} + +template +inline basic_string_view to_string_view(basic_string_view s) { + return s; +} + +template >::value)> +inline basic_string_view to_string_view( + internal::std_string_view s) { + return s; +} + +// A base class for compile-time strings. It is defined in the fmt namespace to +// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42). +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +template ::value)> +constexpr basic_string_view to_string_view(const S& s) { + return s; +} + +namespace internal { +void to_string_view(...); +using fmt::v6::to_string_view; + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +struct error_handler { + FMT_CONSTEXPR error_handler() = default; + FMT_CONSTEXPR error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN FMT_API void on_error(const char* message); +}; +} // namespace internal + +/** String's character type. */ +template using char_t = typename internal::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + + You can use one of the following type aliases for common character types: + + +-----------------------+-------------------------------------+ + | Type | Definition | + +=======================+=====================================+ + | format_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + | wformat_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit FMT_CONSTEXPR basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { + return format_str_.begin(); + } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(internal::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR int next_arg_id() { + if (next_arg_id_ >= 0) return next_arg_id_++; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int) { + if (next_arg_id_ > 0) + on_error("cannot switch from automatic to manual argument indexing"); + else + next_arg_id_ = -1; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +template +using basic_parse_context FMT_DEPRECATED_ALIAS = + basic_format_parse_context; +using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context; +using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context; + +template class basic_format_arg; +template class basic_format_args; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +template +struct FMT_DEPRECATED convert_to_int + : bool_constant::value && + std::is_convertible::value> {}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +namespace internal { + +/** A contiguous memory buffer with an optional growing ability. */ +template class buffer { + private: + T* ptr_; + std::size_t size_; + std::size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + buffer(std::size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + + buffer(T* p = nullptr, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT + : ptr_(p), + size_(sz), + capacity_(cap) {} + + /** Sets the buffer data and capacity. */ + void set(T* buf_data, std::size_t buf_capacity) FMT_NOEXCEPT { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(std::size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + virtual ~buffer() = default; + + T* begin() FMT_NOEXCEPT { return ptr_; } + T* end() FMT_NOEXCEPT { return ptr_ + size_; } + + const T* begin() const FMT_NOEXCEPT { return ptr_; } + const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + std::size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + std::size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T* data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T* data() const FMT_NOEXCEPT { return ptr_; } + + /** + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ + void resize(std::size_t new_size) { + reserve(new_size); + size_ = new_size; + } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + /** Reserves space to store at least *capacity* elements. */ + void reserve(std::size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + void push_back(const T& value) { + reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template T& operator[](I index) { return ptr_[index]; } + template const T& operator[](I index) const { + return ptr_[index]; + } +}; + +// A container-backed buffer. +template +class container_buffer : public buffer { + private: + Container& container_; + + protected: + void grow(std::size_t capacity) FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit container_buffer(Container& c) + : buffer(c.size()), container_(c) {} +}; + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container& get_container(std::back_insert_iterator it) { + using bi_iterator = std::back_insert_iterator; + struct accessor : bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = + std::is_constructible>; + +template struct named_arg_base; +template struct named_arg; + +enum class type { + none_type, + named_arg_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(const named_arg_base&, named_arg_type); +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +FMT_CONSTEXPR bool is_integral_type(type t) { + FMT_ASSERT(t != type::named_arg_type, "invalid argument type"); + return t > type::none_type && t <= type::last_integer_type; +} + +FMT_CONSTEXPR bool is_arithmetic_type(type t) { + FMT_ASSERT(t != type::named_arg_type, "invalid argument type"); + return t > type::none_type && t <= type::last_numeric_type; +} + +template struct string_value { + const Char* data; + std::size_t size; +}; + +template struct custom_value { + using parse_context = basic_format_parse_context; + const void* value; + void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_t int128_value; + uint128_t uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + const named_arg_base* named_arg; + }; + + FMT_CONSTEXPR value(int val = 0) : int_value(val) {} + FMT_CONSTEXPR value(unsigned val) : uint_value(val) {} + value(long long val) : long_long_value(val) {} + value(unsigned long long val) : ulong_long_value(val) {} + value(int128_t val) : int128_value(val) {} + value(uint128_t val) : uint128_value(val) {} + value(float val) : float_value(val) {} + value(double val) : double_value(val) {} + value(long double val) : long_double_value(val) {} + value(bool val) : bool_value(val) {} + value(char_type val) : char_value(val) {} + value(const char_type* val) { string.data = val; } + value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + value(const void* val) : pointer(val) {} + + template value(const T& val) { + custom.value = &val; + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + T, conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + + value(const named_arg_base& val) { named_arg = &val; } + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg( + const void* arg, basic_format_parse_context& parse_ctx, + Context& ctx) { + Formatter f; + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value); + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +// Maps formatting arguments to core types. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR int map(signed char val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } + FMT_CONSTEXPR int map(short val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } + FMT_CONSTEXPR int map(int val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned val) { return val; } + FMT_CONSTEXPR long_type map(long val) { return val; } + FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } + FMT_CONSTEXPR long long map(long long val) { return val; } + FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } + FMT_CONSTEXPR int128_t map(int128_t val) { return val; } + FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } + FMT_CONSTEXPR bool map(bool val) { return val; } + + template ::value)> + FMT_CONSTEXPR char_type map(T val) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + return val; + } + + FMT_CONSTEXPR float map(float val) { return val; } + FMT_CONSTEXPR double map(double val) { return val; } + FMT_CONSTEXPR long double map(long double val) { return val; } + + FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } + FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } + template ::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + static_assert(std::is_same>::value, + "mixing character types is disallowed"); + return to_string_view(val); + } + template , T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return basic_string_view(val); + } + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return std_string_view(val); + } + FMT_CONSTEXPR const char* map(const signed char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(const unsigned char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + + FMT_CONSTEXPR const void* map(void* val) { return val; } + FMT_CONSTEXPR const void* map(const void* val) { return val; } + FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } + template FMT_CONSTEXPR int map(const T*) { + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); + return 0; + } + + template ::value && + !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { + return map(static_cast::type>(val)); + } + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR const T& map(const T& val) { + return val; + } + + template + FMT_CONSTEXPR const named_arg_base& map( + const named_arg& val) { + auto arg = make_arg(val.value); + std::memcpy(val.data, &arg, sizeof(arg)); + return val; + } + + int map(...) { + constexpr bool formattable = sizeof(Context) == 0; + static_assert( + formattable, + "Cannot format argument. To make type T formattable provide a " + "formatter specialization: " + "https://fmt.dev/latest/api.html#formatting-user-defined-types"); + return 0; + } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 5 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 63 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; + +template class arg_map; +} // namespace internal + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + internal::value value_; + internal::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg internal::make_arg( + const T& value); + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class internal::arg_map; + + using char_type = typename Context::char_type; + + public: + class handle { + public: + explicit handle(internal::custom_value custom) : custom_(custom) {} + + void format(basic_format_parse_context& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + internal::custom_value custom_; + }; + + FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {} + + FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT { + return type_ != internal::type::none_type; + } + + internal::type type() const { return type_; } + + bool is_integral() const { return internal::is_integral_type(type_); } + bool is_arithmetic() const { return internal::is_arithmetic_type(type_); } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)) { + using char_type = typename Context::char_type; + switch (arg.type_) { + case internal::type::none_type: + break; + case internal::type::named_arg_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case internal::type::int_type: + return vis(arg.value_.int_value); + case internal::type::uint_type: + return vis(arg.value_.uint_value); + case internal::type::long_long_type: + return vis(arg.value_.long_long_value); + case internal::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); +#if FMT_USE_INT128 + case internal::type::int128_type: + return vis(arg.value_.int128_value); + case internal::type::uint128_type: + return vis(arg.value_.uint128_value); +#else + case internal::type::int128_type: + case internal::type::uint128_type: + break; +#endif + case internal::type::bool_type: + return vis(arg.value_.bool_value); + case internal::type::char_type: + return vis(arg.value_.char_value); + case internal::type::float_type: + return vis(arg.value_.float_value); + case internal::type::double_type: + return vis(arg.value_.double_value); + case internal::type::long_double_type: + return vis(arg.value_.long_double_value); + case internal::type::cstring_type: + return vis(arg.value_.string.data); + case internal::type::string_type: + return vis(basic_string_view(arg.value_.string.data, + arg.value_.string.size)); + case internal::type::pointer_type: + return vis(arg.value_.pointer); + case internal::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +namespace internal { +// A map from argument names to their values for named arguments. +template class arg_map { + private: + using char_type = typename Context::char_type; + + struct entry { + basic_string_view name; + basic_format_arg arg; + }; + + entry* map_; + unsigned size_; + + void push_back(value val) { + const auto& named = *val.named_arg; + map_[size_] = {named.name, named.template deserialize()}; + ++size_; + } + + public: + arg_map(const arg_map&) = delete; + void operator=(const arg_map&) = delete; + arg_map() : map_(nullptr), size_(0) {} + void init(const basic_format_args& args); + ~arg_map() { delete[] map_; } + + basic_format_arg find(basic_string_view name) const { + // The list is unsorted, so just return the first matching name. + for (entry *it = map_, *end = map_ + size_; it != end; ++it) { + if (it->name == name) return it->arg; + } + return {}; + } +}; + +// A type-erased reference to an std::locale to avoid heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + + template Locale get() const; +}; + +template constexpr unsigned long long encode_types() { return 0; } + +template +constexpr unsigned long long encode_types() { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +template +inline value make_arg(const T& val) { + return arg_mapper().map(val); +} + +template +inline basic_format_arg make_arg(const T& value) { + return make_arg(value); +} + +template struct is_reference_wrapper : std::false_type {}; + +template +struct is_reference_wrapper> : std::true_type {}; + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto node = std::unique_ptr>(new typed_node(arg)); + auto& value = node->value; + node->next = std::move(head_); + head_ = std::move(node); + return value; + } +}; +} // namespace internal + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + internal::arg_map map_; + internal::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + template using formatter_type = formatter; + + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, + basic_format_args ctx_args, + internal::locale_ref loc = internal::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + format_arg arg(int id) const { return args_.get(id); } + + // Checks if manual indexing is used and returns the argument with the + // specified name. + format_arg arg(basic_string_view name); + + internal::error_handler error_handler() { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + iterator out() { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { out_ = it; } + + internal::locale_ref locale() { return loc_; } +}; + +template +using buffer_context = + basic_format_context>, + Char>; +using format_context = buffer_context; +using wformat_context = buffer_context; + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const bool is_packed = num_args < internal::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + // If the arguments are not packed, add one more element to mark the end. + value_type data_[num_args + (num_args == 0 ? 1 : 0)]; + + friend class basic_format_args; + + public: + static constexpr unsigned long long types = + is_packed ? internal::encode_types() + : internal::is_unpacked_bit | num_args; + + format_arg_store(const Args&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{internal::make_arg(args)...} { + } +}; + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +inline format_arg_store make_format_args( + const Args&... args) { + return {args...}; +} + +/** + \rst + A dynamic version of `fmt::format_arg_store<>`. + It's equipped with a storage to potentially temporary objects which lifetime + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr internal::type mapped_type = + internal::mapped_type_constant::value; + + enum { + value = !(internal::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != internal::type::cstring_type && + mapped_type != internal::type::string_type && + mapped_type != internal::type::custom_type && + mapped_type != internal::type::named_arg_type)) + }; + }; + + template + using stored_type = conditional_t::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + internal::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return internal::is_unpacked_bit | data_.size(); + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(internal::make_arg(arg)); + } + + public: + /** + \rst + Adds an argument into the dynamic store for later passing to a formating + function. + + Note that custom types and string types (but not string views!) are copied + into the store with dynamic memory (in addition to resizing vector). + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + static_assert( + !std::is_base_of, T>::value, + "named arguments are not supported yet"); + if (internal::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(arg); + } + + /** + Adds a reference to the argument into the dynamic store for later passing to + a formating function. + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } +}; + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // To reduce compiled code size per formatting function call, types of first + // max_packed_args arguments are passed in the types_ field. + unsigned long long types_; + union { + // If the number of arguments is less than max_packed_args, the argument + // values are stored in values_, otherwise they are stored in args_. + // This is done to reduce compiled code size as storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const internal::value* values_; + const format_arg* args_; + }; + + bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; } + + internal::type type(int index) const { + int shift = index * internal::packed_arg_bits; + unsigned int mask = (1 << internal::packed_arg_bits) - 1; + return static_cast((types_ >> shift) & mask); + } + + friend class internal::arg_map; + + void set_data(const internal::value* values) { values_ = values; } + void set_data(const format_arg* args) { args_ = args; } + + format_arg do_get(int index) const { + format_arg arg; + if (!is_packed()) { + auto num_args = max_size(); + if (index < num_args) arg = args_[index]; + return arg; + } + if (index > internal::max_packed_args) return arg; + arg.type_ = type(index); + if (arg.type_ == internal::type::none_type) return arg; + internal::value& val = arg.value_; + val = values_[index]; + return arg; + } + + public: + basic_format_args() : types_(0) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + basic_format_args(const format_arg_store& store) + : types_(store.types) { + set_data(store.data_); + } + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + basic_format_args(const dynamic_format_arg_store& store) + : types_(store.get_types()) { + set_data(store.data_.data()); + } + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + basic_format_args(const format_arg* args, int count) + : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) { + set_data(args); + } + + /** Returns the argument at specified index. */ + format_arg get(int index) const { + format_arg arg = do_get(index); + if (arg.type_ == internal::type::named_arg_type) + arg = arg.value_.named_arg->template deserialize(); + return arg; + } + + int max_size() const { + unsigned long long max_packed = internal::max_packed_args; + return static_cast(is_packed() ? max_packed + : types_ & ~internal::is_unpacked_bit); + } +}; + +/** An alias to ``basic_format_args``. */ +// It is a separate type rather than an alias to make symbols readable. +struct format_args : basic_format_args { + template + format_args(Args&&... args) + : basic_format_args(static_cast(args)...) {} +}; +struct wformat_args : basic_format_args { + template + wformat_args(Args&&... args) + : basic_format_args(static_cast(args)...) {} +}; + +template struct is_contiguous : std::false_type {}; + +template +struct is_contiguous> : std::true_type {}; + +template +struct is_contiguous> : std::true_type {}; + +namespace internal { + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; + +template struct named_arg_base { + basic_string_view name; + + // Serialized value. + mutable char data[sizeof(basic_format_arg>)]; + + named_arg_base(basic_string_view nm) : name(nm) {} + + template basic_format_arg deserialize() const { + basic_format_arg arg; + std::memcpy(&arg, data, sizeof(basic_format_arg)); + return arg; + } +}; + +struct view {}; + +template +struct named_arg : view, named_arg_base { + const T& value; + + named_arg(basic_string_view name, const T& val) + : named_arg_base(name), value(val) {} +}; + +template ::value)> +inline void check_format_string(const S&) { +#if defined(FMT_ENFORCE_COMPILE_STRING) + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to " + "utilize FMT_STRING() or fmt()."); +#endif +} +template ::value)> +void check_format_string(S); + +template struct bool_pack; +template +using all_true = + std::is_same, bool_pack>; + +template > +inline format_arg_store, remove_reference_t...> +make_args_checked(const S& format_str, + const remove_reference_t&... args) { + static_assert( + all_true<(!std::is_base_of>::value || + !std::is_reference::value)...>::value, + "passing views as lvalues is disallowed"); + check_format_string(format_str); + return {args...}; +} + +template +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); + +template +typename buffer_context::iterator vformat_to( + buffer& buf, basic_string_view format_str, + basic_format_args>> args); + +template ::value)> +inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +} // namespace internal + +/** + \rst + Returns a named argument to be used in a formatting function. It should only + be used in a call to a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template > +inline internal::named_arg arg(const S& name, const T& arg) { + static_assert(internal::is_string::value, ""); + return {name, arg}; +} + +// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``. +template +void arg(S, internal::named_arg) = delete; + +/** Formats a string and writes the output to ``out``. */ +// GCC 8 and earlier cannot handle std::back_insert_iterator with +// vformat_to(...) overload, so SFINAE on iterator type instead. +template , + FMT_ENABLE_IF( + internal::is_contiguous_back_insert_iterator::value)> +OutputIt vformat_to( + OutputIt out, const S& format_str, + basic_format_args>> args) { + using container = remove_reference_t; + internal::container_buffer buf((internal::get_container(out))); + internal::vformat_to(buf, to_string_view(format_str), args); + return out; +} + +template ::value&& internal::is_string::value)> +inline std::back_insert_iterator format_to( + std::back_insert_iterator out, const S& format_str, + Args&&... args) { + return vformat_to(out, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); +} + +template > +inline std::basic_string vformat( + const S& format_str, + basic_format_args>> args) { + return internal::vformat(to_string_view(format_str), args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template > +inline std::basic_string format(const S& format_str, Args&&... args) { + return internal::vformat( + to_string_view(format_str), + internal::make_args_checked(format_str, args...)); +} + +FMT_API void vprint(string_view, format_args); +FMT_API void vprint(std::FILE*, string_view, format_args); + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes the + output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template > +inline void print(std::FILE* f, const S& format_str, Args&&... args) { + return internal::is_unicode() + ? vprint(f, to_string_view(format_str), + internal::make_args_checked(format_str, args...)) + : internal::vprint_mojibake( + f, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); +} + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes + the output to ``stdout``. Strings are assumed to be Unicode-encoded unless + the ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template > +inline void print(const S& format_str, Args&&... args) { + return internal::is_unicode() + ? vprint(to_string_view(format_str), + internal::make_args_checked(format_str, args...)) + : internal::vprint_mojibake( + stdout, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/lib/fmt/fmt/format-inl.h b/lib/fmt/fmt/format-inl.h new file mode 100644 index 0000000..f632714 --- /dev/null +++ b/lib/fmt/fmt/format-inl.h @@ -0,0 +1,1403 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include +#include +#include +#include // for std::memmove +#include + +#include "format.h" +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +# include +#endif + +#ifdef _WIN32 +# include +# include +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4702) // unreachable code +#endif + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; } +inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; } + +FMT_BEGIN_NAMESPACE +namespace internal { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + print(stderr, "{}:{}: assertion failed: {}", file, line, message); + std::abort(); +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// A portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +FMT_FUNC int safe_strerror(int error_code, char*& buffer, + std::size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); + + class dispatcher { + private: + int error_code_; + char*& buffer_; + std::size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher&) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + FMT_MAYBE_UNUSED + int handle(char* message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + FMT_MAYBE_UNUSED + int handle(internal::null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + FMT_MAYBE_UNUSED + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE + : result; + } + +#if !FMT_MSC_VER + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(internal::null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } +#endif + + public: + dispatcher(int err_code, char*& buf, std::size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +FMT_FUNC void format_error_code(internal::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (internal::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += internal::to_unsigned(internal::count_digits(abs_value)); + internal::writer w(out); + if (message.size() <= inline_buffer_size - error_code_size) { + w.write(message); + w.write(SEP); + } + w.write(ERROR_STR); + w.write(error_code); + assert(out.size() <= inline_buffer_size); +} + +FMT_FUNC void report_error(format_func func, int error_code, + string_view message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); +} +} // namespace internal + +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +namespace internal { + +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template FMT_FUNC std::string grouping_impl(locale_ref loc) { + return std::use_facet>(loc.get()).grouping(); +} +template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .thousands_sep(); +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +} // namespace internal +#else +template +FMT_FUNC std::string internal::grouping_impl(locale_ref) { + return "\03"; +} +template +FMT_FUNC Char internal::thousands_sep_impl(locale_ref) { + return FMT_STATIC_THOUSANDS_SEPARATOR; +} +template +FMT_FUNC Char internal::decimal_point_impl(locale_ref) { + return '.'; +} +#endif + +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; + +FMT_FUNC void system_error::init(int err_code, string_view format_str, + format_args args) { + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error& base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace internal { + +template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +template +const char basic_data::digits[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +template +const char basic_data::hex_digits[] = "0123456789abcdef"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +template +const uint64_t basic_data::powers_of_10_64[] = { + 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, + FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::zero_or_powers_of_10_64[] = { + 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::pow10_significands[] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, +}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::pow10_exponents[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + +template +const char basic_data::foreground_color[] = "\x1b[38;2;"; +template +const char basic_data::background_color[] = "\x1b[48;2;"; +template const char basic_data::reset_color[] = "\x1b[0m"; +template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; +template const char basic_data::signs[] = {0, '-', '+', ' '}; + +template struct bits { + static FMT_CONSTEXPR_DECL const int value = + static_cast(sizeof(T) * std::numeric_limits::digits); +}; + +class fp; +template fp normalize(fp value); + +// Lower (upper) boundary is a value half way between a floating-point value +// and its predecessor (successor). Boundaries have the same exponent as the +// value so only significands are stored. +struct boundaries { + uint64_t lower; + uint64_t upper; +}; + +// A handmade floating-point number f * pow(2, e). +class fp { + private: + using significand_type = uint64_t; + + public: + significand_type f; + int e; + + // All sizes are in bits. + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static FMT_CONSTEXPR_DECL const int double_significand_size = + std::numeric_limits::digits - 1; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = + 1ULL << double_significand_size; + static FMT_CONSTEXPR_DECL const int significand_size = + bits::value; + + fp() : f(0), e(0) {} + fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template explicit fp(Double d) { assign(d); } + + // Assigns d to this and return true iff predecessor is closer than successor. + template + bool assign(Double d) { + // Assume double is in the format [sign][exponent][significand]. + using limits = std::numeric_limits; + const int exponent_size = + bits::value - double_significand_size - 1; // -1 for sign + const uint64_t significand_mask = implicit_bit - 1; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + auto u = bit_cast(d); + f = u & significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> double_significand_size); + // Predecessor is closer if d is a normalized power of 2 (f == 0) other than + // the smallest normalized number (biased_e > 1). + bool is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e != 0) + f += implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = biased_e - exponent_bias - double_significand_size; + return is_predecessor_closer; + } + + template + bool assign(Double) { + *this = fp(); + return false; + } + + // Assigns d to this together with computing lower and upper boundaries, + // where a boundary is a value half way between the number and its predecessor + // (lower) or successor (upper). The upper boundary is normalized and lower + // has the same exponent but may be not normalized. + template boundaries assign_with_boundaries(Double d) { + bool is_lower_closer = assign(d); + fp lower = + is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1); + // 1 in normalize accounts for the exponent shift above. + fp upper = normalize<1>(fp((f << 1) + 1, e - 1)); + lower.f <<= lower.e - upper.e; + return boundaries{lower.f, upper.f}; + } + + template boundaries assign_float_with_boundaries(Double d) { + assign(d); + constexpr int min_normal_e = std::numeric_limits::min_exponent - + std::numeric_limits::digits; + significand_type half_ulp = 1 << (std::numeric_limits::digits - + std::numeric_limits::digits - 1); + if (min_normal_e > e) half_ulp <<= min_normal_e - e; + fp upper = normalize<0>(fp(f + half_ulp, e)); + fp lower = fp( + f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e); + lower.f <<= lower.e - upper.e; + return boundaries{lower.f, upper.f}; + } +}; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template fp normalize(fp value) { + // Handle subnormals. + const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::significand_size - fp::double_significand_size - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +inline fp get_cached_power(int min_exponent, int& pow10_exponent) { + const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) + int index = static_cast( + ((min_exponent + fp::significand_size - 1) * one_over_log2_10 + + ((int64_t(1) << 32) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {data::pow10_significands[index], data::pow10_exponents[index]}; +} + +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + accumulator() : lower(0), upper(0) {} + explicit operator uint32_t() const { return static_cast(lower); } + + void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + void operator>>=(int shift) { + assert(shift == 32); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } + bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } + + static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; + + friend struct formatter; + + void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) { + subtract_bigits(i, other.bigits_[j], borrow); + } + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + void multiply(uint64_t value) { + const bigit mask = ~bigit(0); + const double_bigit lower = value & mask; + const double_bigit upper = value >> bigit_bits; + double_bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * lower + (carry & mask); + carry = + bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(carry & mask); + carry >>= bigit_bits; + } + } + + public: + bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + void assign(const bigint& other) { + bigits_.resize(other.bigits_.size()); + auto data = other.bigits_.data(); + std::copy(data, data + other.bigits_.size(), bigits_.data()); + exp_ = other.exp_; + } + + void assign(uint64_t n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = n & ~bigit(0); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + int num_bigits() const { return static_cast(bigits_.size()) + exp_; } + + bigint& operator<<=(int shift) { + assert(shift >= 0); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + void assign_pow10(int exp) { + assert(exp >= 0); + if (exp == 0) return assign(1); + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + assign(5); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + void square() { + basic_memory_buffer n(std::move(bigits_)); + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + bigits_.resize(to_unsigned(num_result_bigits)); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; + } + --num_result_bigits; + remove_leading_zeros(); + exp_ *= 2; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + int num_bigits = static_cast(bigits_.size()); + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + int exp_difference = exp_ - divisor.exp_; + if (exp_difference > 0) { + // Align bigints by adding trailing zeros to simplify subtraction. + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +// A version of count_digits optimized for grisu_gen_digits. +inline int grisu_count_digits(uint32_t n) { + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +template +FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, + int& exp, Handler& handler) { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = grisu_count_digits(integral); // kappa in Grisu. + // Divide by 10 to prevent overflow. + auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, + value.f / 10, error * 10, exp); + if (result != digits::more) return result; + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + uint64_t remainder = + (static_cast(integral) << -one.e) + fractional; + result = handler.on_digit(static_cast('0' + digit), + data::powers_of_10_64[exp] << -one.e, remainder, + error, exp, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = + static_cast('0' + static_cast(fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + result = handler.on_digit(digit, one.f, fractional, error, exp, false); + if (result != digits::more) return result; + } +} + +// The fixed precision digit handler. +struct fixed_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, + int& exp) { + // Non-fixed formats require at least one digit and no precision adjustment. + if (!fixed) return digits::more; + // Adjust fixed precision by exponent because it is relative to decimal + // point. + precision += exp + exp10; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (precision > 0) return digits::more; + if (precision < 0) return digits::done; + auto dir = get_round_direction(divisor, remainder, error); + if (dir == round_direction::unknown) return digits::error; + buf[size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int, bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + buf[size++] = '0'; + } + return digits::done; + } +}; + +// The shortest representation digit handler. +struct grisu_shortest_handler { + char* buf; + int size; + // Distance between scaled value and upper bound (wp_W in Grisu3). + uint64_t diff; + + digits::result on_start(uint64_t, uint64_t, uint64_t, int&) { + return digits::more; + } + + // Decrement the generated number approaching value from above. + void round(uint64_t d, uint64_t divisor, uint64_t& remainder, + uint64_t error) { + while ( + remainder < d && error - remainder >= divisor && + (remainder + divisor < d || d - remainder >= remainder + divisor - d)) { + --buf[size - 1]; + remainder += divisor; + } + } + + // Implements Grisu's round_weed. + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int exp, bool integral) { + buf[size++] = digit; + if (remainder >= error) return digits::more; + uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp]; + uint64_t up = (diff - 1) * unit; // wp_Wup + round(up, divisor, remainder, error); + uint64_t down = (diff + 1) * unit; // wp_Wdown + if (remainder < down && error - remainder >= divisor && + (remainder + divisor < down || + down - remainder > remainder + divisor - down)) { + return digits::error; + } + return 2 * unit <= remainder && remainder <= error - 4 * unit + ? digits::done + : digits::error; + } +}; + +// Formats value using a variation of the Fixed-Precision Positive +// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/p372-steele.pdf. +template +void fallback_format(Double d, buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + fp value; + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + // TODO: handle float + int shift = value.assign(d) ? 2 : 1; + uint64_t significand = value.f << shift; + if (value.e >= 0) { + numerator.assign(significand); + numerator <<= value.e; + lower.assign(1); + lower <<= value.e; + if (shift != 1) { + upper_store.assign(1); + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= 1; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (shift != 1) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= significand; + denominator.assign(1); + denominator <<= shift - value.e; + } else { + numerator.assign(significand); + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower.assign(1); + if (shift != 1) { + upper_store.assign(1ULL << 1); + upper = &upper_store; + } + } + if (!upper) upper = &lower; + // Invariant: value == (numerator / denominator) * pow(10, exp10). + bool even = (value.f & 1) == 0; + int num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } +} + +// Formats value using the Grisu algorithm +// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) +// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. +template +int format_float(T value, int precision, float_specs specs, buffer& buf) { + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.resize(to_unsigned(precision)); + std::uninitialized_fill_n(buf.data(), precision, '0'); + return -precision; + } + + if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); + + int exp = 0; + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + if (precision < 0) { + fp fp_value; + auto boundaries = specs.binary32 + ? fp_value.assign_float_with_boundaries(value) + : fp_value.assign_with_boundaries(value); + fp_value = normalize(fp_value); + // Find a cached power of 10 such that multiplying value by it will bring + // the exponent in the range [min_exp, -32]. + const fp cached_pow = get_cached_power( + min_exp - (fp_value.e + fp::significand_size), cached_exp10); + // Multiply value and boundaries by the cached power of 10. + fp_value = fp_value * cached_pow; + boundaries.lower = multiply(boundaries.lower, cached_pow.f); + boundaries.upper = multiply(boundaries.upper, cached_pow.f); + assert(min_exp <= fp_value.e && fp_value.e <= -32); + --boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}. + ++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}. + // Numbers outside of (lower, upper) definitely do not round to value. + grisu_shortest_handler handler{buf.data(), 0, + boundaries.upper - fp_value.f}; + auto result = + grisu_gen_digits(fp(boundaries.upper, fp_value.e), + boundaries.upper - boundaries.lower, exp, handler); + if (result == digits::error) { + exp += handler.size - cached_exp10 - 1; + fallback_format(value, buf, exp); + return exp; + } + buf.resize(to_unsigned(handler.size)); + } else { + if (precision > 17) return snprintf_float(value, precision, specs, buf); + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::significand_size), cached_exp10); + normalized = normalized * cached_pow; + fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) + return snprintf_float(value, precision, specs, buf); + int num_digits = handler.size; + if (!fixed) { + // Remove trailing zeros. + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + } + buf.resize(to_unsigned(num_digits)); + } + return exp - cached_exp10; +} + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // Ths longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto becase of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially. + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + assert(sign == '+' || sign == '-'); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + assert(is_digit(*p)); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from buf, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) { + static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 3, 3, 4, 0}; + static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + static const int shiftc[] = {0, 18, 12, 6, 0}; + static const int shifte[] = {0, 6, 4, 2, 0}; + + auto s = reinterpret_cast(buf); + int len = lengths[s[0] >> 3]; + + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = buf + len + !len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} +} // namespace internal + +template <> struct formatter { + format_parse_context::iterator parse(format_parse_context& ctx) { + return ctx.begin(); + } + + format_context::iterator format(const internal::bigint& n, + format_context& ctx) { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, "{:x}", value); + first = false; + continue; + } + out = format_to(out, "{:08x}", value); + } + if (n.exp_ > 0) + out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) { + auto transcode = [this](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + memcpy(buf, p, to_unsigned(num_chars_left)); + p = buf; + do { + p = transcode(p); + } while (p - buf < num_chars_left); + } + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(internal::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + FMT_TRY { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + char* system_message = &buf[0]; + int result = + internal::safe_strerror(error_code, system_message, buf.size()); + if (result == 0) { + internal::writer w(out); + w.write(message); + w.write(": "); + w.write(system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void internal::error_handler::on_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error(int error_code, + fmt::string_view message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + internal::vformat_to(buffer, format_str, + basic_format_args>(args)); +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + auto written = DWORD(); + if (!WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), &written, + nullptr)) { + FMT_THROW(format_error("failed to write to console")); + } + return; + } +#endif + internal::fwrite_fully(buffer.data(), 1, buffer.size(), f); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + internal::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_END_NAMESPACE + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif // FMT_FORMAT_INL_H_ diff --git a/lib/fmt/fmt/format.cc b/lib/fmt/fmt/format.cc new file mode 100644 index 0000000..9a9abf8 --- /dev/null +++ b/lib/fmt/fmt/format.cc @@ -0,0 +1,176 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/format-inl.h" + +FMT_BEGIN_NAMESPACE +namespace internal { + +template +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about nonliteral format string. + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + return precision < 0 ? snprintf_ptr(buf, size, format, value) + : snprintf_ptr(buf, size, format, precision, value); +} +struct sprintf_specs { + int precision; + char type; + bool alt : 1; + + template + constexpr sprintf_specs(basic_format_specs specs) + : precision(specs.precision), type(specs.type), alt(specs.alt) {} + + constexpr bool has_precision() const { return precision >= 0; } +}; + +// This is deprecated and is kept only to preserve ABI compatibility. +template +char* sprintf_format(Double value, internal::buffer& buf, + sprintf_specs specs) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() != 0, "empty buffer"); + + // Build format string. + enum { max_format_size = 10 }; // longest format: %#-*.*Lg + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.alt || !specs.type) *format_ptr++ = '#'; + if (specs.precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same::value) *format_ptr++ = 'L'; + + char type = specs.type; + + if (type == '%') + type = 'f'; + else if (type == 0 || type == 'n') + type = 'g'; +#if FMT_MSC_VER + if (type == 'F') { + // MSVC's printf doesn't support 'F'. + type = 'f'; + } +#endif + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + char* start = nullptr; + char* decimal_point_pos = nullptr; + for (;;) { + std::size_t buffer_size = buf.capacity(); + start = &buf[0]; + int result = + format_float(start, buffer_size, format, specs.precision, value); + if (result >= 0) { + unsigned n = internal::to_unsigned(result); + if (n < buf.capacity()) { + // Find the decimal point. + auto p = buf.data(), end = p + n; + if (*p == '+' || *p == '-') ++p; + if (specs.type != 'a' && specs.type != 'A') { + while (p < end && *p >= '0' && *p <= '9') ++p; + if (p < end && *p != 'e' && *p != 'E') { + decimal_point_pos = p; + if (!specs.type) { + // Keep only one trailing zero after the decimal point. + ++p; + if (*p == '0') ++p; + while (p != end && *p >= '1' && *p <= '9') ++p; + char* where = p; + while (p != end && *p == '0') ++p; + if (p == end || *p < '0' || *p > '9') { + if (p != end) std::memmove(where, p, to_unsigned(end - p)); + n -= static_cast(p - where); + } + } + } + } + buf.resize(n); + break; // The buffer is large enough - continue with formatting. + } + buf.reserve(n + 1); + } else { + // If result is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buf.reserve(buf.capacity() + 1); + } + } + return decimal_point_pos; +} +} // namespace internal + +template FMT_API char* internal::sprintf_format(double, internal::buffer&, + sprintf_specs); +template FMT_API char* internal::sprintf_format(long double, + internal::buffer&, + sprintf_specs); + +template struct FMT_INSTANTIATION_DEF_API internal::basic_data; + +// Workaround a bug in MSVC2013 that prevents instantiation of format_float. +int (*instantiate_format_float)(double, int, internal::float_specs, + internal::buffer&) = + internal::format_float; + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API internal::locale_ref::locale_ref(const std::locale& loc); +template FMT_API std::locale internal::locale_ref::get() const; +#endif + +// Explicit instantiations for char. + +template FMT_API std::string internal::grouping_impl(locale_ref); +template FMT_API char internal::thousands_sep_impl(locale_ref); +template FMT_API char internal::decimal_point_impl(locale_ref); + +template FMT_API void internal::buffer::append(const char*, const char*); + +template FMT_API void internal::arg_map::init( + const basic_format_args& args); + +template FMT_API std::string internal::vformat( + string_view, basic_format_args); + +template FMT_API format_context::iterator internal::vformat_to( + internal::buffer&, string_view, basic_format_args); + +template FMT_API int internal::snprintf_float(double, int, + internal::float_specs, + internal::buffer&); +template FMT_API int internal::snprintf_float(long double, int, + internal::float_specs, + internal::buffer&); +template FMT_API int internal::format_float(double, int, internal::float_specs, + internal::buffer&); +template FMT_API int internal::format_float(long double, int, + internal::float_specs, + internal::buffer&); + +// Explicit instantiations for wchar_t. + +template FMT_API std::string internal::grouping_impl(locale_ref); +template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); +template FMT_API wchar_t internal::decimal_point_impl(locale_ref); + +template FMT_API void internal::buffer::append(const wchar_t*, + const wchar_t*); + +template FMT_API std::wstring internal::vformat( + wstring_view, basic_format_args); +FMT_END_NAMESPACE diff --git a/lib/fmt/fmt/format.h b/lib/fmt/fmt/format.h new file mode 100644 index 0000000..4e96539 --- /dev/null +++ b/lib/fmt/fmt/format.h @@ -0,0 +1,3648 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + 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. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#ifdef FMT_DEPRECATED_INCLUDE_OS +# include "os.h" +#endif + +#ifdef __INTEL_COMPILER +# define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +# define FMT_ICC_VERSION __ICL +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#else +# define FMT_CUDA_VERSION 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && !defined(__PGI) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define FMT_FALLTHROUGH [[fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VER || FMT_NVCC +FMT_BEGIN_NAMESPACE +namespace internal { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace internal +FMT_END_NAMESPACE +# define FMT_THROW(x) internal::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + static_cast(sizeof(x)); \ + FMT_ASSERT(false, ""); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \ + FMT_CUDA_VERSION >= 700) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +#ifndef FMT_USE_UDL_TEMPLATE +// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly +// support UDL templates and GCC >= 9 warns about them. +# if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \ + FMT_CUDA_VERSION == 0 && \ + ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \ + __cplusplus >= 201402L) || \ + FMT_CLANG_VERSION >= 304) +# define FMT_USE_UDL_TEMPLATE 1 +# else +# define FMT_USE_UDL_TEMPLATE 0 +# endif +#endif + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif + +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif + +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) +# include // _BitScanReverse, _BitScanReverse64 + +FMT_BEGIN_NAMESPACE +namespace internal { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# endif +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress : 6102) + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) internal::clz(n) + +# if defined(_WIN64) && !defined(__clang__) +# pragma intrinsic(_BitScanReverse64) +# endif + +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress : 6102) + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) internal::clzll(n) +} // namespace internal +FMT_END_NAMESPACE +#endif + +// Enable the deprecated numeric alignment. +#ifndef FMT_NUMERIC_ALIGN +# define FMT_NUMERIC_ALIGN 1 +#endif + +// Enable the deprecated percent specifier. +#ifndef FMT_DEPRECATED_PERCENT +# define FMT_DEPRECATED_PERCENT 0 +#endif + +FMT_BEGIN_NAMESPACE +namespace internal { + +// An equivalent of `*reinterpret_cast(&source)` that doesn't have +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +inline bool is_big_endian() { + const auto u = 1u; + struct bytes { + char data[sizeof(u)]; + }; + return bit_cast(u).data[0] == 0; +} + +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; + + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (is_big_endian()) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); + } + } +}; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +#else +using uintptr_t = fallback_uintptr; +inline fallback_uintptr to_uintptr(const void* p) { + return fallback_uintptr(p); +} +#endif + +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr T max_value() { + return (std::numeric_limits::max)(); +} +template constexpr int num_bits() { + return std::numeric_limits::digits; +} +template <> constexpr int num_bits() { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); +} + +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); + +// Detect the iterator category of *any* given type in a SFINAE-friendly way. +// Unfortunately, older implementations of std::iterator_traits are not safe +// for use in a SFINAE-context. +template +struct iterator_category : std::false_type {}; + +template struct iterator_category { + using type = std::random_access_iterator_tag; +}; + +template +struct iterator_category> { + using type = typename It::iterator_category; +}; + +// Detect if *any* given type models the OutputIterator concept. +template class is_output_iterator { + // Check for mutability because all iterator categories derived from + // std::input_iterator_tag *may* also meet the requirements of an + // OutputIterator, thereby falling into the category of 'mutable iterators' + // [iterator.requirements.general] clause 4. The compiler reveals this + // property only at the point of *actually dereferencing* the iterator! + template + static decltype(*(std::declval())) test(std::input_iterator_tag); + template static char& test(std::output_iterator_tag); + template static const char& test(...); + + using type = decltype(test(typename iterator_category::type{})); + + public: + enum { value = !std::is_const>::value }; +}; + +// A workaround for std::string not having mutable data() until C++17. +template inline Char* get_data(std::basic_string& s) { + return &s[0]; +} +template +inline typename Container::value_type* get_data(Container& c) { + return c.data(); +} + +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template checked_ptr make_checked(T* p, std::size_t size) { + return {p, size}; +} +#else +template using checked_ptr = T*; +template inline T* make_checked(T* p, std::size_t) { return p; } +#endif + +template ::value)> +inline checked_ptr reserve( + std::back_insert_iterator& it, std::size_t n) { + Container& c = get_container(it); + std::size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template +inline Iterator& reserve(Iterator& it, std::size_t) { + return it; +} + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + std::size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + std::size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type operator*() const { return {}; } +}; + +template class truncating_iterator_base { + protected: + OutputIt out_; + std::size_t limit_; + std::size_t count_; + + truncating_iterator_base(OutputIt out, std::size_t limit) + : out_(out), limit_(limit), count_(0) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = void; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + std::size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator(OutputIt out, std::size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator(OutputIt out, std::size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + +// A range with the specified output iterator and value type. +template +class output_range { + private: + OutputIt it_; + + public: + using value_type = T; + using iterator = OutputIt; + struct sentinel {}; + + explicit output_range(OutputIt it) : it_(it) {} + OutputIt begin() const { return it_; } + sentinel end() const { return {}; } // Sentinel is not used yet. +}; + +template +inline size_t count_code_points(basic_string_view s) { + return s.size(); +} + +// Counts the number of code points in a UTF-8 string. +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80) ++num_code_points; + } + return num_code_points; +} + +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + +template +inline size_t code_point_index(basic_string_view s, size_t n) { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline size_t code_point_index(basic_string_view s, size_t n) { + const char8_type* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { + return i; + } + } + return s.size(); +} + +inline char8_type to_char8_t(char c) { return static_cast(c); } + +template +using needs_conversion = bool_constant< + std::is_same::value_type, + char>::value && + std::is_same::value>; + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::copy(begin, end, it); +} + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::transform(begin, end, it, to_char8_t); +} + +#ifndef FMT_USE_GRISU +# define FMT_USE_GRISU 1 +#endif + +template constexpr bool use_grisu() { + return FMT_USE_GRISU && std::numeric_limits::is_iec559 && + sizeof(T) <= sizeof(double); +} + +template +template +void buffer::append(const U* begin, const U* end) { + std::size_t new_size = size_ + to_unsigned(end - begin); + reserve(new_size); + std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_); + size_ = new_size; +} +} // namespace internal + +// A range with an iterator appending to a buffer. +template +class buffer_range : public internal::output_range< + std::back_insert_iterator>, T> { + public: + using iterator = std::back_insert_iterator>; + using internal::output_range::output_range; + buffer_range(internal::buffer& buf) + : internal::output_range(std::back_inserter(buf)) {} +}; + +class FMT_DEPRECATED u8string_view + : public basic_string_view { + public: + u8string_view(const char* s) + : basic_string_view( + reinterpret_cast(s)) {} + u8string_view(const char* s, size_t count) FMT_NOEXCEPT + : basic_string_view( + reinterpret_cast(s), count) {} +}; + +#if FMT_USE_USER_DEFINED_LITERALS +inline namespace literals { +FMT_DEPRECATED inline basic_string_view operator"" _u( + const char* s, std::size_t n) { + return {reinterpret_cast(s), n}; +} +} // namespace literals +#endif + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use one of the following type aliases for common character types: + + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + + **Example**:: + + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); + + This will append the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer : private Allocator, public internal::buffer { + private: + T store_[SIZE]; + + // Deallocate memory allocated by the buffer. + void deallocate() { + T* data = this->data(); + if (data != store_) Allocator::deallocate(data, this->capacity()); + } + + protected: + void grow(std::size_t size) FMT_OVERRIDE; + + public: + using value_type = T; + using const_reference = const T&; + + explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + : Allocator(alloc) { + this->set(store_, SIZE); + } + ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); } + + private: + // Move data from other to this buffer. + void move(basic_memory_buffer& other) { + Allocator &this_alloc = *this, &other_alloc = other; + this_alloc = std::move(other_alloc); + T* data = other.data(); + std::size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, + internal::make_checked(store_, capacity)); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return *this; } +}; + +template +void basic_memory_buffer::grow(std::size_t size) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif + std::size_t old_capacity = this->capacity(); + std::size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) new_capacity = size; + T* old_data = this->data(); + T* new_data = std::allocator_traits::allocate(*this, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + internal::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) Allocator::deallocate(old_data, old_capacity); +} + +using memory_buffer = basic_memory_buffer; +using wmemory_buffer = basic_memory_buffer; + +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { + public: + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; +}; + +namespace internal { + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::is_signed)> +FMT_CONSTEXPR bool is_negative(T value) { + return value < 0; +} +template ::is_signed)> +FMT_CONSTEXPR bool is_negative(T) { + return false; +} + +template ::value)> +FMT_CONSTEXPR bool is_supported_floating_point(T) { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of T. +template +using uint32_or_64_or_128_t = conditional_t< + std::numeric_limits::digits <= 32, uint32_t, + conditional_t::digits <= 64, uint64_t, uint128_t>>; + +// Static data is placed in this class template for the header-only config. +template struct FMT_EXTERN_TEMPLATE_API basic_data { + static const uint64_t powers_of_10_64[]; + static const uint32_t zero_or_powers_of_10_32[]; + static const uint64_t zero_or_powers_of_10_64[]; + static const uint64_t pow10_significands[]; + static const int16_t pow10_exponents[]; + static const char digits[]; + static const char hex_digits[]; + static const char foreground_color[]; + static const char background_color[]; + static const char reset_color[5]; + static const wchar_t wreset_color[5]; + static const char signs[]; +}; + +FMT_EXTERN template struct basic_data; + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline int count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return t - (n < data::zero_or_powers_of_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline int count_digits(uint64_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#if FMT_USE_INT128 +inline int count_digits(uint128_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000U; + count += 4; + } +} +#endif + +// Counts the number of digits in n. BITS = log2(radix). +template inline int count_digits(UInt n) { + int num_digits = 0; + do { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; +} + +template <> int count_digits<4>(internal::fallback_uintptr n); + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline int count_digits(uint32_t n) { + int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return t - (n < data::zero_or_powers_of_10_32[t]) + 1; +} +#endif + +template FMT_API std::string grouping_impl(locale_ref loc); +template inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} +template <> inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} + +template FMT_API Char thousands_sep_impl(locale_ref loc); +template inline Char thousands_sep(locale_ref loc) { + return Char(thousands_sep_impl(loc)); +} +template <> inline wchar_t thousands_sep(locale_ref loc) { + return thousands_sep_impl(loc); +} + +template FMT_API Char decimal_point_impl(locale_ref loc); +template inline Char decimal_point(locale_ref loc) { + return Char(decimal_point_impl(loc)); +} +template <> inline wchar_t decimal_point(locale_ref loc) { + return decimal_point_impl(loc); +} + +// Formats a decimal unsigned integer value writing into buffer. +// add_thousands_sep is called after writing each char to add a thousands +// separator if necessary. +template +inline Char* format_decimal(Char* buffer, UInt value, int num_digits, + F add_thousands_sep) { + FMT_ASSERT(num_digits >= 0, "invalid digit count"); + buffer += num_digits; + Char* end = buffer; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + auto index = static_cast((value % 100) * 2); + value /= 100; + *--buffer = static_cast(data::digits[index + 1]); + add_thousands_sep(buffer); + *--buffer = static_cast(data::digits[index]); + add_thousands_sep(buffer); + } + if (value < 10) { + *--buffer = static_cast('0' + value); + return end; + } + auto index = static_cast(value * 2); + *--buffer = static_cast(data::digits[index + 1]); + add_thousands_sep(buffer); + *--buffer = static_cast(data::digits[index]); + return end; +} + +template constexpr int digits10() FMT_NOEXCEPT { + return std::numeric_limits::digits10; +} +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } + +template +inline Iterator format_decimal(Iterator out, UInt value, int num_digits, + F add_thousands_sep) { + FMT_ASSERT(num_digits >= 0, "invalid digit count"); + // Buffer should be large enough to hold all digits (<= digits10 + 1). + enum { max_size = digits10() + 1 }; + Char buffer[2 * max_size]; + auto end = format_decimal(buffer, value, num_digits, add_thousands_sep); + return internal::copy_str(buffer, end, out); +} + +template +inline It format_decimal(It out, UInt value, int num_digits) { + return format_decimal(out, value, num_digits, [](Char*) {}); +} + +template +inline Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits, + bool = false) { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast(data::hex_digits[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + +template +inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return internal::copy_str(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + wmemory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { return {&buffer_[0], size()}; } + size_t size() const { return buffer_.size() - 1; } + const wchar_t* c_str() const { return &buffer_[0]; } + std::wstring str() const { return {&buffer_[0], size()}; } +}; + +template struct null {}; + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size]; + unsigned char size_; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) { + FMT_THROW(format_error("invalid fill")); + return; + } + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + size_t size() const { return size_; } + const Char* data() const { return data_; } + + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } + FMT_CONSTEXPR const Char& operator[](size_t index) const { + return data_[index]; + } + + static FMT_CONSTEXPR fill_t make() { + auto fill = fill_t(); + fill[0] = Char(' '); + fill.size_ = 1; + return fill; + } +}; +} // namespace internal + +// We cannot use enum classes as bit fields because of a gcc bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +namespace align { +enum type { none, left, right, center, numeric }; +} +using align_t = align::type; + +namespace sign { +enum type { none, minus, plus, space }; +} +using sign_t = sign::type; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + char type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + internal::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(0), + align(align::none), + sign(sign::none), + alt(false), + fill(internal::fill_t::make()) {} +}; + +using format_specs = basic_format_specs; + +namespace internal { + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool percent : 1; + bool binary32 : 1; + bool use_grisu : 1; + bool showpoint : 1; +}; + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template It write_exponent(int exp, It it) { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = data::digits + (exp / 100) * 2; + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = data::digits + exp * 2; + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} + +template class float_writer { + private: + // The number is given as v = digits_ * pow(10, exp_). + const char* digits_; + int num_digits_; + int exp_; + size_t size_; + float_specs specs_; + Char decimal_point_; + + template It prettify(It it) const { + // pow(10, full_exp - 1) <= v <= pow(10, full_exp). + int full_exp = num_digits_ + exp_; + if (specs_.format == float_format::exp) { + // Insert a decimal point after the first digit and add an exponent. + *it++ = static_cast(*digits_); + int num_zeros = specs_.precision - num_digits_; + if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_; + it = copy_str(digits_ + 1, digits_ + num_digits_, it); + if (num_zeros > 0 && specs_.showpoint) + it = std::fill_n(it, num_zeros, static_cast('0')); + *it++ = static_cast(specs_.upper ? 'E' : 'e'); + return write_exponent(full_exp - 1, it); + } + if (num_digits_ <= full_exp) { + // 1234e7 -> 12340000000[.0+] + it = copy_str(digits_, digits_ + num_digits_, it); + it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); + if (specs_.showpoint || specs_.precision < 0) { + *it++ = decimal_point_; + int num_zeros = specs_.precision - full_exp; + if (num_zeros <= 0) { + if (specs_.format != float_format::fixed) + *it++ = static_cast('0'); + return it; + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (num_zeros > 1000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else if (full_exp > 0) { + // 1234e-2 -> 12.34[0+] + it = copy_str(digits_, digits_ + full_exp, it); + if (!specs_.showpoint) { + // Remove trailing zeros. + int num_digits = num_digits_; + while (num_digits > full_exp && digits_[num_digits - 1] == '0') + --num_digits; + if (num_digits != full_exp) *it++ = decimal_point_; + return copy_str(digits_ + full_exp, digits_ + num_digits, it); + } + *it++ = decimal_point_; + it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); + if (specs_.precision > num_digits_) { + // Add trailing zeros. + int num_zeros = specs_.precision - num_digits_; + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else { + // 1234e-6 -> 0.001234 + *it++ = static_cast('0'); + int num_zeros = -full_exp; + int num_digits = num_digits_; + if (num_digits == 0 && specs_.precision >= 0 && + specs_.precision < num_zeros) { + num_zeros = specs_.precision; + } + // Remove trailing zeros. + if (!specs_.showpoint) + while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; + if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) { + *it++ = decimal_point_; + it = std::fill_n(it, num_zeros, static_cast('0')); + it = copy_str(digits_, digits_ + num_digits, it); + } + } + return it; + } + + public: + float_writer(const char* digits, int num_digits, int exp, float_specs specs, + Char decimal_point) + : digits_(digits), + num_digits_(num_digits), + exp_(exp), + specs_(specs), + decimal_point_(decimal_point) { + int full_exp = num_digits + exp - 1; + int precision = specs.precision > 0 ? specs.precision : 16; + if (specs_.format == float_format::general && + !(full_exp >= -4 && full_exp < precision)) { + specs_.format = float_format::exp; + } + size_ = prettify(counting_iterator()).count(); + size_ += specs.sign ? 1 : 0; + } + + size_t size() const { return size_; } + size_t width() const { return size(); } + + template void operator()(It&& it) { + if (specs_.sign) *it++ = static_cast(data::signs[specs_.sign]); + it = prettify(it); + } +}; + +template +int format_float(T value, int precision, float_specs specs, buffer& buf); + +// Formats a floating-point number with snprintf. +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf); + +template T promote_float(T value) { return value; } +inline double promote_float(float value) { return static_cast(value); } + +template +FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { + switch (spec) { + case 0: + case 'd': + handler.on_dec(); + break; + case 'x': + case 'X': + handler.on_hex(); + break; + case 'b': + case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; + case 'n': + case 'L': + handler.on_num(); + break; + default: + handler.on_error(); + } +} + +template +FMT_CONSTEXPR float_specs parse_float_type_spec( + const basic_format_specs& specs, ErrorHandler&& eh = {}) { + auto result = float_specs(); + result.showpoint = specs.alt; + switch (specs.type) { + case 0: + result.format = float_format::general; + result.showpoint |= specs.precision > 0; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; +#if FMT_DEPRECATED_PERCENT + case '%': + result.format = float_format::fixed; + result.percent = true; + break; +#endif + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; + case 'n': + result.locale = true; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, + Handler&& handler) { + if (!specs) return handler.on_char(); + if (specs->type && specs->type != 'c') return handler.on_int(); + if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} + +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +} + +template class int_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} + + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; + +template +class char_specs_checker : public ErrorHandler { + private: + char type_; + + public: + FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) + : ErrorHandler(eh), type_(type) {} + + FMT_CONSTEXPR void on_int() { + handle_int_type_spec(type_, int_type_checker(*this)); + } + FMT_CONSTEXPR void on_char() {} +}; + +template +class cstring_type_checker : public ErrorHandler { + public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} +}; + +template +void arg_map::init(const basic_format_args& args) { + if (map_) return; + map_ = new entry[internal::to_unsigned(args.max_size())]; + if (args.is_packed()) { + for (int i = 0;; ++i) { + internal::type arg_type = args.type(i); + if (arg_type == internal::type::none_type) return; + if (arg_type == internal::type::named_arg_type) + push_back(args.values_[i]); + } + } + for (int i = 0, n = args.max_size(); i < n; ++i) { + auto type = args.args_[i].type_; + if (type == internal::type::named_arg_type) push_back(args.args_[i].value_); + } +} + +template struct nonfinite_writer { + sign_t sign; + const char* str; + static constexpr size_t str_size = 3; + + size_t size() const { return str_size + (sign ? 1 : 0); } + size_t width() const { return size(); } + + template void operator()(It&& it) const { + if (sign) *it++ = static_cast(data::signs[sign]); + it = copy_str(str, str + str_size, it); + } +}; + +template +FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { + auto fill_size = fill.size(); + if (fill_size == 1) return std::fill_n(it, n, fill[0]); + for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + return it; +} + +// This template provides operations for formatting and writing data into a +// character range. +template class basic_writer { + public: + using char_type = typename Range::value_type; + using iterator = typename Range::iterator; + using format_specs = basic_format_specs; + + private: + iterator out_; // Output iterator. + locale_ref locale_; + + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) { + return internal::reserve(out_, n); + } + + template struct padded_int_writer { + size_t size_; + string_view prefix; + char_type fill; + std::size_t padding; + F f; + + size_t size() const { return size_; } + size_t width() const { return size_; } + + template void operator()(It&& it) const { + if (prefix.size() != 0) + it = copy_str(prefix.begin(), prefix.end(), it); + it = std::fill_n(it, padding, fill); + f(it); + } + }; + + // Writes an integer in the format + // + // where are written by f(it). + template + void write_int(int num_digits, string_view prefix, format_specs specs, F f) { + std::size_t size = prefix.size() + to_unsigned(num_digits); + char_type fill = specs.fill[0]; + std::size_t padding = 0; + if (specs.align == align::numeric) { + auto unsiged_width = to_unsigned(specs.width); + if (unsiged_width > size) { + padding = unsiged_width - size; + size = unsiged_width; + } + } else if (specs.precision > num_digits) { + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + fill = static_cast('0'); + } + if (specs.align == align::none) specs.align = align::right; + write_padded(specs, padded_int_writer{size, prefix, fill, padding, f}); + } + + // Writes a decimal integer. + template void write_decimal(Int value) { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto&& it = reserve((negative ? 1 : 0) + static_cast(num_digits)); + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits); + } + + // The handle_int_type_spec handler that writes an integer. + template struct int_writer { + using unsigned_type = uint32_or_64_or_128_t; + + basic_writer& writer; + const Specs& specs; + unsigned_type abs_value; + char prefix[4]; + unsigned prefix_size; + + string_view get_prefix() const { return string_view(prefix, prefix_size); } + + int_writer(basic_writer& w, Int value, const Specs& s) + : writer(w), + specs(s), + abs_value(static_cast(value)), + prefix_size(0) { + if (is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (specs.sign != sign::none && specs.sign != sign::minus) { + prefix[0] = specs.sign == sign::plus ? '+' : ' '; + ++prefix_size; + } + } + + struct dec_writer { + unsigned_type abs_value; + int num_digits; + + template void operator()(It&& it) const { + it = internal::format_decimal(it, abs_value, num_digits); + } + }; + + void on_dec() { + int num_digits = count_digits(abs_value); + writer.write_int(num_digits, get_prefix(), specs, + dec_writer{abs_value, num_digits}); + } + + struct hex_writer { + int_writer& self; + int num_digits; + + template void operator()(It&& it) const { + it = format_uint<4, char_type>(it, self.abs_value, num_digits, + self.specs.type != 'x'); + } + }; + + void on_hex() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = specs.type; + } + int num_digits = count_digits<4>(abs_value); + writer.write_int(num_digits, get_prefix(), specs, + hex_writer{*this, num_digits}); + } + + template struct bin_writer { + unsigned_type abs_value; + int num_digits; + + template void operator()(It&& it) const { + it = format_uint(it, abs_value, num_digits); + } + }; + + void on_bin() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(specs.type); + } + int num_digits = count_digits<1>(abs_value); + writer.write_int(num_digits, get_prefix(), specs, + bin_writer<1>{abs_value, num_digits}); + } + + void on_oct() { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + writer.write_int(num_digits, get_prefix(), specs, + bin_writer<3>{abs_value, num_digits}); + } + + enum { sep_size = 1 }; + + struct num_writer { + unsigned_type abs_value; + int size; + const std::string& groups; + char_type sep; + + template void operator()(It&& it) const { + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having + // index 0. + int digit_index = 0; + std::string::const_iterator group = groups.cbegin(); + it = format_decimal( + it, abs_value, size, + [this, s, &group, &digit_index](char_type*& buffer) { + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + return; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + buffer -= s.size(); + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(buffer, s.size())); + }); + } + }; + + void on_num() { + std::string groups = grouping(writer.locale_); + if (groups.empty()) return on_dec(); + auto sep = thousands_sep(writer.locale_); + if (!sep) return on_dec(); + int num_digits = count_digits(abs_value); + int size = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && num_digits > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + num_digits -= *group; + ++group; + } + if (group == groups.cend()) + size += sep_size * ((num_digits - 1) / groups.back()); + writer.write_int(size, get_prefix(), specs, + num_writer{abs_value, size, groups, sep}); + } + + FMT_NORETURN void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } + }; + + template struct str_writer { + const Char* s; + size_t size_; + + size_t size() const { return size_; } + size_t width() const { + return count_code_points(basic_string_view(s, size_)); + } + + template void operator()(It&& it) const { + it = copy_str(s, s + size_, it); + } + }; + + struct bytes_writer { + string_view bytes; + + size_t size() const { return bytes.size(); } + size_t width() const { return bytes.size(); } + + template void operator()(It&& it) const { + const char* data = bytes.data(); + it = copy_str(data, data + size(), it); + } + }; + + template struct pointer_writer { + UIntPtr value; + int num_digits; + + size_t size() const { return to_unsigned(num_digits) + 2; } + size_t width() const { return size(); } + + template void operator()(It&& it) const { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + it = format_uint<4, char_type>(it, value, num_digits); + } + }; + + public: + explicit basic_writer(Range out, locale_ref loc = locale_ref()) + : out_(out.begin()), locale_(loc) {} + + iterator out() const { return out_; } + + // Writes a value in the format + // + // where is written by f(it). + template void write_padded(const format_specs& specs, F&& f) { + // User-perceived width (in code points). + unsigned width = to_unsigned(specs.width); + size_t size = f.size(); // The number of code units. + size_t num_code_points = width != 0 ? f.width() : size; + if (width <= num_code_points) return f(reserve(size)); + size_t padding = width - num_code_points; + size_t fill_size = specs.fill.size(); + auto&& it = reserve(size + padding * fill_size); + if (specs.align == align::right) { + it = fill(it, padding, specs.fill); + f(it); + } else if (specs.align == align::center) { + std::size_t left_padding = padding / 2; + it = fill(it, left_padding, specs.fill); + f(it); + it = fill(it, padding - left_padding, specs.fill); + } else { + f(it); + it = fill(it, padding, specs.fill); + } + } + + void write(int value) { write_decimal(value); } + void write(long value) { write_decimal(value); } + void write(long long value) { write_decimal(value); } + + void write(unsigned value) { write_decimal(value); } + void write(unsigned long value) { write_decimal(value); } + void write(unsigned long long value) { write_decimal(value); } + +#if FMT_USE_INT128 + void write(int128_t value) { write_decimal(value); } + void write(uint128_t value) { write_decimal(value); } +#endif + + template + void write_int(T value, const Spec& spec) { + handle_int_type_spec(spec.type, int_writer(*this, value, spec)); + } + + template ::value)> + void write(T value, format_specs specs = {}) { + if (const_check(!is_supported_floating_point(value))) { + return; + } + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; + } + + if (!std::isfinite(value)) { + auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf") + : (fspecs.upper ? "NAN" : "nan"); + return write_padded(specs, nonfinite_writer{fspecs.sign, str}); + } + + if (specs.align == align::none) { + specs.align = align::right; + } else if (specs.align == align::numeric) { + if (fspecs.sign) { + auto&& it = reserve(1); + *it++ = static_cast(data::signs[fspecs.sign]); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; + } + specs.align = align::right; + } + + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + write_padded(specs, str_writer{buffer.data(), buffer.size()}); + return; + } + int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + FMT_THROW(format_error("number is too big")); + else + ++precision; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + fspecs.use_grisu = use_grisu(); + if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100; + int exp = format_float(promote_float(value), precision, fspecs, buffer); + if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) { + buffer.push_back('%'); + --exp; // Adjust decimal place position. + } + fspecs.precision = precision; + char_type point = fspecs.locale ? decimal_point(locale_) + : static_cast('.'); + write_padded(specs, float_writer(buffer.data(), + static_cast(buffer.size()), + exp, fspecs, point)); + } + + void write(char value) { + auto&& it = reserve(1); + *it++ = value; + } + + template ::value)> + void write(Char value) { + auto&& it = reserve(1); + *it++ = value; + } + + void write(string_view value) { + auto&& it = reserve(value.size()); + it = copy_str(value.begin(), value.end(), it); + } + void write(wstring_view value) { + static_assert(std::is_same::value, ""); + auto&& it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); + } + + template + void write(const Char* s, std::size_t size, const format_specs& specs) { + write_padded(specs, str_writer{s, size}); + } + + template + void write(basic_string_view s, const format_specs& specs = {}) { + const Char* data = s.data(); + std::size_t size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + write(data, size, specs); + } + + void write_bytes(string_view bytes, const format_specs& specs) { + write_padded(specs, bytes_writer{bytes}); + } + + template + void write_pointer(UIntPtr value, const format_specs* specs) { + int num_digits = count_digits<4>(value); + auto pw = pointer_writer{value, num_digits}; + if (!specs) return pw(reserve(to_unsigned(num_digits) + 2)); + format_specs specs_copy = *specs; + if (specs_copy.align == align::none) specs_copy.align = align::right; + write_padded(specs_copy, pw); + } +}; + +using writer = basic_writer>; + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +class arg_formatter_base { + public: + using char_type = typename Range::value_type; + using iterator = typename Range::iterator; + using format_specs = basic_format_specs; + + private: + using writer_type = basic_writer; + writer_type writer_; + format_specs* specs_; + + struct char_writer { + char_type value; + + size_t size() const { return 1; } + size_t width() const { return 1; } + + template void operator()(It&& it) const { *it++ = value; } + }; + + void write_char(char_type value) { + if (specs_) + writer_.write_padded(*specs_, char_writer{value}); + else + writer_.write(value); + } + + void write_pointer(const void* p) { + writer_.write_pointer(internal::to_uintptr(p), specs_); + } + + protected: + writer_type& writer() { return writer_; } + FMT_DEPRECATED format_specs* spec() { return specs_; } + format_specs* specs() { return specs_; } + iterator out() { return writer_.out(); } + + void write(bool value) { + string_view sv(value ? "true" : "false"); + specs_ ? writer_.write(sv, *specs_) : writer_.write(sv); + } + + void write(const char_type* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + basic_string_view sv(value, length); + specs_ ? writer_.write(sv, *specs_) : writer_.write(sv); + } + } + + public: + arg_formatter_base(Range r, format_specs* s, locale_ref loc) + : writer_(r, loc), specs_(s) {} + + iterator operator()(monostate) { + FMT_ASSERT(false, "invalid argument type"); + return out(); + } + + template ::value)> + iterator operator()(T value) { + if (specs_) + writer_.write_int(value, *specs_); + else + writer_.write(value); + return out(); + } + + iterator operator()(char_type value) { + internal::handle_char_specs( + specs_, char_spec_handler(*this, static_cast(value))); + return out(); + } + + iterator operator()(bool value) { + if (specs_ && specs_->type) return (*this)(value ? 1 : 0); + write(value != 0); + return out(); + } + + template ::value)> + iterator operator()(T value) { + if (const_check(is_supported_floating_point(value))) + writer_.write(value, specs_ ? *specs_ : format_specs()); + else + FMT_ASSERT(false, "unsupported float argument type"); + return out(); + } + + struct char_spec_handler : ErrorHandler { + arg_formatter_base& formatter; + char_type value; + + char_spec_handler(arg_formatter_base& f, char_type val) + : formatter(f), value(val) {} + + void on_int() { + if (formatter.specs_) + formatter.writer_.write_int(value, *formatter.specs_); + else + formatter.writer_.write(value); + } + void on_char() { formatter.write_char(value); } + }; + + struct cstring_spec_handler : internal::error_handler { + arg_formatter_base& formatter; + const char_type* value; + + cstring_spec_handler(arg_formatter_base& f, const char_type* val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + + iterator operator()(const char_type* value) { + if (!specs_) return write(value), out(); + internal::handle_cstring_type_spec(specs_->type, + cstring_spec_handler(*this, value)); + return out(); + } + + iterator operator()(basic_string_view value) { + if (specs_) { + internal::check_string_type_spec(specs_->type, internal::error_handler()); + writer_.write(value, *specs_); + } else { + writer_.write(value); + } + return out(); + } + + iterator operator()(const void* value) { + if (specs_) + check_pointer_type_spec(specs_->type, internal::error_handler()); + write_pointer(value); + return out(); + } +}; + +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, + ErrorHandler&& eh) { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + constexpr unsigned max_int = max_value(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + unsigned(*begin - '0'); + ++begin; + } while (begin != end && '0' <= *begin && *begin <= '9'); + if (value > max_int) eh.on_error("number is too big"); + return static_cast(value); +} + +template class custom_formatter { + private: + using char_type = typename Context::char_type; + + basic_format_parse_context& parse_ctx_; + Context& ctx_; + + public: + explicit custom_formatter(basic_format_parse_context& parse_ctx, + Context& ctx) + : parse_ctx_(parse_ctx), ctx_(ctx) {} + + bool operator()(typename basic_format_arg::handle h) const { + h.format(parse_ctx_, ctx_); + return true; + } + + template bool operator()(T) const { return false; } +}; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } + FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } + FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + + FMT_CONSTEXPR void on_zero() { + specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } + + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(Char type) { + specs_.type = static_cast(type); + } + + protected: + basic_format_specs& specs_; +}; + +template class numeric_specs_checker { + public: + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + error_handler_.on_error("format specifier requires numeric argument"); + } + + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + error_handler_.on_error("format specifier requires signed argument"); + } + } + + FMT_CONSTEXPR void check_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + error_handler_.on_error("precision not allowed for this argument type"); + } + + private: + ErrorHandler& error_handler_; + internal::type arg_type_; +}; + +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template class specs_checker : public Handler { + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type) + : Handler(handler), checker_(*this, arg_type) {} + + FMT_CONSTEXPR specs_checker(const specs_checker& other) + : Handler(other), checker_(*this, other.arg_type_) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) checker_.require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_plus() { + checker_.check_sign(); + Handler::on_plus(); + } + + FMT_CONSTEXPR void on_minus() { + checker_.check_sign(); + Handler::on_minus(); + } + + FMT_CONSTEXPR void on_space() { + checker_.check_sign(); + Handler::on_space(); + } + + FMT_CONSTEXPR void on_hash() { + checker_.require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_zero() { + checker_.require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } + + private: + numeric_specs_checker checker_; +}; + +template