From e3cfc7cfeffd3b53d0dfd72aa5942d358d4a9751 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 23 Apr 2020 09:40:03 +0200 Subject: [PATCH 1/3] Generic balancing algorithm. --- experiments/sop_balancing.cpp | 78 ++++++ experiments/sop_balancing.json | 216 +++++++++++++++++ include/mockturtle/algorithms/balancing.hpp | 225 ++++++++++++++++++ .../algorithms/balancing/sop_balancing.hpp | 157 ++++++++++++ 4 files changed, 676 insertions(+) create mode 100644 experiments/sop_balancing.cpp create mode 100644 experiments/sop_balancing.json create mode 100644 include/mockturtle/algorithms/balancing.hpp create mode 100644 include/mockturtle/algorithms/balancing/sop_balancing.hpp diff --git a/experiments/sop_balancing.cpp b/experiments/sop_balancing.cpp new file mode 100644 index 000000000..de1a5f0ee --- /dev/null +++ b/experiments/sop_balancing.cpp @@ -0,0 +1,78 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main() +{ + using namespace experiments; + using namespace mockturtle; + + experiment exp( "sop_balancing", "benchmark", "size", "depth", "size 4", "depth 4", "RT 4", "size 6", "depth 6", "RT 6" ); + + sop_rebalancing sop_balancing; + + for ( auto const& benchmark : epfl_benchmarks( ~experiments::hyp ) ) + { + fmt::print( "[i] processing {}\n", benchmark ); + aig_network aig; + lorina::read_aiger( benchmark_path( benchmark ), aiger_reader( aig ) ); + + balancing_params ps; + balancing_stats st4, st6; + + ps.progress = false; + ps.cut_enumeration_ps.cut_size = 4u; + const auto aig4 = balancing( aig, {sop_balancing}, ps, &st4 ); + + ps.cut_enumeration_ps.cut_size = 6u; + const auto aig6 = balancing( aig, {sop_balancing}, ps, &st6 ); + + depth_view daig{aig}; + depth_view daig4{aig4}; + depth_view daig6{aig6}; + + exp( benchmark, + aig.num_gates(), daig.depth(), + aig4.num_gates(), daig4.depth(), + to_seconds( st4.time_total ), + aig6.num_gates(), daig6.depth(), + to_seconds( st6.time_total ) ); + } + + exp.save(); + exp.table(); + + return 0; +} diff --git a/experiments/sop_balancing.json b/experiments/sop_balancing.json new file mode 100644 index 000000000..c87a3438f --- /dev/null +++ b/experiments/sop_balancing.json @@ -0,0 +1,216 @@ +[ + { + "entries": [ + { + "RT 4": 0.0381278, + "RT 6": 0.174033, + "benchmark": "adder", + "depth": 255, + "depth 4": 171, + "depth 6": 105, + "size": 1020, + "size 4": 1397, + "size 6": 2169 + }, + { + "RT 4": 0.1308824, + "RT 6": 0.676073, + "benchmark": "bar", + "depth": 12, + "depth 4": 12, + "depth 6": 12, + "size": 3336, + "size 4": 3336, + "size 6": 3336 + }, + { + "RT 4": 3.1077049, + "RT 6": 23.7085526, + "benchmark": "div", + "depth": 4372, + "depth 4": 2961, + "depth 6": 2305, + "size": 57247, + "size 4": 92425, + "size 6": 141735 + }, + { + "RT 4": 2.622275, + "RT 6": 29.7000635, + "benchmark": "log2", + "depth": 444, + "depth 4": 278, + "depth 6": 258, + "size": 32060, + "size 4": 47838, + "size 6": 72990 + }, + { + "RT 4": 0.1001915, + "RT 6": 0.5553738, + "benchmark": "max", + "depth": 287, + "depth 4": 177, + "depth 6": 143, + "size": 2865, + "size 4": 3767, + "size 6": 4063 + }, + { + "RT 4": 1.9590171, + "RT 6": 10.7824366, + "benchmark": "multiplier", + "depth": 274, + "depth 4": 186, + "depth 6": 162, + "size": 27062, + "size 4": 41206, + "size 6": 55863 + }, + { + "RT 4": 0.4167063, + "RT 6": 8.7527864, + "benchmark": "sin", + "depth": 225, + "depth 4": 137, + "depth 6": 123, + "size": 5416, + "size 4": 8998, + "size 6": 9794 + }, + { + "RT 4": 1.0723049, + "RT 6": 10.675819, + "benchmark": "sqrt", + "depth": 5058, + "depth 4": 4095, + "depth 6": 3977, + "size": 24618, + "size 4": 29731, + "size 6": 36507 + }, + { + "RT 4": 1.3051347, + "RT 6": 7.4452107, + "benchmark": "square", + "depth": 250, + "depth 4": 169, + "depth 6": 130, + "size": 18484, + "size 4": 23727, + "size 6": 23976 + }, + { + "RT 4": 0.7306073, + "RT 6": 2.1840276, + "benchmark": "arbiter", + "depth": 87, + "depth 4": 59, + "depth 6": 38, + "size": 11839, + "size 4": 9151, + "size 6": 7174 + }, + { + "RT 4": 0.0285151, + "RT 6": 0.140805, + "benchmark": "cavlc", + "depth": 16, + "depth 4": 13, + "depth 6": 11, + "size": 693, + "size 4": 732, + "size 6": 757 + }, + { + "RT 4": 0.0131632, + "RT 6": 0.0281519, + "benchmark": "ctrl", + "depth": 10, + "depth 4": 7, + "depth 6": 5, + "size": 174, + "size 4": 145, + "size 6": 140 + }, + { + "RT 4": 0.0126114, + "RT 6": 0.0194634, + "benchmark": "dec", + "depth": 3, + "depth 4": 3, + "depth 6": 3, + "size": 304, + "size 4": 304, + "size 6": 304 + }, + { + "RT 4": 0.0365404, + "RT 6": 0.1353685, + "benchmark": "i2c", + "depth": 20, + "depth 4": 13, + "depth 6": 10, + "size": 1342, + "size 4": 1473, + "size 6": 1564 + }, + { + "RT 4": 0.0095459, + "RT 6": 0.0432792, + "benchmark": "int2float", + "depth": 16, + "depth 4": 12, + "depth 6": 9, + "size": 260, + "size 4": 261, + "size 6": 285 + }, + { + "RT 4": 1.4656049, + "RT 6": 60.3010024, + "benchmark": "mem_ctrl", + "depth": 114, + "depth 4": 78, + "depth 6": 58, + "size": 46836, + "size 4": 61125, + "size 6": 83501 + }, + { + "RT 4": 0.0358097, + "RT 6": 0.1378108, + "benchmark": "priority", + "depth": 250, + "depth 4": 166, + "depth 6": 125, + "size": 978, + "size 4": 1236, + "size 6": 3885 + }, + { + "RT 4": 0.0150206, + "RT 6": 0.0478858, + "benchmark": "router", + "depth": 54, + "depth 4": 25, + "depth 6": 20, + "size": 257, + "size 4": 330, + "size 6": 441 + }, + { + "RT 4": 0.7481873, + "RT 6": 5.5986999, + "benchmark": "voter", + "depth": 70, + "depth 4": 61, + "depth 6": 59, + "size": 13758, + "size 4": 15926, + "size 6": 16129 + } + ], + "version": "8e4a74f" + } +] diff --git a/include/mockturtle/algorithms/balancing.hpp b/include/mockturtle/algorithms/balancing.hpp new file mode 100644 index 000000000..234bc2b45 --- /dev/null +++ b/include/mockturtle/algorithms/balancing.hpp @@ -0,0 +1,225 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 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 Mathias Soeken +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "../utils/node_map.hpp" +#include "../utils/progress_bar.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/topo_view.hpp" +#include "cleanup.hpp" +#include "cut_enumeration.hpp" + +namespace mockturtle +{ + +struct balancing_params +{ + /*! \brief Cut enumeration params. */ + cut_enumeration_params cut_enumeration_ps; + + /*! \brief Show progress. */ + bool progress{false}; + + /*! \brief Be verbose. */ + bool verbose{false}; +}; + +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(); + } +}; + +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&)>; + +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 ), + old_to_new_( ntk ), + levels_( ntk ) + { + } + + Ntk run() + { + /* input arrival times and mapping */ + old_to_new_[ntk_.get_constant( false )] = dest_.get_constant( false ); + levels_[ntk_.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 ); + levels_[ntk_.get_constant( true )] = 0u; + } + ntk_.foreach_pi( [&]( auto const& n ) { + old_to_new_[n] = dest_.create_pi(); + levels_[n] = 0u; + } ); + + 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 ) { + if ( index == size ) + return false; + + pbar( index, index, current_level ); + + if ( ntk_.is_constant( n ) || ntk_.is_pi( n ) ) + { + return true; + } + + signal best_signal; + uint32_t best_level = std::numeric_limits::max(); + uint32_t best_size{}; + for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) + { + if ( cut->size() == 1u ) + continue; + + std::vector> arrival_times( cut->size() ); + std::transform( cut->begin(), cut->end(), arrival_times.begin(), [&]( auto leaf ) -> arrival_time_pair { + auto leaf_node = ntk_.index_to_node( leaf ); + auto leaf_level = levels_[leaf_node]; + return {old_to_new_[leaf_node], leaf_level}; + }); + + 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_signal = cand.f; + best_level = cand.level; + best_size = cand_size; + } + }); + } + levels_[n] = best_level; + old_to_new_[n] = best_signal; + current_level = std::max( current_level, best_level ); + + return true; + } ); + + ntk_.foreach_po( [&]( auto const& f ) { + const auto s = old_to_new_[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_; + Ntk dest_; + balancing_params const& ps_; + balancing_stats& st_; + + node_map, Ntk> old_to_new_; + node_map levels_; +}; + +} // namespace detail + +template +Ntk balancing( Ntk const& ntk, rebalancing_function_t const& rebalancing_fn = {}, balancing_params const& ps = {}, balancing_stats* pst = nullptr ) +{ + 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; +} + +} // namespace mockturtle diff --git a/include/mockturtle/algorithms/balancing/sop_balancing.hpp b/include/mockturtle/algorithms/balancing/sop_balancing.hpp new file mode 100644 index 000000000..597eb35c4 --- /dev/null +++ b/include/mockturtle/algorithms/balancing/sop_balancing.hpp @@ -0,0 +1,157 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 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 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" + +namespace mockturtle +{ + +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>; + +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 + { + auto [and_terms, num_and_gates] = create_function( dest, function, inputs ); + const auto num_gates = num_and_gates + ( and_terms.empty() ? 0u : static_cast( and_terms.size() ) - 1u ); + const auto cand = balanced_tree( dest, and_terms, false ); + + if ( cand.level < best_level || ( cand.level == best_level && num_gates < best_cost ) ) + { + callback( cand, num_gates ); + } + } + +private: + std::pair, uint32_t> create_function( Ntk& dest, kitty::dynamic_truth_table const& func, std::vector> const& arrival_times ) const + { + const auto sop = create_sop_form( func ); + + 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 ) const + { + stopwatch<> t( time_sop ); + if ( auto it = sop_hash_.find( func ); it != sop_hash_.end() ) + { + sop_cache_hits++; + return it->second; + } + else + { + sop_cache_misses++; + return sop_hash_[func] = kitty::isop( func ); // TODO generalize + } + } + +private: + mutable std::unordered_map, kitty::hash> sop_hash_; + +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 From ec8e88fffcf065e4330320b88c9d0b2bea29e2bb Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 23 Apr 2020 10:00:42 +0200 Subject: [PATCH 2/3] Refactoring. --- experiments/sop_balancing.json | 214 ++++++++++++++++++++ include/mockturtle/algorithms/balancing.hpp | 43 ++-- 2 files changed, 229 insertions(+), 28 deletions(-) diff --git a/experiments/sop_balancing.json b/experiments/sop_balancing.json index c87a3438f..921f5a711 100644 --- a/experiments/sop_balancing.json +++ b/experiments/sop_balancing.json @@ -212,5 +212,219 @@ } ], "version": "8e4a74f" + }, + { + "entries": [ + { + "RT 4": 0.0580946, + "RT 6": 0.2390003, + "benchmark": "adder", + "depth": 255, + "depth 4": 171, + "depth 6": 105, + "size": 1020, + "size 4": 1397, + "size 6": 2169 + }, + { + "RT 4": 0.1267833, + "RT 6": 0.5530166, + "benchmark": "bar", + "depth": 12, + "depth 4": 12, + "depth 6": 12, + "size": 3336, + "size 4": 3336, + "size 6": 3336 + }, + { + "RT 4": 3.8091721, + "RT 6": 34.3811929, + "benchmark": "div", + "depth": 4372, + "depth 4": 2961, + "depth 6": 2305, + "size": 57247, + "size 4": 92425, + "size 6": 141735 + }, + { + "RT 4": 3.4374011, + "RT 6": 45.3987995, + "benchmark": "log2", + "depth": 444, + "depth 4": 278, + "depth 6": 258, + "size": 32060, + "size 4": 47838, + "size 6": 72990 + }, + { + "RT 4": 0.1336696, + "RT 6": 0.7755267, + "benchmark": "max", + "depth": 287, + "depth 4": 177, + "depth 6": 143, + "size": 2865, + "size 4": 3767, + "size 6": 4063 + }, + { + "RT 4": 3.2369146, + "RT 6": 15.2005196, + "benchmark": "multiplier", + "depth": 274, + "depth 4": 186, + "depth 6": 162, + "size": 27062, + "size 4": 41206, + "size 6": 55863 + }, + { + "RT 4": 0.6201438, + "RT 6": 13.0162522, + "benchmark": "sin", + "depth": 225, + "depth 4": 137, + "depth 6": 123, + "size": 5416, + "size 4": 8998, + "size 6": 9794 + }, + { + "RT 4": 1.5366326, + "RT 6": 14.8429438, + "benchmark": "sqrt", + "depth": 5058, + "depth 4": 4095, + "depth 6": 3977, + "size": 24618, + "size 4": 29731, + "size 6": 36507 + }, + { + "RT 4": 1.8785287, + "RT 6": 11.0540579, + "benchmark": "square", + "depth": 250, + "depth 4": 169, + "depth 6": 130, + "size": 18484, + "size 4": 23727, + "size 6": 23976 + }, + { + "RT 4": 0.5706227, + "RT 6": 2.3630905, + "benchmark": "arbiter", + "depth": 87, + "depth 4": 59, + "depth 6": 38, + "size": 11839, + "size 4": 9151, + "size 6": 7174 + }, + { + "RT 4": 0.0298407, + "RT 6": 0.192832, + "benchmark": "cavlc", + "depth": 16, + "depth 4": 13, + "depth 6": 11, + "size": 693, + "size 4": 732, + "size 6": 757 + }, + { + "RT 4": 0.0099502, + "RT 6": 0.0359301, + "benchmark": "ctrl", + "depth": 10, + "depth 4": 7, + "depth 6": 5, + "size": 174, + "size 4": 145, + "size 6": 140 + }, + { + "RT 4": 0.0103363, + "RT 6": 0.0259742, + "benchmark": "dec", + "depth": 3, + "depth 4": 3, + "depth 6": 3, + "size": 304, + "size 4": 304, + "size 6": 304 + }, + { + "RT 4": 0.0435584, + "RT 6": 0.2283724, + "benchmark": "i2c", + "depth": 20, + "depth 4": 13, + "depth 6": 10, + "size": 1342, + "size 4": 1473, + "size 6": 1564 + }, + { + "RT 4": 0.0099333, + "RT 6": 0.0646255, + "benchmark": "int2float", + "depth": 16, + "depth 4": 12, + "depth 6": 9, + "size": 260, + "size 4": 261, + "size 6": 285 + }, + { + "RT 4": 2.2369787, + "RT 6": 80.8950253, + "benchmark": "mem_ctrl", + "depth": 114, + "depth 4": 78, + "depth 6": 58, + "size": 46836, + "size 4": 61125, + "size 6": 83501 + }, + { + "RT 4": 0.0457404, + "RT 6": 0.218961, + "benchmark": "priority", + "depth": 250, + "depth 4": 166, + "depth 6": 125, + "size": 978, + "size 4": 1236, + "size 6": 3885 + }, + { + "RT 4": 0.0118288, + "RT 6": 0.0693483, + "benchmark": "router", + "depth": 54, + "depth 4": 25, + "depth 6": 20, + "size": 257, + "size 4": 330, + "size 6": 441 + }, + { + "RT 4": 1.0713156, + "RT 6": 8.159181, + "benchmark": "voter", + "depth": 70, + "depth 4": 61, + "depth 6": 59, + "size": 13758, + "size 4": 15926, + "size 6": 16129 + } + ], + "version": "e3cfc7c" } ] diff --git a/include/mockturtle/algorithms/balancing.hpp b/include/mockturtle/algorithms/balancing.hpp index 234bc2b45..5c2bf9dd7 100644 --- a/include/mockturtle/algorithms/balancing.hpp +++ b/include/mockturtle/algorithms/balancing.hpp @@ -123,16 +123,13 @@ struct balancing_impl Ntk run() { /* input arrival times and mapping */ - old_to_new_[ntk_.get_constant( false )] = dest_.get_constant( false ); - levels_[ntk_.get_constant( false )] = 0u; + 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 ); - levels_[ntk_.get_constant( true )] = 0u; + 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(); - levels_[n] = 0u; + old_to_new_[n] = {dest_.create_pi(), 0u}; } ); stopwatch<> t( st_.time_total ); @@ -142,49 +139,39 @@ struct balancing_impl 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 ) { - if ( index == size ) - return false; - pbar( index, index, current_level ); if ( ntk_.is_constant( n ) || ntk_.is_pi( n ) ) { - return true; + return; } - signal best_signal; - uint32_t best_level = std::numeric_limits::max(); + 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 ) + { continue; + } std::vector> arrival_times( cut->size() ); - std::transform( cut->begin(), cut->end(), arrival_times.begin(), [&]( auto leaf ) -> arrival_time_pair { - auto leaf_node = ntk_.index_to_node( leaf ); - auto leaf_level = levels_[leaf_node]; - return {old_to_new_[leaf_node], leaf_level}; - }); + std::transform( cut->begin(), cut->end(), arrival_times.begin(), [&]( auto leaf ) { return old_to_new_[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 ) ) + 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_signal = cand.f; - best_level = cand.level; + best = cand; best_size = cand_size; } }); } - levels_[n] = best_level; - old_to_new_[n] = best_signal; - current_level = std::max( current_level, best_level ); - - return true; + 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]; + const auto s = old_to_new_[f].f; dest_.create_po( ntk_.is_complemented( f ) ? dest_.create_not( s ) : s ); } ); @@ -198,7 +185,7 @@ struct balancing_impl balancing_params const& ps_; balancing_stats& st_; - node_map, Ntk> old_to_new_; + node_map, Ntk> old_to_new_; node_map levels_; }; From 0e718bc34e1d72148fdc9cd81684154bbbf05c06 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 30 Apr 2020 16:15:42 +0200 Subject: [PATCH 3/3] Changes, fixes, docs. --- docs/algorithms/balancing.rst | 25 ++ docs/changelog.rst | 1 + docs/index.rst | 1 + experiments/sop_balancing.cpp | 11 +- experiments/sop_balancing.json | 252 ++++++++++++++++++ include/mockturtle/algorithms/balancing.hpp | 70 +++-- .../algorithms/balancing/sop_balancing.hpp | 6 + test/algorithms/balancing.cpp | 24 ++ 8 files changed, 369 insertions(+), 21 deletions(-) create mode 100644 docs/algorithms/balancing.rst create mode 100644 test/algorithms/balancing.cpp diff --git a/docs/algorithms/balancing.rst b/docs/algorithms/balancing.rst new file mode 100644 index 000000000..3eca4e11b --- /dev/null +++ b/docs/algorithms/balancing.rst @@ -0,0 +1,25 @@ +Balancing +--------- + +**Header:** ``mockturtle/algorithms/balancing.hpp`` + +Parameters and Statistics +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. doxygenstruct:: mockturtle::balancing_params + :members: + +.. doxygenstruct:: mockturtle::balancing_stats + :members: + +Algorithm +~~~~~~~~~ + +.. doxygenfunction:: mockturtle::balancing + +Rebalancing engines +~~~~~~~~~~~~~~~~~~~ + +**Header:** ``mockturtle/algorithms/balancing/sop_balancing.hpp`` + +.. doxygenstruct:: mockturtle::sop_balancing diff --git a/docs/changelog.rst b/docs/changelog.rst index dd47d3dac..939bf5068 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -28,6 +28,7 @@ v0.2 (not yet released) - XAG optimization by linear resynthesis (`linear_resynthesis_optimization`, `exact_linear_resynthesis_optimization`) `#296 `_ - Davio decomposition (`positive_davio_decomposition`, `positive_davio_decomposition`) `#308 `_ - Collapse network into single node per output network `#309 `_ + - Generic balancing algorithm `#340 `_ * Views: - Assign names to signals and outputs (`names_view`) `#181 `_ `#184 `_ - Creates a CNF while creating a network (`cnf_view`) `#181 `_ `#184 `_ diff --git a/docs/index.rst b/docs/index.rst index 38ccf14ac..f471ca144 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,6 +33,7 @@ Welcome to mockturtle's documentation! algorithms/node_resynthesis algorithms/cut_rewriting algorithms/refactoring + algorithms/balancing algorithms/mig_algebraic_rewriting algorithms/akers_synthesis algorithms/resubstitution diff --git a/experiments/sop_balancing.cpp b/experiments/sop_balancing.cpp index de1a5f0ee..0a970b11d 100644 --- a/experiments/sop_balancing.cpp +++ b/experiments/sop_balancing.cpp @@ -39,7 +39,7 @@ int main() using namespace experiments; using namespace mockturtle; - experiment exp( "sop_balancing", "benchmark", "size", "depth", "size 4", "depth 4", "RT 4", "size 6", "depth 6", "RT 6" ); + experiment exp( "sop_balancing", "benchmark", "size", "depth", "size 4", "depth 4", "RT 4", "cec 4", "size 6", "depth 6", "RT 6", "cec 6" ); sop_rebalancing sop_balancing; @@ -52,7 +52,7 @@ int main() balancing_params ps; balancing_stats st4, st6; - ps.progress = false; + ps.progress = true; ps.cut_enumeration_ps.cut_size = 4u; const auto aig4 = balancing( aig, {sop_balancing}, ps, &st4 ); @@ -63,12 +63,15 @@ int main() depth_view daig4{aig4}; depth_view daig6{aig6}; + const auto cec4 = abc_cec( aig4, benchmark ); + const auto cec6 = abc_cec( aig6, benchmark ); + exp( benchmark, aig.num_gates(), daig.depth(), aig4.num_gates(), daig4.depth(), - to_seconds( st4.time_total ), + to_seconds( st4.time_total ), cec4, aig6.num_gates(), daig6.depth(), - to_seconds( st6.time_total ) ); + to_seconds( st6.time_total ), cec6 ); } exp.save(); diff --git a/experiments/sop_balancing.json b/experiments/sop_balancing.json index 921f5a711..c18536983 100644 --- a/experiments/sop_balancing.json +++ b/experiments/sop_balancing.json @@ -426,5 +426,257 @@ } ], "version": "e3cfc7c" + }, + { + "entries": [ + { + "RT 4": 0.073122937, + "RT 6": 0.218030107, + "benchmark": "adder", + "cec 4": true, + "cec 6": true, + "depth": 255, + "depth 4": 171, + "depth 6": 105, + "size": 1020, + "size 4": 1397, + "size 6": 2195 + }, + { + "RT 4": 0.136484074, + "RT 6": 0.547137525, + "benchmark": "bar", + "cec 4": true, + "cec 6": true, + "depth": 12, + "depth 4": 12, + "depth 6": 12, + "size": 3336, + "size 4": 3336, + "size 6": 3336 + }, + { + "RT 4": 4.257333019, + "RT 6": 17.696381612, + "benchmark": "div", + "cec 4": true, + "cec 6": true, + "depth": 4372, + "depth 4": 2961, + "depth 6": 2305, + "size": 57247, + "size 4": 92426, + "size 6": 141220 + }, + { + "RT 4": 3.61922098, + "RT 6": 11.383245314, + "benchmark": "log2", + "cec 4": true, + "cec 6": true, + "depth": 444, + "depth 4": 278, + "depth 6": 258, + "size": 32060, + "size 4": 47817, + "size 6": 73064 + }, + { + "RT 4": 0.131254806, + "RT 6": 0.574256591, + "benchmark": "max", + "cec 4": true, + "cec 6": true, + "depth": 287, + "depth 4": 177, + "depth 6": 143, + "size": 2865, + "size 4": 3767, + "size 6": 4058 + }, + { + "RT 4": 3.25733943, + "RT 6": 9.059335842, + "benchmark": "multiplier", + "cec 4": true, + "cec 6": true, + "depth": 274, + "depth 4": 186, + "depth 6": 162, + "size": 27062, + "size 4": 41123, + "size 6": 55742 + }, + { + "RT 4": 0.645001867, + "RT 6": 2.185834847, + "benchmark": "sin", + "cec 4": true, + "cec 6": true, + "depth": 225, + "depth 4": 137, + "depth 6": 123, + "size": 5416, + "size 4": 8983, + "size 6": 9789 + }, + { + "RT 4": 2.004371285, + "RT 6": 8.081080689, + "benchmark": "sqrt", + "cec 4": true, + "cec 6": true, + "depth": 5058, + "depth 4": 4095, + "depth 6": 3977, + "size": 24618, + "size 4": 29732, + "size 6": 36499 + }, + { + "RT 4": 1.936625365, + "RT 6": 5.384671257, + "benchmark": "square", + "cec 4": true, + "cec 6": true, + "depth": 250, + "depth 4": 169, + "depth 6": 130, + "size": 18484, + "size 4": 23730, + "size 6": 23979 + }, + { + "RT 4": 0.551399279, + "RT 6": 2.210378884, + "benchmark": "arbiter", + "cec 4": true, + "cec 6": true, + "depth": 87, + "depth 4": 59, + "depth 6": 38, + "size": 11839, + "size 4": 9151, + "size 6": 7077 + }, + { + "RT 4": 0.045477736, + "RT 6": 0.225608701, + "benchmark": "cavlc", + "cec 4": true, + "cec 6": true, + "depth": 16, + "depth 4": 13, + "depth 6": 11, + "size": 693, + "size 4": 732, + "size 6": 761 + }, + { + "RT 4": 0.013370639, + "RT 6": 0.049519092, + "benchmark": "ctrl", + "cec 4": true, + "cec 6": true, + "depth": 10, + "depth 4": 7, + "depth 6": 5, + "size": 174, + "size 4": 138, + "size 6": 133 + }, + { + "RT 4": 0.010606386, + "RT 6": 0.030078157, + "benchmark": "dec", + "cec 4": true, + "cec 6": true, + "depth": 3, + "depth 4": 3, + "depth 6": 3, + "size": 304, + "size 4": 304, + "size 6": 304 + }, + { + "RT 4": 0.055774981, + "RT 6": 0.245543742, + "benchmark": "i2c", + "cec 4": true, + "cec 6": true, + "depth": 20, + "depth 4": 13, + "depth 6": 10, + "size": 1342, + "size 4": 1478, + "size 6": 1572 + }, + { + "RT 4": 0.017948877, + "RT 6": 0.065679967, + "benchmark": "int2float", + "cec 4": true, + "cec 6": true, + "depth": 16, + "depth 4": 12, + "depth 6": 9, + "size": 260, + "size 4": 260, + "size 6": 280 + }, + { + "RT 4": 2.480335609, + "RT 6": 14.440610276, + "benchmark": "mem_ctrl", + "cec 4": true, + "cec 6": true, + "depth": 114, + "depth 4": 78, + "depth 6": 58, + "size": 46836, + "size 4": 61125, + "size 6": 83377 + }, + { + "RT 4": 0.052432179, + "RT 6": 0.194768, + "benchmark": "priority", + "cec 4": true, + "cec 6": true, + "depth": 250, + "depth 4": 166, + "depth 6": 125, + "size": 978, + "size 4": 1222, + "size 6": 3882 + }, + { + "RT 4": 0.018377905, + "RT 6": 0.05466485, + "benchmark": "router", + "cec 4": true, + "cec 6": true, + "depth": 54, + "depth 4": 25, + "depth 6": 20, + "size": 257, + "size 4": 319, + "size 6": 431 + }, + { + "RT 4": 1.185177933, + "RT 6": 4.182636549, + "benchmark": "voter", + "cec 4": true, + "cec 6": true, + "depth": 70, + "depth 4": 61, + "depth 6": 59, + "size": 13758, + "size 4": 15926, + "size 6": 16131 + } + ], + "version": "8d8dbb4" } ] diff --git a/include/mockturtle/algorithms/balancing.hpp b/include/mockturtle/algorithms/balancing.hpp index 5c2bf9dd7..f04eb038d 100644 --- a/include/mockturtle/algorithms/balancing.hpp +++ b/include/mockturtle/algorithms/balancing.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,8 @@ namespace mockturtle { +/*! \brief Parameters for balancing. + */ struct balancing_params { /*! \brief Cut enumeration params. */ @@ -63,6 +66,8 @@ struct balancing_params bool verbose{false}; }; +/*! \brief Statistics for balancing. + */ struct balancing_stats { /*! \brief Total run-time. */ @@ -114,22 +119,23 @@ struct balancing_impl : ntk_( ntk ), rebalancing_fn_( rebalancing_fn ), ps_( ps ), - st_( st ), - old_to_new_( ntk ), - levels_( ntk ) + 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}; + 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}; + 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}; + old_to_new[n] = {dest.create_pi(), 0u}; } ); stopwatch<> t( st_.time_total ); @@ -150,15 +156,15 @@ struct balancing_impl uint32_t best_size{}; for ( auto& cut : cuts.cuts( ntk_.node_to_index( n ) ) ) { - if ( cut->size() == 1u ) + 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_[leaf]; }); + 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 ) { + 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; @@ -166,34 +172,64 @@ struct balancing_impl } }); } - old_to_new_[n] = best; + 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 ); + const auto s = old_to_new[f].f; + dest.create_po( ntk_.is_complemented( f ) ? dest.create_not( s ) : s ); } ); - return cleanup_dangling( dest_ ); + return cleanup_dangling( dest ); } private: Ntk const& ntk_; rebalancing_function_t const& rebalancing_fn_; - Ntk dest_; balancing_params const& ps_; balancing_stats& st_; - - node_map, Ntk> old_to_new_; - node_map levels_; }; } // 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. + * + \verbatim embed:rst + + Example + + .. code-block:: c++ + + const auto aig = ...; + + sop_balancing balance_fn; + balance_params ps; + ps.cut_enumeration_ps.cut_size = 6u; + const auto balanced_aig = balance( 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(); diff --git a/include/mockturtle/algorithms/balancing/sop_balancing.hpp b/include/mockturtle/algorithms/balancing/sop_balancing.hpp index 597eb35c4..8e2a165a2 100644 --- a/include/mockturtle/algorithms/balancing/sop_balancing.hpp +++ b/include/mockturtle/algorithms/balancing/sop_balancing.hpp @@ -65,6 +65,12 @@ struct arrival_time_compare template using arrival_time_queue = std::priority_queue, std::vector>, arrival_time_compare>; +/*! \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 { diff --git a/test/algorithms/balancing.cpp b/test/algorithms/balancing.cpp new file mode 100644 index 000000000..e49dc447c --- /dev/null +++ b/test/algorithms/balancing.cpp @@ -0,0 +1,24 @@ +#include + +#include +#include +#include +#include + +using namespace mockturtle; + +TEST_CASE( "Rebalance AND chain in AIG", "[balancing]" ) +{ + aig_network aig; + const auto a = aig.create_pi(); + const auto b = aig.create_pi(); + const auto c = aig.create_pi(); + const auto d = aig.create_pi(); + + aig.create_po( aig.create_and( a, aig.create_and( b, aig.create_and( c, d ) ) ) ); + + CHECK( depth_view{aig}.depth() == 3u ); + + aig = balancing( aig, {sop_rebalancing{}} ); + CHECK( depth_view{aig}.depth() == 2u ); +}