diff --git a/docs/changelog.rst b/docs/changelog.rst index ca2051122..f9542227e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,8 @@ v0.3 (not yet released) - Read GENLIB files using *lorina* (`genlib_reader`) `#421 `_ * Algorithms: - Logic resynthesis engines for MIGs (`mig_resyn_engines` `#414 `_) and AIGs/XAGs (`xag_resyn_engines` `#425 `_) +* Utils + - Manipulate windows with network data types (`clone_subnetwork` and `insert_ntk`) `#451 `_ v0.2 (February 16, 2021) ------------------------ diff --git a/docs/index.rst b/docs/index.rst index 42cbd0a3f..e42a51e92 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -86,6 +86,7 @@ Welcome to mockturtle's documentation! :caption: Utilities utils/util_data_structures + utils/util_functions Indices and tables ================== diff --git a/docs/utils/util_functions.rst b/docs/utils/util_functions.rst new file mode 100644 index 000000000..07dfbc089 --- /dev/null +++ b/docs/utils/util_functions.rst @@ -0,0 +1,11 @@ +Utility functions +----------------- + +Manipulate windows with network data types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Header:** ``mockturtle/utils/network_utils.hpp`` + +.. doxygenfunction:: mockturtle::clone_subnetwork( Ntk const&, std::vector const&, std::vector const&, std::vector const&, SubNtk& ) + +.. doxygenfunction:: mockturtle::insert_ntk( Ntk&, BeginIter, EndIter, SubNtk const&, Fn&& ) \ No newline at end of file diff --git a/include/mockturtle/utils/debugging_utils.hpp b/include/mockturtle/utils/debugging_utils.hpp index 429346abc..e6e3f2dda 100644 --- a/include/mockturtle/utils/debugging_utils.hpp +++ b/include/mockturtle/utils/debugging_utils.hpp @@ -32,6 +32,11 @@ #pragma once +#include "../algorithms/simulation.hpp" +#include "../views/topo_view.hpp" + +#include + #include #include @@ -456,6 +461,28 @@ bool check_fanouts( Ntk const& ntk ) } return true; -} +} + +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 */ diff --git a/include/mockturtle/utils/network_utils.hpp b/include/mockturtle/utils/network_utils.hpp new file mode 100644 index 000000000..3c84a0453 --- /dev/null +++ b/include/mockturtle/utils/network_utils.hpp @@ -0,0 +1,238 @@ +/* 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 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 )] ); + }); +} + +} /* mockturtle */ diff --git a/test/utils/network_utils.cpp b/test/utils/network_utils.cpp new file mode 100644 index 000000000..72879b0ca --- /dev/null +++ b/test/utils/network_utils.cpp @@ -0,0 +1,102 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace mockturtle; + +TEST_CASE( "clone a window, optimize it, and insert it back", "[network_utils]" ) +{ + using node = aig_network::node; + using signal = aig_network::signal; + + /* original network */ + aig_network aig; + auto const x0 = aig.create_pi(); + auto const x1 = aig.create_pi(); + auto const x2 = aig.create_pi(); + auto const x3 = aig.create_pi(); + auto const a = aig.create_and( x0, x1 ); + auto const b = aig.create_and( x2, x3 ); + auto const t0 = aig.create_and( !a, b ); + auto const t1 = aig.create_and( a, !b ); + auto const t2 = aig.create_or( t0, t1 ); // a XOR b + auto const t3 = aig.create_and( a, b ); + aig.create_po( t2 ); + aig.create_po( t3 ); + + /* create a window*/ + std::vector inputs{aig.get_node( a ), aig.get_node( b )}; + std::vector outputs{t2, t3}; + std::vector gates{aig.get_node( t0 ), aig.get_node( t1 ), aig.get_node( t2 ), aig.get_node( t3 )}; + + aig_network win; + clone_subnetwork( aig, inputs, outputs, gates, win ); + + CHECK( win.num_pis() == 2u ); + CHECK( win.num_pos() == 2u ); + CHECK( win.num_gates() == 4u ); + + /* optimize the window */ + using TT = kitty::static_truth_table<2>; + using ResynEngine = xag_resyn_engine>; + typename ResynEngine::stats engine_st; + typename ResynEngine::params engine_ps; + + default_simulator sim; + auto tts = simulate_nodes( win, sim ); + win.foreach_po( [&]( auto const& f, auto const i ){ + if ( i == 0 ) // try only the first PO + { + auto const root = win.get_node( f ); + std::vector div_signals; + + engine_ps.max_size = 2u; + ResynEngine engine( tts[root], ~tts[win.get_constant( false )], tts, engine_st, engine_ps ); + + win.incr_trav_id(); + win.set_visited( root, win.trav_id() ); // exclude root + win.foreach_fanin( root, [&]( auto const& fi ){ + win.set_visited( win.get_node( fi ), win.trav_id() ); // exclude MFFC + }); + win.foreach_node( [&]( auto const& n ){ + if ( win.visited( n ) != win.trav_id() ) + { + engine.add_divisor( n ); + div_signals.emplace_back( win.make_signal( n ) ); + } + }); + + auto const il = engine(); + if ( il ) + { + insert( win, div_signals.begin(), div_signals.end(), *il, [&]( auto const& s ){ + win.substitute_node( root, s ); + }); + } + } + }); + + CHECK( win.num_gates() == 3u ); + CHECK( check_window_equivalence( aig, inputs, outputs, gates, win ) ); + + /* insert the window back */ + std::vector input_signals{a, b}; + uint32_t counter = 0u; + insert_ntk( aig, input_signals.begin(), input_signals.end(), win, [&]( signal const& _new ) + { + auto const _old = outputs.at( counter++ ); + if ( _old != _new ) + { + aig.substitute_node( aig.get_node( _old ), aig.is_complemented( _old ) ? !_new : _new ); + } + }); + + CHECK( aig.num_gates() == 5u ); +} +