diff --git a/docs/algorithms/simulation.rst b/docs/algorithms/simulation.rst index 88b6e6333..5ba3d134e 100644 --- a/docs/algorithms/simulation.rst +++ b/docs/algorithms/simulation.rst @@ -82,16 +82,34 @@ With ``partial_simulator``, adding new simulation patterns and re-simulation can Incremental simulation is adopted, which speeds up simulation time by only re-simulating needed nodes and only re-computing the last block of ``partial_truth_table``. Note that currently only AIG and XAG are supported. +**Constructors** + .. doxygenfunction:: mockturtle::partial_simulator::partial_simulator( unsigned, unsigned, std::default_random_engine::result_type ) .. doxygenfunction:: mockturtle::partial_simulator::partial_simulator( std::vector const& ) .. doxygenfunction:: mockturtle::partial_simulator::partial_simulator( const std::string&, uint32_t ) +**Interfaces** + +.. doxygenfunction:: mockturtle::partial_simulator::add_pattern( std::vector const& ) + .. doxygenfunction:: mockturtle::partial_simulator::num_bits() .. doxygenfunction:: mockturtle::partial_simulator::get_patterns() -.. doxygenfunction:: mockturtle::simulate_nodes( Ntk const&, unordered_node_map&, partial_simulator const&, bool ) +**Simulation** + +.. doxygenfunction:: mockturtle::simulate_nodes( Ntk const&, unordered_node_map&, Simulator const&, bool ) + +.. doxygenfunction:: mockturtle::simulate_node( Ntk const&, typename Ntk::node const&, unordered_node_map&, Simulator const& ) + +**Bit Packing** + +To reduce the size of simulation pattern set during pattern generation, ``bit_packed_simulator`` can be used instead of ``partial_simulator``, which has additional interfaces to specify care bits in patterns and to perform bit packing. + +.. doxygenclass:: mockturtle::bit_packed_simulator + +.. doxygenfunction:: mockturtle::bit_packed_simulator::add_pattern( std::vector const&, std::vector const& ) -.. doxygenfunction:: mockturtle::simulate_node( Ntk const&, typename Ntk::node const&, unordered_node_map&, partial_simulator const& ) +.. doxygenfunction:: mockturtle::bit_packed_simulator::pack_bits() diff --git a/experiments/pattern_generation.cpp b/experiments/pattern_generation.cpp index a7e5ffd9b..cf2872cac 100644 --- a/experiments/pattern_generation.cpp +++ b/experiments/pattern_generation.cpp @@ -54,7 +54,7 @@ int main() pattern_generation_stats st; uint32_t num_random_pattern = 1000; - partial_simulator sim( aig.num_pis(), num_random_pattern ); + bit_packed_simulator sim( aig.num_pis(), num_random_pattern ); pattern_generation( aig, sim, ps, &st ); exp( benchmark, aig.num_pis(), size_before, sim.num_bits(), st.num_generated_patterns, st.num_constant, to_seconds( st.time_total ), to_seconds( st.time_sim ), to_seconds( st.time_sat ) ); diff --git a/include/mockturtle/algorithms/circuit_validator.hpp b/include/mockturtle/algorithms/circuit_validator.hpp index 42f24aada..a5e771d21 100644 --- a/include/mockturtle/algorithms/circuit_validator.hpp +++ b/include/mockturtle/algorithms/circuit_validator.hpp @@ -95,7 +95,7 @@ class circuit_validator }; explicit circuit_validator( Ntk const& ntk, validator_params const& ps = {} ) - : ntk( ntk ), ps( ps ), literals( ntk ), cex( ntk.num_pis() ) + : ntk( ntk ), ps( ps ), literals( 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" ); @@ -146,7 +146,7 @@ class circuit_validator 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 ) + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) { restart(); } @@ -161,7 +161,7 @@ class circuit_validator 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 ) + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) { restart(); } @@ -231,7 +231,7 @@ class circuit_validator pop(); } - if ( solver.num_clauses() > ps.max_clauses ) + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) { restart(); } @@ -278,7 +278,7 @@ class circuit_validator res = solve( {lit_not_cond( literals[root], value )} ); } - if ( solver.num_clauses() > ps.max_clauses ) + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) { restart(); } @@ -345,7 +345,7 @@ class circuit_validator } pop(); - if ( solver.num_clauses() > ps.max_clauses ) + if ( solver.num_clauses() > ps.max_clauses && num_invoke >= MIN_NUM_INVOKE ) { restart(); } @@ -365,6 +365,7 @@ class circuit_validator private: void restart() { + num_invoke = 0u; solver.restart(); if constexpr ( randomize ) { @@ -516,6 +517,7 @@ class circuit_validator std::optional solve( std::vector assumptions ) { + ++num_invoke; auto const res = solver.solve( assumptions, ps.conflict_limit ); if ( res == bill::result::states::satisfiable ) @@ -692,6 +694,9 @@ class circuit_validator unordered_node_map literals; bill::solver solver; + static const uint32_t MIN_NUM_INVOKE = 20u; + uint32_t num_invoke; + bool between_push_pop = false; std::vector tmp; diff --git a/include/mockturtle/algorithms/pattern_generation.hpp b/include/mockturtle/algorithms/pattern_generation.hpp index d0da63c68..bf0af0c61 100644 --- a/include/mockturtle/algorithms/pattern_generation.hpp +++ b/include/mockturtle/algorithms/pattern_generation.hpp @@ -111,7 +111,7 @@ struct pattern_generation_stats namespace detail { -template +template class patgen_impl { public: @@ -119,7 +119,7 @@ class patgen_impl using signal = typename Ntk::signal; using TT = unordered_node_map; - explicit patgen_impl( Ntk& ntk, partial_simulator& sim, pattern_generation_params const& ps, validator_params& vps, pattern_generation_stats& st ) + 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 ) { @@ -130,12 +130,20 @@ class patgen_impl stopwatch t( st.time_total ); call_with_stopwatch( st.time_sim, [&]() { - simulate_nodes( ntk, tts, 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 ( ps.substitute_const ) { for ( auto n : const_nodes ) @@ -151,6 +159,14 @@ class patgen_impl 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 ); + } ); + } } } @@ -226,7 +242,7 @@ class patgen_impl } } - new_pattern( validator.cex ); + new_pattern( validator.cex, n ); if ( ps.num_stuck_at > 1 ) { @@ -236,7 +252,7 @@ class patgen_impl } ); for ( auto& pattern : generated ) { - new_pattern( pattern ); + new_pattern( pattern, n ); } } @@ -311,7 +327,7 @@ class patgen_impl { if ( !( *res ) ) { - new_pattern( validator.cex ); + new_pattern( validator.cex, n ); ++st.unobservable_type2; if ( ps.verbose ) @@ -348,7 +364,7 @@ class patgen_impl { if ( !( *res ) ) { - new_pattern( validator.cex ); + new_pattern( validator.cex, n ); ++st.unobservable_type2; if ( ps.verbose ) @@ -376,9 +392,18 @@ class patgen_impl } private: - void new_pattern( std::vector const& pattern ) + void new_pattern( std::vector const& pattern, node const& n ) { - sim.add_pattern( pattern ); + if constexpr( std::is_same_v ) + { + sim.add_pattern( pattern, compute_support( n ) ); + } + else + { + (void)n; + sim.add_pattern( pattern ); + } + ++st.num_generated_patterns; /* re-simulate */ @@ -411,11 +436,78 @@ class patgen_impl } ); for ( auto& pattern : generated ) { - new_pattern( pattern ); + 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, int 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; @@ -428,7 +520,7 @@ class patgen_impl TT tts; std::vector const_nodes; - partial_simulator& sim; + Simulator& sim; }; } /* namespace detail */ @@ -441,22 +533,24 @@ class patgen_impl * * [1] Simulation-Guided Boolean Resubstitution. IWLS 2020 (arXiv:2007.02579). * - * \param sim Reference of a `partial_simulator` object where the generated - * patterns will be stored. It can be empty (`partial_simulator( ntk.num_pis(), 0 )`) + * \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 - * (`partial_simulator( filename )`) or randomly generated - * (`partial_simulator( ntk.num_pis(), num_random_patterns )`). The generated + * (`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, partial_simulator& sim, pattern_generation_params const& ps = {}, pattern_generation_stats* pst = nullptr ) +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; @@ -469,7 +563,7 @@ void pattern_generation( Ntk& ntk, partial_simulator& sim, pattern_generation_pa using fanout_view_t = fanout_view; fanout_view_t fanout_view{ntk}; - detail::patgen_impl p( fanout_view, sim, ps, vps, st ); + detail::patgen_impl p( fanout_view, sim, ps, vps, st ); p.run(); } else diff --git a/include/mockturtle/algorithms/sim_resub.hpp b/include/mockturtle/algorithms/sim_resub.hpp index 40ce5625b..d57b8e241 100644 --- a/include/mockturtle/algorithms/sim_resub.hpp +++ b/include/mockturtle/algorithms/sim_resub.hpp @@ -566,7 +566,7 @@ struct abc_resub_functor_stats std::cout << fmt::format( "[i] #solution = {:6d}\n", num_success ); std::cout << fmt::format( "[i] #invoke = {:6d}\n", num_success + num_fail ); std::cout << fmt::format( "[i] ABC time: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); - std::cout << fmt::format( "[i] interface: {:>5.2f} secs\n", to_seconds( time_compute_function ) ); + std::cout << fmt::format( "[i] interface: {:>5.2f} secs\n", to_seconds( time_interface ) ); // clang-format on } }; @@ -815,7 +815,7 @@ class simulation_based_resub_engine /* first simulation: the whole circuit; from 0 bits. */ call_with_stopwatch( st.time_sim, [&]() { - simulate_nodes( ntk, tts, sim ); + simulate_nodes( ntk, tts, sim, true ); }); } diff --git a/include/mockturtle/algorithms/simulation.hpp b/include/mockturtle/algorithms/simulation.hpp index 404ba7d7b..9af0b2822 100644 --- a/include/mockturtle/algorithms/simulation.hpp +++ b/include/mockturtle/algorithms/simulation.hpp @@ -1,5 +1,5 @@ /* mockturtle: C++ logic network library - * Copyright (C) 2018-2019 EPFL + * Copyright (C) 2018-2020 EPFL * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,6 +28,7 @@ \brief Simulate networks \author Mathias Soeken + \author Siang-Yun Lee (partial simulation) */ #pragma once @@ -35,6 +36,7 @@ #include #include #include +#include #include "../traits.hpp" #include "../utils/node_map.hpp" @@ -42,6 +44,7 @@ #include #include #include +#include #include #include @@ -167,6 +170,8 @@ class default_simulator> */ class partial_simulator { + friend class bit_packed_simulator; + public: partial_simulator() {} @@ -252,6 +257,10 @@ class partial_simulator 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() ); @@ -265,8 +274,7 @@ class partial_simulator /*! \brief Get the simulation patterns. * - * Returns a vector of `num_pis()` patterns stored in `kitty::partial_truth_table`s. - * + * \return A vector of `num_pis()` patterns stored in `kitty::partial_truth_table`s. */ std::vector get_patterns() const { @@ -278,6 +286,170 @@ class partial_simulator 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_pi; + using partial_simulator::compute_not; + using partial_simulator::num_bits; + using partial_simulator::get_patterns; + + bit_packed_simulator() {} + + bit_packed_simulator( unsigned num_pis, unsigned num_patterns, std::default_random_engine::result_type seed = 0 ) + : partial_simulator( num_pis, num_patterns, seed ), packed_patterns( num_patterns ) + { + fill_cares( num_pis ); + } + + /* copy constructor */ + bit_packed_simulator( bit_packed_simulator const& sim ) + : partial_simulator( sim ), care( sim.care ), packed_patterns( sim.packed_patterns ) + { } + + /* 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 ( int p = num_patterns - 1; p >= (int)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 */ + int j = 0; + for ( int i = empty_slots.size() - 1; i >= 0; --i ) + { + while ( empty_slots[j] >= num_patterns - 1 && j <= i ) + { + if ( empty_slots[j] == num_patterns - 1 ) { --num_patterns; } + ++j; + if ( j == (int)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; + } + +private: + /* all bits in patterns generated before construction are care bits */ + void fill_cares( uint32_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( uint32_t const from, uint32_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 ) ); + if ( kitty::get_bit( patterns[i], from ) ) + { + kitty::set_bit( patterns[i], to ); + } + else + { + kitty::clear_bit( 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. @@ -429,8 +601,8 @@ void simulate_nodes( Ntk const& ntk, unordered_node_map& no namespace detail { -template -void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, unordered_node_map& node_to_value, partial_simulator const& sim ) +template +void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, unordered_node_map& node_to_value, Simulator const& sim ) { std::vector fanin_values( ntk.fanin_size( n ) ); ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { @@ -444,8 +616,8 @@ void simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, unordered 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, unordered_node_map& node_to_value, partial_simulator const& sim ) +template +void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, unordered_node_map& node_to_value, Simulator const& sim ) { std::vector fanin_values( ntk.fanin_size( n ) ); ntk.foreach_fanin( n, [&]( auto const& f, auto i ) { @@ -459,8 +631,8 @@ void re_simulate_fanin_cone( Ntk const& ntk, typename Ntk::node const& n, unorde ntk.compute( n, node_to_value[n], fanin_values.begin(), fanin_values.end() ); } -template -void update_const_pi( Ntk const& ntk, unordered_node_map& node_to_value, partial_simulator const& sim ) +template +void update_const_pi( Ntk const& ntk, unordered_node_map& node_to_value, Simulator const& sim ) { /* constants */ node_to_value[ntk.get_node( ntk.get_constant( false ) )] = sim.compute_constant( ntk.constant_value( ntk.get_node( ntk.get_constant( false ) ) ) ); @@ -485,8 +657,8 @@ void update_const_pi( Ntk const& ntk, unordered_node_map -void simulate_node( Ntk const& ntk, typename Ntk::node const& n, unordered_node_map& node_to_value, partial_simulator const& sim ) +template +void simulate_node( Ntk const& ntk, typename Ntk::node const& n, unordered_node_map& 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" ); @@ -496,6 +668,7 @@ void simulate_node( Ntk const& ntk, typename Ntk::node const& n, unordered_node_ 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() ) { @@ -524,7 +697,7 @@ void simulate_node( Ntk const& ntk, typename Ntk::node const& n, unordered_node_ } } -/*! \brief Simulates a network with a partial simulator. +/*! \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. @@ -533,8 +706,8 @@ void simulate_node( Ntk const& ntk, typename Ntk::node const& n, unordered_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, unordered_node_map& node_to_value, partial_simulator const& sim, bool simulate_whole_tt = true ) +template +void simulate_nodes( Ntk const& ntk, unordered_node_map& 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" ); @@ -545,6 +718,7 @@ void simulate_nodes( Ntk const& ntk, unordered_node_map, "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 ); diff --git a/include/mockturtle/io/write_patterns.hpp b/include/mockturtle/io/write_patterns.hpp index 83774b566..9a6c6d9ab 100644 --- a/include/mockturtle/io/write_patterns.hpp +++ b/include/mockturtle/io/write_patterns.hpp @@ -50,11 +50,14 @@ namespace mockturtle * 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` object containing simulation patterns + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns * \param out Output stream */ -inline void write_patterns( partial_simulator const& sim, std::ostream& out = std::cout ) +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 ) { @@ -67,11 +70,14 @@ inline void write_patterns( partial_simulator const& sim, std::ostream& out = st * 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` object containing simulation patterns + * \param sim The `partial_simulator` or `bit_packed_simulator` object containing simulation patterns * \param filename Filename */ -inline void write_patterns( partial_simulator const& sim, std::string const& 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(); diff --git a/test/algorithms/simulation.cpp b/test/algorithms/simulation.cpp index 153222c42..d3f4e912f 100644 --- a/test/algorithms/simulation.cpp +++ b/test/algorithms/simulation.cpp @@ -190,4 +190,67 @@ TEST_CASE( "Incremental simulation with partial_simulator", "[simulation]" ) const auto f5 = aig.create_and( f2, f4 ); simulate_node( aig, aig.get_node( f5 ), node_to_value, sim ); CHECK( ( aig.is_complemented( f5 ) ? ~node_to_value[f5] : node_to_value[f5] ) == kitty::partial_truth_table( 65 ) ); -} \ No newline at end of file +} + +TEST_CASE( "Bit packing", "[simulation]" ) +{ + std::vector pats( 5 ); + pats[0].add_bits( 0x1, 2 ); /* x0 = 01 */ + pats[1].add_bits( 0x1, 2 ); /* x1 = 01 */ + pats[2].add_bits( 0x1, 2 ); /* x2 = 01 */ + pats[3].add_bits( 0x1, 2 ); /* x3 = 01 */ + pats[4].add_bits( 0x1, 2 ); /* x4 = 01 */ + bit_packed_simulator sim( pats ); /* initial patterns are not packable (all bits are cared) */ + + std::vector p( 5 ); /* pattern */ + std::vector c( 5 ); /* care */ + + /* first pattern */ + p[0] = 0; p[1] = 1; p[2] = 0; p[3] = 1; p[4] = 1; + c[0] = 0; c[1] = 0; c[2] = 0; c[3] = 1; c[4] = 1; + sim.add_pattern( p, c ); + + /* second pattern */ + p[0] = 1; p[1] = 0; p[2] = 1; p[3] = 0; p[4] = 1; + c[0] = 0; c[1] = 0; c[2] = 1; c[3] = 0; c[4] = 1; + sim.add_pattern( p, c ); + + CHECK( sim.pack_bits() == false ); + CHECK( sim.num_bits() == 4u ); + + /* third pattern: can be packed into the third bit */ + p[0] = 1; p[1] = 0; p[2] = 1; p[3] = 0; p[4] = 0; + c[0] = 0; c[1] = 1; c[2] = 1; c[3] = 0; c[4] = 0; + sim.add_pattern( p, c ); + + CHECK( sim.pack_bits() == true ); + CHECK( sim.num_bits() == 4u ); + CHECK( ( sim.compute_pi( 0 )._bits[0] & 0x3 ) == 0x1 ); /* x0 = xxx01 -> xx01 */ + CHECK( ( sim.compute_pi( 1 )._bits[0] & 0x7 ) == 0x1 ); /* x1 = 0xx01 -> x001 */ + CHECK( ( sim.compute_pi( 2 )._bits[0] & 0xf ) == 0xd ); /* x2 = 11x01 -> 1101 */ + CHECK( ( sim.compute_pi( 3 )._bits[0] & 0x7 ) == 0x5 ); /* x3 = xx101 -> x101 */ + CHECK( ( sim.compute_pi( 4 )._bits[0] & 0xf ) == 0xd ); /* x4 = x1101 -> 1101 */ + + /* fourth pattern: can be packed into the fourth bit */ + p[0] = 1; p[1] = 0; p[2] = 0; p[3] = 1; p[4] = 0; + c[0] = 1; c[1] = 1; c[2] = 0; c[3] = 1; c[4] = 0; + sim.add_pattern( p, c ); + + /* fifth pattern */ + p[0] = 0; p[1] = 1; p[2] = 0; p[3] = 1; p[4] = 1; + c[0] = 1; c[1] = 0; c[2] = 0; c[3] = 0; c[4] = 1; + sim.add_pattern( p, c ); + + /* sixth pattern: can be packed into the third bit */ + p[0] = 1; p[1] = 0; p[2] = 0; p[3] = 1; p[4] = 0; + c[0] = 1; c[1] = 0; c[2] = 0; c[3] = 0; c[4] = 0; + sim.add_pattern( p, c ); + + CHECK( sim.pack_bits() == true ); + CHECK( sim.num_bits() == 5u ); + CHECK( ( sim.compute_pi( 0 )._bits[0] & 0x1f ) == 0x0d ); /* x0 = 101xx01 -> 01101 */ + CHECK( ( sim.compute_pi( 1 )._bits[0] & 0x0f ) == 0x01 ); /* x1 = xx0x001 -> x0001 */ + CHECK( ( sim.compute_pi( 2 )._bits[0] & 0x0f ) == 0x0d ); /* x2 = xxx1101 -> x1101 */ + CHECK( ( sim.compute_pi( 3 )._bits[0] & 0x0f ) == 0x0d ); /* x3 = xx1x101 -> x1101 */ + CHECK( ( sim.compute_pi( 4 )._bits[0] & 0x1f ) == 0x1d ); /* x4 = x1x1101 -> 11101 */ +}