diff --git a/docs/changelog.rst b/docs/changelog.rst index ce67dd471..15beb1ca4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -35,6 +35,8 @@ v0.2 (not yet released) * Views: - Assign names to signals and outputs (`names_view`) `#181 `_ `#184 `_ - Creates a CNF while creating a network (`cnf_view`) `#274 `_ + - Revised window view (`window_view`) `#381 `_ + - In-place and out-of-place color view (`color_view`, `out_of_place_color_view`) `#381 `_ * I/O: - Write networks to DIMACS files for CNF (`write_dimacs`) `#146 `_ - Read BLIF files using *lorina* (`blif_reader`) `#167 `_ @@ -63,6 +65,8 @@ v0.2 (not yet released) - Random logic networks for XAGs (`random_logic_generator`) `#366 `_ * Properties: - Costs based on multiplicative complexity (`multiplicative_complexity` and `multiplicative_complexity_depth`) `#170 `_ +* Utils: + - Computing windows and manipulating cuts (`create_window_impl`, `collect_nodes`, `collect_inputs`, `collect_outputs`, `expand0_towards_tfi`, `expand_towards_tfi`, `expand_towards_tfo`, `levelized_expand_towards_tfo`) `#381 `_ v0.1 (March 31, 2019) --------------------- diff --git a/docs/implementations.rst b/docs/implementations.rst index 017dc1b5e..fe57a426e 100644 --- a/docs/implementations.rst +++ b/docs/implementations.rst @@ -284,3 +284,17 @@ All network implementations are located in `mockturtle/networks/`: +--------------------------------+-------------+-------------+-------------+-------------+-----------------+ | ``get_output_name`` | | | | | | +--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| | *Coloring methods* | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``new_color`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``current_color`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``color`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``paint`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``eval_color`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ +| ``eval_fanins_color`` | | | | | | ++--------------------------------+-------------+-------------+-------------+-------------+-----------------+ diff --git a/docs/views.rst b/docs/views.rst index cedac7a73..a60cc6469 100644 --- a/docs/views.rst +++ b/docs/views.rst @@ -89,3 +89,14 @@ algorithm. Several views are implemented in mockturtle. .. doxygenclass:: mockturtle::cnf_view :members: + +`color_view`: Manages traversal IDs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Header:** ``mockturtle/views/color_view.hpp`` + +.. doxygenclass:: mockturtle::color_view + :members: + + .. doxygenclass:: mockturtle::out_of_place_color_view + :members: diff --git a/include/mockturtle/algorithms/dont_cares.hpp b/include/mockturtle/algorithms/dont_cares.hpp index d20c1c69e..6ba25b838 100644 --- a/include/mockturtle/algorithms/dont_cares.hpp +++ b/include/mockturtle/algorithms/dont_cares.hpp @@ -44,6 +44,7 @@ #include "../views/fanout_view.hpp" #include "../views/topo_view.hpp" #include "../views/window_view.hpp" +#include "../views/color_view.hpp" #include #include @@ -74,8 +75,10 @@ kitty::dynamic_truth_table satisfiability_dont_cares( Ntk const& ntk, std::vecto fanout_view fanout_ntk{ntk}; fanout_ntk.clear_visited(); + color_view color_ntk{fanout_ntk}; - window_view> window_ntk{fanout_ntk, extended_leaves, leaves, false}; + 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 ); @@ -110,8 +113,10 @@ kitty::dynamic_truth_table observability_dont_cares( Ntk const& ntk, node c { fanout_view fanout_ntk{ntk}; fanout_ntk.clear_visited(); + color_view color_ntk{fanout_ntk}; - window_view> window_ntk{fanout_ntk, leaves, roots, false}; + 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 ); diff --git a/include/mockturtle/interface.hpp b/include/mockturtle/interface.hpp index 6d97e4da2..54df39655 100644 --- a/include/mockturtle/interface.hpp +++ b/include/mockturtle/interface.hpp @@ -916,17 +916,69 @@ class network final /*! \brief Sets the visited value of a node. */ void set_visited( node const& n, uint32_t v ) const; - /*! \brief An id that can be used as a visited flag. - * - * By using the traversal is, one can reuse multiple visited flags over - * several levels. - */ + /*! \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 beginregion Color values + /* Color values offer a more recent and flexible mechanism to manage + and manipulate traversal ids. */ + + /*! \brief Returns a new color and increases the current color. */ + uint32_t new_color() const; + + /*! \brief Returns the current color. */ + uint32_t current_color() const; + + /*! \brief Resets all nodes colors to value `color`. */ + void clear_colors( uint32_t color = 0 ) const; + + /*! \brief Returns the color of a node. */ + auto color( node const& n ) const; + + /*! \brief Returns the color of a node. */ + auto color( signal const& n ) const; + + /*! \brief Assigns the current color to a node. */ + void paint( node const& n ) const; + + /*! \brief Assigns `color` to a node. */ + void paint( node const& n, uint32_t color ) const; + + /*! \brief Copies the color from `other` to `n`. */ + void paint( node const& n, node const& other ) const; + + /*! \brief Evaluates a predicate on the color of a node. + * + * The predicate `pred` is any callable that must have the signature + * ``bool(color_type)``, where `color_type` is the + * implementation-dependent type returned by the method `color`. + */ + template + bool eval_color( node const& n, Pred&& pred ) const; + + /*! \brief Evaluates a predicate on the colors of two nodes. + * + * The predicate `pred` is any callable that must have the signature + * ``bool(color_type,color_type)``, where `color_type` is the + * implementation-dependent type returned by the method `color`. + */ + template + bool eval_color( node const& a, node const& b, Pred&& pred ) const; + + /*! \brief Evaluates a predicate on the colors of the fanins of a node. + * + * The predicate `pred` is any callable that must have the signature + * ``bool(color_type)``, where `color_type` is the + * implementation-dependent type returned by the method `color`. + */ + template + bool eval_fanins_color( node const& n, Pred&& pred ) const; +#pragma endregion + #pragma region Signal naming /*! \brief Checks if a signal has a name. */ bool has_name( signal const& s ) const; @@ -945,7 +997,7 @@ class network final /*! \brief Returns the name of an output signal. */ std::string get_output_name( uint32_t index ) const; -#end endregion +#pragma endregion #pragma region General methods /*! \brief Returns network events object. diff --git a/include/mockturtle/traits.hpp b/include/mockturtle/traits.hpp index bc0cb995e..e2c54f41e 100644 --- a/include/mockturtle/traits.hpp +++ b/include/mockturtle/traits.hpp @@ -1943,6 +1943,111 @@ 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 + /*! \brief SFINAE based on iterator type (for compute functions). */ template diff --git a/include/mockturtle/utils/window_utils.hpp b/include/mockturtle/utils/window_utils.hpp new file mode 100644 index 000000000..d19027440 --- /dev/null +++ b/include/mockturtle/utils/window_utils.hpp @@ -0,0 +1,897 @@ +/* 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 window_utils.hpp + \brief Utilities to collect small-scale sets of nodes + + \author Heinz Riener +*/ + +#pragma once + +#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 ){ + 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. + * + * **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; + }); + } + + 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; +} + +/*! \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. + * + * Uses the current color to mark nodes. Only nodes not painted with + * the current color are considered for expanding 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; + + /* mark all inputs */ + for ( auto const& i : inputs ) + { + ntk.paint( i ); + } + + /* 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 = false; + + /* repeat expansion towards TFI until a fix-point is reached */ + bool changed = true; + std::vector new_inputs; + while ( changed ) + { + changed = false; + trivial_cut = true; + + for ( auto it = std::begin( inputs ); it != std::end( inputs ); ) + { + /* count how many fanins are not 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 ); + trivial_cut = false; + + if ( ntk.eval_color( n, [&ntk]( auto c ){ return c != ntk.current_color(); } ) ) + { + ++count_fanin_outside; + ep = n; + } + }); + + /* if only one fanin is not in the cut, then the input expansion + can be done wihout affecting the cut size */ + if ( count_fanin_outside == 1u ) + { + /* expand the cut */ + assert( ep ); + it = inputs.erase( it ); + new_inputs.push_back( *ep ); + changed = true; + } + else + { + ++it; + } + } + + std::copy( std::begin( new_inputs ), std::end( new_inputs ), + std::back_inserter( inputs ) ); + new_inputs.clear(); + } + + 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_not_trivial( 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; + }); + } + + /* select the fanin with maximum reference count; if two fanins have equal reference count, select the one with more fanouts */ + std::pair best_fanin; + 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`. + * + * 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}; + + ntk.new_color(); + if ( expand0_towards_tfi( ntk, inputs ) ) + { + return; + } + + std::vector 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 ); + if ( inputs.size() <= input_limit ) + { + best_cut = inputs; + } + + iterations = inputs.size() > input_limit ? iterations + 1 : 0; + } + + inputs = best_cut; +} + +/*! \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; + } + } +} + +/*! \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 ) +{ + 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?) */ + std::vector> levels; + levels.resize( ntk.depth() + 1 ); + + /* list of indices of used levels (avoid iterating over all levels) */ + std::vector used; + + /* remove all nodes */ + nodes.clear(); + + /* 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 ); + 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 ); + } + } + std::sort( std::begin( used ), std::end( used ) ); + + for ( uint32_t index = 0u; index < used.size(); ++index ) + { + std::vector& level = levels.at( 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 wihout 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 ); + 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 ); + } + } + + return true; + }); + } + level.clear(); + } +} + +/*! \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() ) + { + } + + std::optional run( node const& pivot, uint32_t cut_size ) + { + /* find a reconvergence from the pivot and collect the nodes */ + std::optional> nodes; + if ( !( nodes = identify_reconvergence( pivot, NUM_ITERATIONS ) ) ) + { + /* 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 ) + { + return std::nullopt; + } + + /* expand the nodes towards the TFI */ + expand_towards_tfi( ntk, inputs, cut_size ); + assert( inputs.size() <= cut_size ); + + /* expand the nodes towards the TFO */ + levelized_expand_towards_tfo( ntk, inputs, *nodes ); + if ( nodes->empty() ) + { + return std::nullopt; + } + + /* collect the nodes with fanout outside of nodes */ + std::vector outputs = collect_outputs( ntk, inputs, *nodes, refs ); + assert( outputs.size() > 0u ); + + /* top. sort nodes */ + std::sort( std::begin( inputs ), std::end( inputs ) ); + std::sort( std::begin( *nodes ), std::end( *nodes ) ); + + return window{inputs, *nodes, outputs}; + } + +protected: + std::optional> identify_reconvergence( node const& pivot, uint64_t num_iterations ) + { + 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( *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 ) + { + std::optional meet; + ntk.foreach_fanin( n, [&]( signal const& fi ){ + node const& fi_node = ntk.get_node( fi ); + if ( ntk.is_constant( fi_node ) || ntk.is_ci( fi_node ) ) + { + return true; /* next */ + } + + 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; +}; /* create_window_impl */ + +} /* namespace mockturtle */ diff --git a/include/mockturtle/views/color_view.hpp b/include/mockturtle/views/color_view.hpp new file mode 100644 index 000000000..c9a229209 --- /dev/null +++ b/include/mockturtle/views/color_view.hpp @@ -0,0 +1,244 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2020 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 +*/ + +#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->data.trav_id; + // return ++value; + } + + /*! \brief Returns the current color */ + uint32_t current_color() const + { + return this->_storage->data.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 */ + +} /* mockturtle */ diff --git a/include/mockturtle/views/window_view.hpp b/include/mockturtle/views/window_view.hpp index 3d7a61ebb..c938b133d 100644 --- a/include/mockturtle/views/window_view.hpp +++ b/include/mockturtle/views/window_view.hpp @@ -32,21 +32,43 @@ #pragma once +#include "../traits.hpp" +#include "../networks/detail/foreach.hpp" +#include "../utils/window_utils.hpp" +#include "immutable_view.hpp" #include +#include #include +#include #include #include -#include -#include - -#include "../traits.hpp" -#include "../networks/detail/foreach.hpp" -#include "immutable_view.hpp" namespace mockturtle { -/*! \brief Implements an isolated view on a window in a network. */ +/*! \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 one new API method: + * 1.) `belongs_to`: takes a node (or a signal) and returns true if and + * only if the corresponding node belongs to the window + */ template class window_view : public immutable_view { @@ -56,210 +78,200 @@ class window_view : public immutable_view using signal = typename Ntk::signal; public: - explicit window_view( Ntk const& ntk, std::vector const& leaves, std::vector const& pivots, bool auto_extend = true ) + 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 ) { - 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_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); - - this->incr_trav_id(); - - /* 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; - } + construct( inputs, gates ); + } - /* primary inputs */ - for ( auto const& leaf : leaves ) - { - if ( this->visited( leaf ) == this->trav_id() ) - continue; - this->set_visited( leaf, this->trav_id() ); + 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 ); - add_node( leaf ); - ++_num_leaves; - } + /* 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 ); + }); + } - for ( auto const& p : pivots ) - { - traverse( p ); - } +#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 ); + } - if ( auto_extend ) - { - extend( ntk ); - } + inline bool belongs_to( node const& n ) const + { + return std::find( std::begin( _nodes ), std::end( _nodes ), n ) != std::end( _nodes ); + } +#pragma endregion - add_roots( ntk ); +#pragma region Structural properties + inline uint32_t size() const + { + return static_cast( _nodes.size() ); } - inline auto size() const { return _nodes.size(); } - inline auto num_pis() const { return _num_leaves; } - inline auto num_pos() const { return _roots.size(); } - inline auto num_gates() const { return _nodes.size() - _num_leaves - _num_constants; } + inline uint32_t num_cis() const + { + return num_pis(); + } + + inline uint32_t num_cos() const + { + return num_pos(); + } - 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]; } + inline uint32_t num_latches() const + { + return 0u; + } + + inline uint32_t num_pis() const + { + return static_cast( _inputs.size() ); + } - inline bool is_pi( node const& pi ) const + inline uint32_t num_pos() const { - const auto beg = _nodes.begin() + _num_constants; - return std::find( beg, beg + _num_leaves, pi ) != beg + _num_leaves; + 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 ); + } +#pragma endregion + +#pragma region Node and signal iterators template void foreach_pi( Fn&& fn ) const { - detail::foreach_element( _nodes.begin() + _num_constants, _nodes.begin() + _num_constants + _num_leaves, fn ); + detail::foreach_element( std::begin( _inputs ), std::end( _inputs ), fn ); } template void foreach_po( Fn&& fn ) const { - detail::foreach_element( _roots.begin(), _roots.end(), fn ); + detail::foreach_element( std::begin( _outputs ), std::end( _outputs ), fn ); } template - void foreach_node( Fn&& fn ) const + void foreach_ci( Fn&& fn ) const { - detail::foreach_element( _nodes.begin(), _nodes.end(), fn ); + foreach_pi( fn ); } template - void foreach_gate( Fn&& fn ) const + void foreach_co( Fn&& fn ) const { - detail::foreach_element( _nodes.begin() + _num_constants + _num_leaves, _nodes.end(), fn ); + foreach_po( fn ); } - uint32_t fanout_size( node const& n ) const + template + void foreach_ro( Fn&& fn ) const { - return _fanout_size.at( node_to_index( n ) ); + (void)fn; } -private: - void add_node( node const& n ) + template + void foreach_ri( Fn&& fn ) const { - _node_to_index[n] = static_cast( _nodes.size() ); - _nodes.push_back( n ); - - auto fanout_counter = 0; - this->foreach_fanin( n, [&]( const auto& f ) { - if ( std::find( _nodes.begin(), _nodes.end(), this->get_node( f ) ) != _nodes.end() ) - { - fanout_counter++; - } - }); - _fanout_size.push_back( fanout_counter ); + (void)fn; } - void traverse( node const& n ) + template + void foreach_register( Fn&& fn ) const { - if ( this->visited( n ) == this->trav_id() ) - return; - this->set_visited( n, this->trav_id() ); + (void)fn; + } - this->foreach_fanin( n, [&]( const auto& f ) { - traverse( this->get_node( f ) ); - } ); + template + void foreach_node( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ), std::end( _nodes ), fn ); + } - add_node( n ); + template + void foreach_gate( Fn&& fn ) const + { + detail::foreach_element( std::begin( _nodes ) + 1u + _inputs.size(), std::end( _nodes ), fn ); } - void extend( Ntk const& ntk ) + template + void foreach_fanin( node const& n, Fn&& fn ) const { - std::set new_nodes; - do + /* constants and inputs do not have fanins */ + if ( this->is_constant( n ) || + std::find( std::begin( _inputs ), std::end( _inputs ), n ) != std::end( _inputs ) ) { - new_nodes.clear(); - for ( const auto& n : _nodes ) - { - ntk.foreach_fanout( n, [&]( auto const& p ){ - /* skip node if it is already in _nodes */ - if ( std::find( _nodes.begin(), _nodes.end(), p ) != _nodes.end() ) return; - - auto all_children_in_nodes = true; - ntk.foreach_fanin( p, [&]( auto const& s ){ - auto const& child = ntk.get_node( s ); - if ( std::find( _nodes.begin(), _nodes.end(), child ) == _nodes.end() ) - { - all_children_in_nodes = false; - return false; - } - return true; - }); - - if ( all_children_in_nodes ) - { - assert( p != 0 ); - assert( !is_pi( p ) ); - new_nodes.insert( p ); - } - }); - } - - for ( const auto& n : new_nodes ) - { - add_node( n ); - } - } while ( !new_nodes.empty() ); + 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 ); } +#pragma endregion - void add_roots( Ntk const& ntk ) +protected: + void construct( std::vector const& inputs, std::vector const& gates ) { - /* compute po nodes */ - std::vector pos; - ntk.foreach_po( [&]( auto const& s ){ - pos.push_back( ntk.get_node( s ) ); - }); - - /* compute window outputs */ - for ( const auto& n : _nodes ) + /* 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 ) { - // if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) continue; - - if ( std::find( pos.begin(), pos.end(), n ) != pos.end() ) - { - auto s = this->make_signal( n ); - if ( std::find( _roots.begin(), _roots.end(), s ) == _roots.end() ) - { - _roots.push_back( s ); - } - continue; - } - - ntk.foreach_fanout( n, [&]( auto const& p ){ - if ( std::find( _nodes.begin(), _nodes.end(), p ) == _nodes.end() ) - { - auto s = this->make_signal( n ); - if ( std::find( _roots.begin(), _roots.end(), s ) == _roots.end() ) - { - _roots.push_back( s ); - return false; - } - } - return true; - }); + _node_to_index[_nodes.at( index )] = index; } } -public: - unsigned _num_constants{1}; - unsigned _num_leaves{0}; +protected: + std::vector _inputs; + std::vector _outputs; std::vector _nodes; std::unordered_map _node_to_index; - std::vector _roots; - std::vector _fanout_size; -}; +}; /* window_view */ } /* namespace mockturtle */ diff --git a/lib/abcesop/exor.cpp b/lib/abcesop/exor.cpp index e63b11647..041788840 100644 --- a/lib/abcesop/exor.cpp +++ b/lib/abcesop/exor.cpp @@ -48,6 +48,7 @@ #include "eabc/exor.h" #include +#include namespace abc::exorcism { diff --git a/test/algorithms/quality.cpp b/test/algorithms/quality.cpp index c2a0102e2..95ffb12f3 100644 --- a/test/algorithms/quality.cpp +++ b/test/algorithms/quality.cpp @@ -338,4 +338,46 @@ TEST_CASE( "Test quality improvement for XMG3 Resubstitution", "[quality]" ) CHECK( v == std::vector{{0, 38, 46, 22, 62, 72, 76, 75, 273, 865, 190}} ); } +TEST_CASE( "Test quality of 6-input windowing for AIG", "[quality]" ) +{ + std::vector> const result = + {{1, 4, 2, 4}, + {64, 384, 178, 312}, + {40, 240, 64, 296}, + {44, 264, 81, 173}, + {152, 784, 232, 616}, + {71, 401, 149, 337}, + {106, 636, 190, 500}, + {242, 1440, 1075, 1321}, + {215, 1232, 903, 1683}, + {1161, 6871, 2366, 2904}, + {388, 2229, 2151, 3179}}; + + const auto v = foreach_benchmark( [&]( auto& ntk, auto ){ + fanout_view fntk{ntk}; + depth_view dntk{fntk}; + color_view aig{dntk}; + + uint32_t num_windows{0}; + uint32_t num_pis{0}; + uint32_t num_pos{0}; + uint32_t num_gates{0}; + + create_window_impl windowing( aig ); + aig.foreach_node( [&]( aig_network::node const& n ){ + if ( const auto w = windowing.run( n, 6u ) ) + { + window_view win( aig, w->inputs, w->outputs, w->nodes ); + ++num_windows; + num_pis += win.num_pis(); + num_pos += win.num_pos(); + num_gates += win.num_gates(); + } + }); + return std::make_tuple( num_windows, num_pis, num_pos, num_gates ); + }); + + CHECK( v == result ); +} + #endif diff --git a/test/utils/window_utils.cpp b/test/utils/window_utils.cpp new file mode 100644 index 000000000..21130d25c --- /dev/null +++ b/test/utils/window_utils.cpp @@ -0,0 +1,209 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace mockturtle; + +TEST_CASE( "expand node set towards TFI without cut-size", "[window_utils]" ) +{ + using node = typename aig_network::node; + + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_and( b, c ); + auto const f2 = _aig.create_and( b, f1 ); + auto const f3 = _aig.create_and( a, f2 ); + auto const f4 = _aig.create_and( d, f2 ); + auto const f5 = _aig.create_and( f3, f4 ); + _aig.create_po( f5 ); + + color_view aig{_aig}; + { + aig.new_color(); + + /* a cut that can be expanded without increasing cut-size */ + std::vector inputs{aig.get_node( a ), aig.get_node( b ), aig.get_node( f1 ), aig.get_node( d )}; + + bool const trivial_cut = expand0_towards_tfi( aig, inputs ); + CHECK( trivial_cut ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( a ), aig.get_node( b ), aig.get_node( c ), aig.get_node( d )} ); + } + + { + aig.new_color(); + + /* a cut that cannot be expanded without increasing cut-size */ + std::vector inputs{aig.get_node( f3 ), aig.get_node( f4 )}; + + bool const trivial_cut = expand0_towards_tfi( aig, inputs ); + CHECK( !trivial_cut ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( f3 ), aig.get_node( f4 )} ); + } + + { + aig.new_color(); + + /* a cut that can be moved towards the PIs */ + std::vector inputs{aig.get_node( f2 ), aig.get_node( f3 ), aig.get_node( f4 )}; + + bool const trivial_cut = expand0_towards_tfi( aig, inputs ); + CHECK( !trivial_cut ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( a ), aig.get_node( d ), aig.get_node( f2 )} ); + } + + { + aig.new_color(); + + /* the cut { f3, f5 } can be simplified to { f3, f4 } */ + std::vector inputs{aig.get_node( f3 ), aig.get_node( f5 )}; + + bool const trivial_cut = expand0_towards_tfi( aig, inputs ); + CHECK( !trivial_cut ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( f3 ), aig.get_node( f4 )} ); + } + + { + aig.new_color(); + + /* the cut { f4, f5 } also can be simplified to { f3, f4 } */ + std::vector inputs{aig.get_node( f4 ), aig.get_node( f5 )}; + + bool const trivial_cut = expand0_towards_tfi( aig, inputs ); + CHECK( !trivial_cut ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( f3 ), aig.get_node( f4 )} ); + } +} + +TEST_CASE( "expand node set towards TFI", "[window_utils]" ) +{ + using node = typename aig_network::node; + + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_and( b, c ); + auto const f2 = _aig.create_and( b, f1 ); + auto const f3 = _aig.create_and( a, f2 ); + auto const f4 = _aig.create_and( d, f2 ); + auto const f5 = _aig.create_and( f3, f4 ); + _aig.create_po( f5 ); + + color_view aig{_aig}; + + { + /* expand from { f5 } to 4-cut { a, b, c, d } */ + std::vector inputs{aig.get_node( f5 )}; + expand_towards_tfi( aig, inputs, 4u ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( a ), aig.get_node( b ), aig.get_node( c ), aig.get_node( d )} ); + } + + { + /* expand from { f3, f5 } to 3-cut { a, b, f2 } */ + std::vector inputs{aig.get_node( f3 ), aig.get_node( f5 )}; + expand_towards_tfi( aig, inputs, 3u ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( a ), aig.get_node( d ), aig.get_node( f2 )} ); + } + + { + /* expand from { f4, f5 } to 3-cut { a, b, f2 } */ + std::vector inputs{aig.get_node( f4 ), aig.get_node( f5 )}; + expand_towards_tfi( aig, inputs, 3u ); + + std::sort( std::begin( inputs ), std::end( inputs ) ); + CHECK( inputs == std::vector{aig.get_node( a ), aig.get_node( d ), aig.get_node( f2 )} ); + } +} + +TEST_CASE( "expand node set towards TFO", "[window_utils]" ) +{ + using node = typename aig_network::node; + + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_and( b, c ); + auto const f2 = _aig.create_and( b, f1 ); + auto const f3 = _aig.create_and( a, f2 ); + auto const f4 = _aig.create_and( d, f2 ); + auto const f5 = _aig.create_and( f3, f4 ); + _aig.create_po( f5 ); + + std::vector inputs{_aig.get_node( a ), _aig.get_node( b ), _aig.get_node( c ), _aig.get_node( d )}; + + fanout_view fanout_aig{_aig}; + depth_view depth_aig{fanout_aig}; + color_view aig{depth_aig}; + + { + std::vector nodes; + expand_towards_tfo( aig, inputs, nodes ); + + std::sort( std::begin( nodes ), std::end( nodes ) ); + CHECK( nodes == std::vector{aig.get_node( f1 ), aig.get_node( f2 ), aig.get_node( f3 ), + aig.get_node( f4 ), aig.get_node( f5 )} ); + } + + { + std::vector nodes; + levelized_expand_towards_tfo( aig, inputs, nodes ); + + std::sort( std::begin( nodes ), std::end( nodes ) ); + CHECK( nodes == std::vector{aig.get_node( f1 ), aig.get_node( f2 ), aig.get_node( f3 ), + aig.get_node( f4 ), aig.get_node( f5 )} ); + } +} + +TEST_CASE( "create window for pivot", "[window_utils]" ) +{ + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_and( b, c ); + auto const f2 = _aig.create_and( b, f1 ); + auto const f3 = _aig.create_and( a, f2 ); + auto const f4 = _aig.create_and( d, f2 ); + auto const f5 = _aig.create_and( f3, f4 ); + _aig.create_po( f5 ); + + fanout_view fanout_aig{_aig}; + depth_view depth_aig{fanout_aig}; + color_view aig{depth_aig}; + + create_window_impl windowing( aig ); + if ( auto w = windowing.run( aig.get_node( f5 ), 6u ) ) + { + window_view win( aig, w->inputs, w->outputs, w->nodes ); + CHECK( win.num_cis() == 4u ); + CHECK( win.num_cos() == 1u ); + CHECK( win.num_gates() == 5u ); + } +} diff --git a/test/views/color_view.cpp b/test/views/color_view.cpp new file mode 100644 index 000000000..647b9d4e9 --- /dev/null +++ b/test/views/color_view.cpp @@ -0,0 +1,195 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace mockturtle; + +template +void test_color_view() +{ + CHECK( is_network_type_v ); + CHECK( !has_new_color_v ); + CHECK( !has_current_color_v ); + CHECK( !has_clear_colors_v ); + CHECK( !has_color_v ); + CHECK( !has_paint_v ); + CHECK( !has_eval_color_v ); + CHECK( !has_eval_fanins_color_v ); + + using color_ntk = color_view; + CHECK( is_network_type_v ); + CHECK( has_new_color_v ); + CHECK( has_current_color_v ); + CHECK( has_clear_colors_v ); + CHECK( has_color_v ); + CHECK( has_paint_v ); + CHECK( has_eval_color_v ); + CHECK( has_eval_fanins_color_v ); + + using color_color_ntk = color_view; + CHECK( is_network_type_v ); + CHECK( has_new_color_v ); + CHECK( has_current_color_v ); + CHECK( has_clear_colors_v ); + CHECK( has_color_v ); + CHECK( has_paint_v ); + CHECK( has_eval_color_v ); + CHECK( has_eval_fanins_color_v ); + + using out_of_place_color_ntk = out_of_place_color_view; + CHECK( is_network_type_v ); + CHECK( has_new_color_v ); + CHECK( has_current_color_v ); + CHECK( has_clear_colors_v ); + CHECK( has_color_v ); + CHECK( has_paint_v ); + CHECK( has_eval_color_v ); + CHECK( has_eval_fanins_color_v ); +}; + +TEST_CASE( "create different color views", "[color_view]" ) +{ + test_color_view(); + test_color_view(); + test_color_view(); + test_color_view(); + test_color_view(); +} + +TEST_CASE( "in-place color view", "[color_view]" ) +{ + aig_network _aig; + const auto a = _aig.create_pi(); + const auto b = _aig.create_pi(); + const auto c = _aig.create_pi(); + const auto d = _aig.create_pi(); + const auto e = _aig.create_pi(); + + const auto f1 = _aig.create_and( a, b ); + const auto f2 = _aig.create_and( c, d ); + const auto f3 = _aig.create_and( f1, f2 ); + const auto f4 = _aig.create_and( e, f2 ); + const auto f5 = _aig.create_and( f1, f3 ); + const auto f6 = _aig.create_and( f2, f3 ); + const auto f7 = _aig.create_and( f5, f6 ); + const auto f8 = _aig.create_and( f4, f7 ); + _aig.create_po( f8 ); + + color_view aig{_aig}; + + auto const white = aig.new_color(); + auto const yellow = aig.new_color(); + CHECK( yellow > white ); + auto const red = aig.new_color(); + CHECK( red > white ); + + /* assign some colors: f5 is white, f1 is yellow, and f3 is in the color of f1 */ + aig.paint( aig.get_node( f5 ), white ); + aig.paint( aig.get_node( f1 ), yellow ); + aig.paint( aig.get_node( f3 ), aig.get_node( f1 ) ); + + /* f1 and f3 have the same color */ + CHECK( aig.eval_color( aig.get_node( f1 ), aig.get_node( f3 ), + [&]( auto c0, auto c1 ){ return c0 == c1; } ) ); + + /* f1 and f5 have different colors */ + CHECK( aig.eval_color( aig.get_node( f1 ), aig.get_node( f5 ), + [&]( auto c0, auto c1 ){ return c0 != c1; } ) ); + + /* f5 is at least white */ + CHECK( aig.eval_color( aig.get_node( f5 ), + [&]( auto color ){ return color >= white; } ) ); + + /* f5 is not yellow */ + CHECK( aig.eval_color( aig.get_node( f5 ), + [&]( auto color ){ return color != yellow; } ) ); + + /* the fanin's of f5 are at least white */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color >= white; } ) ); + + /* the fanins of f5 are yellow */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color == yellow; } ) ); + + /* at least one fanin of f5 is not red */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color != red; } ) ); + + /* colors are stored in the visited flags */ + CHECK( aig.visited( aig.get_node( f5 ) ) == white ); + CHECK( aig.visited( aig.get_node( f1 ) ) == yellow ); + CHECK( aig.visited( aig.get_node( f3 ) ) == yellow ); +} + +TEST_CASE( "out-of-place color view", "[color_view]" ) +{ + aig_network _aig; + const auto a = _aig.create_pi(); + const auto b = _aig.create_pi(); + const auto c = _aig.create_pi(); + const auto d = _aig.create_pi(); + const auto e = _aig.create_pi(); + + const auto f1 = _aig.create_and( a, b ); + const auto f2 = _aig.create_and( c, d ); + const auto f3 = _aig.create_and( f1, f2 ); + const auto f4 = _aig.create_and( e, f2 ); + const auto f5 = _aig.create_and( f1, f3 ); + const auto f6 = _aig.create_and( f2, f3 ); + const auto f7 = _aig.create_and( f5, f6 ); + const auto f8 = _aig.create_and( f4, f7 ); + _aig.create_po( f8 ); + + out_of_place_color_view aig{_aig}; + + auto const white = aig.new_color(); + auto const yellow = aig.new_color(); + CHECK( yellow > white ); + auto const red = aig.new_color(); + CHECK( red > white ); + + /* assign some colors: f5 is white, f1 is yellow, and f3 is in the color of f1 */ + aig.paint( aig.get_node( f5 ), white ); + aig.paint( aig.get_node( f1 ), yellow ); + aig.paint( aig.get_node( f3 ), aig.get_node( f1 ) ); + + /* f1 and f3 have the same color */ + CHECK( aig.eval_color( aig.get_node( f1 ), aig.get_node( f3 ), + [&]( auto c0, auto c1 ){ return c0 == c1; } ) ); + + /* f1 and f5 have different colors */ + CHECK( aig.eval_color( aig.get_node( f1 ), aig.get_node( f5 ), + [&]( auto c0, auto c1 ){ return c0 != c1; } ) ); + + /* f5 is at least white */ + CHECK( aig.eval_color( aig.get_node( f5 ), + [&]( auto color ){ return color >= white; } ) ); + + /* f5 is not yellow */ + CHECK( aig.eval_color( aig.get_node( f5 ), + [&]( auto color ){ return color != yellow; } ) ); + + /* the fanin's of f5 are at least white */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color >= white; } ) ); + + /* the fanins of f5 are yellow */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color == yellow; } ) ); + + /* at least one fanin of f5 is not red */ + CHECK( aig.eval_fanins_color( aig.get_node( f5 ), + [&]( auto color ){ return color != red; } ) ); + + /* visited flags have not been affected by assigning colors */ + CHECK( aig.visited( aig.get_node( f5 ) ) == 0u ); + CHECK( aig.visited( aig.get_node( f1 ) ) == 0u ); + CHECK( aig.visited( aig.get_node( f3 ) ) == 0u ); +} diff --git a/test/views/fanout_view.cpp b/test/views/fanout_view.cpp index 2b8b1e28b..6c40a9d60 100644 --- a/test/views/fanout_view.cpp +++ b/test/views/fanout_view.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -31,6 +33,8 @@ TEST_CASE( "create different fanout views", "[fanout_view]" ) { test_fanout_view(); test_fanout_view(); + test_fanout_view(); + test_fanout_view(); test_fanout_view(); } diff --git a/test/views/window_view.cpp b/test/views/window_view.cpp index 16a1706b4..35275c65a 100644 --- a/test/views/window_view.cpp +++ b/test/views/window_view.cpp @@ -4,10 +4,65 @@ #include #include #include +#include #include +#include using namespace mockturtle; +template +std::vector collect_fanin_nodes( Ntk const& ntk, typename Ntk::node const& n ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + std::vector fanin_nodes; + ntk.foreach_fanin( n, [&]( signal const& fi ){ + fanin_nodes.emplace_back( ntk.get_node( fi ) ); + }); + return fanin_nodes; +} + +/*! \brief Ensure that all outputs and fanins belong to the window */ +template +bool window_is_well_formed( Ntk const& ntk ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + bool all_fanins_belong_to_window = true; + ntk.foreach_node( [&]( node const& n ){ + ntk.foreach_fanin( n, [&]( signal const& fi ){ + if ( !ntk.belongs_to( ntk.get_node( fi ) ) ) + { + all_fanins_belong_to_window = false; + return false; /* terminate */ + } + return true; /* next */ + }); + + /* check if property is already violated */ + if ( !all_fanins_belong_to_window ) + { + return false; /* terminate */ + } + return true; /* next */ + }); + + bool all_outputs_belong_to_window = true; + ntk.foreach_po( [&]( signal const& o ){ + if ( !ntk.belongs_to( ntk.get_node( o ) ) ) + { + all_outputs_belong_to_window = false; + return false; /* terminate */ + } + return true; /* next */ + }); + + return all_fanins_belong_to_window && + all_outputs_belong_to_window; +} + TEST_CASE( "create window view on AIG", "[window_view]" ) { aig_network aig; @@ -24,30 +79,180 @@ TEST_CASE( "create window view on AIG", "[window_view]" ) CHECK( aig.num_pos() == 1 ); CHECK( aig.num_gates() == 4 ); - fanout_view fanout_ntk( aig ); - fanout_ntk.clear_visited(); + /* make a window */ + { + window_view view( aig, + /* inputs = */ { aig.get_node( a ), aig.get_node( b ) }, + /* outputs = */ { f3 }, + /* nodes = */ { aig.get_node( f1 ), aig.get_node( f3 ) } ); + CHECK( view.size() == 5 ); + CHECK( view.num_gates() == 2 ); + CHECK( view.num_pis() == 2 ); + CHECK( view.num_pos() == 1 ); + CHECK( view.num_cis() == 2 ); + CHECK( view.num_cos() == 1 ); + + CHECK( view.belongs_to( view.get_node( f1 ) ) ); + CHECK( !view.belongs_to( view.get_node( f2 ) ) ); + CHECK( view.belongs_to( view.get_node( f3 ) ) ); + CHECK( !view.belongs_to( view.get_node( f4 ) ) ); + + CHECK( collect_fanin_nodes( view, view.get_node( f1 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f3 ) ).size() == 2 ); + CHECK( window_is_well_formed( view ) ); + } + + { + window_view view( aig, + /* inputs = */ { aig.get_node( f1 ), aig.get_node( b ) }, + /* outputs = */ { f3 }, + /* nodes = */ { aig.get_node( f3 ) } ); + CHECK( view.size() == 4 ); + CHECK( view.num_gates() == 1 ); + CHECK( view.num_pis() == 2 ); + CHECK( view.num_pos() == 1 ); + CHECK( view.num_cis() == 2 ); + CHECK( view.num_cos() == 1 ); + + CHECK( view.belongs_to( view.get_node( f1 ) ) ); + CHECK( !view.belongs_to( view.get_node( f2 ) ) ); + CHECK( view.belongs_to( view.get_node( f3 ) ) ); + CHECK( !view.belongs_to( view.get_node( f4 ) ) ); + + CHECK( collect_fanin_nodes( view, view.get_node( f1 ) ).size() == 0 ); + CHECK( collect_fanin_nodes( view, view.get_node( f3 ) ).size() == 2 ); + CHECK( window_is_well_formed( view ) ); + } + + { + window_view view( aig, + /* inputs = */ { aig.get_node( a ), aig.get_node( b ) }, + /* outputs = */ { f2 }, + /* nodes = */ { aig.get_node( f1 ), aig.get_node( f2 ) } ); + + CHECK( view.size() == 5 ); + CHECK( view.num_gates() == 2 ); + CHECK( view.num_pis() == 2 ); + CHECK( view.num_pos() == 1 ); + CHECK( view.num_cis() == 2 ); + CHECK( view.num_cos() == 1 ); + + CHECK( view.belongs_to( view.get_node( f1 ) ) ); + CHECK( view.belongs_to( view.get_node( f2 ) ) ); + CHECK( !view.belongs_to( view.get_node( f3 ) ) ); + CHECK( !view.belongs_to( view.get_node( f4 ) ) ); - const auto pivot1 = aig.get_node( f3 ); + CHECK( collect_fanin_nodes( view, view.get_node( f1 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f2 ) ).size() == 2 ); + CHECK( window_is_well_formed( view ) ); + } + + { + window_view view( aig, + /* inputs = */ { aig.get_node( a ), aig.get_node( b ) }, + /* outputs = */ { f4 }, + /* nodes = */ { aig.get_node( f1 ), aig.get_node( f2 ), aig.get_node( f3 ), aig.get_node( f4 ) } ); + + CHECK( view.size() == 7 ); + CHECK( view.num_gates() == 4 ); + CHECK( view.num_pis() == 2 ); + CHECK( view.num_pos() == 1 ); + CHECK( view.num_cis() == 2 ); + CHECK( view.num_cos() == 1 ); + + CHECK( view.belongs_to( view.get_node( f1 ) ) ); + CHECK( view.belongs_to( view.get_node( f2 ) ) ); + CHECK( view.belongs_to( view.get_node( f3 ) ) ); + CHECK( view.belongs_to( view.get_node( f4 ) ) ); + + CHECK( collect_fanin_nodes( view, view.get_node( f1 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f2 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f3 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f4 ) ).size() == 2 ); + CHECK( window_is_well_formed( view ) ); + } + + { + window_view view( aig, + /* inputs = */ { aig.get_node( a ), aig.get_node( b ) }, + /* outputs = */ { f2, f3 }, + /* nodes = */ { aig.get_node( f1 ), aig.get_node( f2 ), aig.get_node( f3 ) } ); + + CHECK( view.size() == 6 ); + CHECK( view.num_gates() == 3 ); + CHECK( view.num_pis() == 2 ); + CHECK( view.num_pos() == 2 ); + CHECK( view.num_cis() == 2 ); + CHECK( view.num_cos() == 2 ); + + CHECK( view.belongs_to( view.get_node( f1 ) ) ); + CHECK( view.belongs_to( view.get_node( f2 ) ) ); + CHECK( view.belongs_to( view.get_node( f3 ) ) ); + CHECK( !view.belongs_to( view.get_node( f4 ) ) ); + + CHECK( collect_fanin_nodes( view, view.get_node( f1 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f2 ) ).size() == 2 ); + CHECK( collect_fanin_nodes( view, view.get_node( f3 ) ).size() == 2 ); + CHECK( window_is_well_formed( view ) ); + } +} + +TEST_CASE( "collect nodes", "[window_view]" ) +{ + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_xor( a, b ); + auto const f2 = _aig.create_xor( c, d ); + auto const f3 = _aig.create_xor( f1, f2 ); + auto const f4 = _aig.create_and( f1, f2 ); + _aig.create_po( f3 ); + _aig.create_po( f4 ); + + using node = typename aig_network::node; + using signal = typename aig_network::signal; + + color_view aig{_aig}; + + std::vector inputs{aig.get_node( a ), aig.get_node( b ), aig.get_node( c ), aig.get_node( d )}; + std::vector outputs{f3, f4}; + std::vector const gates{collect_nodes( aig, inputs, outputs )}; + + CHECK( gates.size() == aig.num_gates() ); + aig.foreach_gate( [&]( node const& n ){ + CHECK( std::find( std::begin( gates ), std::end( gates ), n ) != std::end( gates ) ); + }); +} + +TEST_CASE( "expand towards tfo", "[window_view]" ) +{ + aig_network _aig; + auto const a = _aig.create_pi(); + auto const b = _aig.create_pi(); + auto const c = _aig.create_pi(); + auto const d = _aig.create_pi(); + auto const f1 = _aig.create_xor( a, b ); + auto const f2 = _aig.create_xor( c, d ); + auto const f3 = _aig.create_xor( f1, f2 ); + auto const f4 = _aig.create_and( f1, f2 ); + _aig.create_po( f3 ); + _aig.create_po( f4 ); - const auto leaves1 = reconvergence_driven_cut( aig, pivot1 ).first; - window_view> win1( fanout_ntk, leaves1, { pivot1 }, false ); + using node = typename aig_network::node; + using signal = typename aig_network::signal; - CHECK( is_network_type_v ); - CHECK( win1.size() == 5 ); - CHECK( win1.num_pis() == 2 ); - CHECK( win1.num_pos() == 3 ); // b, f1, f2 + fanout_view faig{_aig}; + color_view aig{faig}; - const auto pivot2 = aig.get_node( f2 ); - const auto leaves2 = reconvergence_driven_cut( aig, pivot2 ).first; - window_view> win2( fanout_ntk, leaves2, { pivot2 }, false ); - CHECK( win2.size() == 5 ); - CHECK( win2.num_pis() == 2 ); - CHECK( win2.num_pos() == 3 ); // a, f1, f3 + std::vector inputs{aig.get_node( a ), aig.get_node( b ), aig.get_node( c ), aig.get_node( d )}; + std::vector outputs{f1}; + std::vector gates{collect_nodes( aig, inputs, outputs )}; + expand_towards_tfo( aig, inputs, gates ); - const auto pivot3 = aig.get_node( f1 ); - const auto leaves3 = reconvergence_driven_cut( aig, pivot3 ).first; - window_view> win3( fanout_ntk, leaves3, { pivot3 }, true ); - CHECK( win3.size() == 7 ); - CHECK( win3.num_pis() == 2 ); - CHECK( win3.num_pos() == 1 ); // f4 + CHECK( gates.size() == aig.num_gates() ); + aig.foreach_gate( [&]( node const& n ){ + CHECK( std::find( std::begin( gates ), std::end( gates ), n ) != std::end( gates ) ); + }); }