diff --git a/docs/changelog.rst b/docs/changelog.rst index ce67dd471..dc9d2ff5f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -39,8 +39,9 @@ v0.2 (not yet released) - Write networks to DIMACS files for CNF (`write_dimacs`) `#146 `_ - Read BLIF files using *lorina* (`blif_reader`) `#167 `_ - Write networks to BLIF files (`write_blif`) `#169 `_ `#184 `_ - - Create circuit from integer index list (`create_from_binary_index_list`) `#259 `_ - Write networks to AIGER files (`write_aiger`) `#379 `_ +* Utils + - Create circuit from integer index list (`encode`, `decode`, `insert`, `to_index_list_string`) `#385 `_ * Resynthesis functions: - Resynthesis function based on DSD decomposition (`dsd_resynthesis`) `#182 `_ - Resynthesis function based on Shannon decomposition (`shannon_resynthesis`) `#185 `_ diff --git a/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp b/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp index c2390e9bd..0dfcfdb72 100644 --- a/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp +++ b/include/mockturtle/algorithms/node_resynthesis/xag_minmc2.hpp @@ -38,7 +38,7 @@ #include #include -#include "../../io/index_list.hpp" +#include "../../utils/index_list.hpp" #include "../../networks/xag.hpp" #include "../detail/minmc_xags.hpp" #include "../equivalence_classes.hpp" @@ -126,7 +126,15 @@ class xag_minmc_resynthesis } const auto f = apply_spectral_transformations( ntk, trans, std::vector>( begin, end ), [&]( xag_network& ntk, std::vector> const& leaves ) { - return create_from_binary_index_list( ntk, it->second.begin(), leaves.begin() )[0u]; + xag_index_list il{it->second}; + std::vector pos; + insert( ntk, std::begin( leaves ), std::begin( leaves ) + il.num_pis(), il, + [&]( xag_network::signal const& f ) + { + pos.push_back( f ); + } ); + assert( pos.size() == 1u ); + return pos[0u]; } ); fn( f ); @@ -142,6 +150,7 @@ class xag_minmc_resynthesis for ( auto const& [_, word, repr, expr] : detail::minmc_xags[i] ) { (void)_; + (void)expr; db_[i][word] = repr; st_.db_size += sizeof( word ) + sizeof( repr ) + sizeof( uint32_t ) * repr.size(); } diff --git a/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp b/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp index 2000d199a..ccdca1c63 100644 --- a/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp +++ b/include/mockturtle/algorithms/node_resynthesis/xag_npn.hpp @@ -44,8 +44,8 @@ #include #include "../../algorithms/simulation.hpp" -#include "../../io/index_list.hpp" #include "../../networks/xag.hpp" +#include "../../utils/index_list.hpp" #include "../../utils/node_map.hpp" #include "../../utils/stopwatch.hpp" @@ -210,7 +210,7 @@ class xag_npn_resynthesis { stopwatch t( st.time_db ); - _db = create_from_binary_index_list( subgraphs ); + decode( _db, xag_index_list{std::vector{subgraphs, subgraphs + sizeof subgraphs / sizeof subgraphs[0]}} ); const auto sim_res = simulate_nodes>( _db ); _db.foreach_node( [&]( auto n ) { diff --git a/include/mockturtle/io/index_list.hpp b/include/mockturtle/io/index_list.hpp deleted file mode 100644 index 0f65bc961..000000000 --- a/include/mockturtle/io/index_list.hpp +++ /dev/null @@ -1,224 +0,0 @@ -/* 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 index_list.hpp - \brief Network representation as index lists - - \author Mathias Soeken -*/ - -#pragma once - -#include -#include -#include - -#include "../networks/aig.hpp" -#include "../networks/xag.hpp" -#include "../traits.hpp" - -#include - -namespace mockturtle -{ - -/*! \brief Creates AND and XOR gates from binary index list. - * - * The `begin` iterator points to the first index of an index list that has the - * following 32-bit unsigned integer elements. It starts with a signature whose - * partitioned into `| num_gates | num_pos | num_pis |`, where `num_gates` - * accounts for the most-significant 16 bits, `num_pos` accounts for 8 bits, and - * `num_pis` accounts for the least-significant 8 bits. Afterwards, gates are - * defined as literal indexes `(2 * i + c)`, where `i` is an index, with 0 - * indexing the constant 0, 1 to `num_pis` indexing the primary inputs, and all - * successive indexes for the gates. Gate literals come in pairs. If the first - * literal has a smaller value than the second one, an AND gate is created, - * otherwise, an XOR gate is created. Afterwards, all outputs are defined in - * terms of literals. The length of both the index list and the primary input - * list can be derived from the signature, and therefore, no end iterators need - * to be passed to this function. - * - * \param dest Network - * \param begin Start iterator to index list - * \param pi_begin Start iterator to primary inputs (in network) - * \return Returns a vector of signals created based on index list - * - * Example: The following index list creates (x1 AND x2) XOR (x3 AND x4): - * `{3 << 16 | 1 << 8 | 4, 2, 4, 6, 8, 12, 10, 14}` - */ -template -std::vector> create_from_binary_index_list( Ntk& dest, IndexIterator begin, LeavesIterator pi_begin ) -{ - static_assert( is_network_type_v, "Ntk is not a network type" ); - - static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); - static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); - static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); - static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); - - static_assert( std::is_same_v::value_type>, uint32_t>, "IndexIterator value_type must be uint32_t" ); - static_assert( std::is_same_v::value_type>, signal>, "LeavesIterator value_type must be Ntk signal type" ); - - const auto signature = *begin++; - const auto num_pis = signature & 0xff; - const auto num_pos = ( signature >> 8 ) & 0xff; - const auto num_gates = signature >> 16; - - // initialize gate-signal list - std::vector> fs; - fs.push_back( dest.get_constant( false ) ); - std::copy_n( pi_begin, num_pis, std::back_inserter( fs ) ); - - for ( auto i = 0u; i < num_gates; ++i ) - { - const auto signal1 = *begin++; - const auto signal2 = *begin++; - - const auto c1 = signal1 % 2 ? dest.create_not( fs[signal1 / 2] ) : fs[signal1 / 2]; - const auto c2 = signal2 % 2 ? dest.create_not( fs[signal2 / 2] ) : fs[signal2 / 2]; - - fs.push_back( signal1 > signal2 ? dest.create_xor( c1, c2 ) : dest.create_and( c1, c2 ) ); - } - - std::vector> pos( num_pos ); - for ( auto i = 0u; i < num_pos; ++i ) - { - const auto signal = *begin++; - pos[i] = signal % 2 ? dest.create_not( fs[signal / 2] ) : fs[signal / 2]; - } - - return pos; -} - -/*! \brief Creates AND and XOR gates from binary index list. - * - * An out-of-place variant for create_from_binary_index_list. - */ -template -Ntk create_from_binary_index_list( IndexIterator begin ) -{ - static_assert( is_network_type_v, "Ntk is not a network type" ); - static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); - static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); - - static_assert( std::is_same_v::value_type>, uint32_t>, "IndexIterator value_type must be uint32_t" ); - - Ntk ntk; - std::vector> pis( *begin & 0xff ); - std::generate( pis.begin(), pis.end(), [&]() { return ntk.create_pi(); } ); - for ( auto const& f : create_from_binary_index_list( ntk, begin, pis.begin() ) ) - { - ntk.create_po( f ); - } - return ntk; -} - -namespace detail -{ - -template -std::vector to_index_list( Ntk const& ntk ) -{ - static_assert( std::is_same_v || std::is_same_v, "Ntk must be XAG or AIG" ); - - std::vector index_list; - index_list.push_back( ( ntk.num_gates() << 16 ) | ( ntk.num_pos() << 8 ) | ntk.num_pis() ); - - ntk.foreach_pi( [&]( auto const& n, auto i ) { - if ( ntk.node_to_index( n ) != i + 1 ) { - fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", i + 1 ); - std::abort(); - } - }); - - ntk.foreach_gate( [&]( auto const& n, auto i ) { - if ( ntk.node_to_index( n ) != ntk.num_pis() + i + 1 ) - { - fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); - std::abort(); - } - - ntk.foreach_fanin( n, [&]( auto const& f ) { - if ( ntk.node_to_index( ntk.get_node( f ) ) > ntk.node_to_index( n ) ) - { - fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); - std::abort(); - } - index_list.push_back( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ( ntk.is_complemented( f ) ? 1 : 0 ) ); - } ); - } ); - - ntk.foreach_po( [&]( auto const& f ) { - index_list.push_back( 2 * ntk.node_to_index( ntk.get_node( f ) ) + ( ntk.is_complemented( f ) ? 1 : 0 ) ); - }); - - return index_list; -} - -template -std::string to_index_list_string( Ntk const& ntk ) -{ - static_assert( std::is_same_v || std::is_same_v, "Ntk must be XAG or AIG" ); - - /* compute signature */ - auto s = fmt::format( "{{{} << 16 | {} << 8 | {}", ntk.num_gates(), ntk.num_pos(), ntk.num_pis() ); - - ntk.foreach_pi( [&]( auto const& n, auto i ) { - if ( ntk.node_to_index( n ) != i + 1 ) { - fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", i + 1 ); - std::abort(); - } - }); - - ntk.foreach_gate( [&]( auto const& n, auto i ) { - if ( ntk.node_to_index( n ) != ntk.num_pis() + i + 1 ) - { - fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); - std::abort(); - } - - ntk.foreach_fanin( n, [&]( auto const& f ) { - if ( ntk.node_to_index( ntk.get_node( f ) ) > ntk.node_to_index( n ) ) - { - fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); - std::abort(); - } - s += fmt::format( ", {}", 2 * ntk.node_to_index( ntk.get_node( f ) ) + ( ntk.is_complemented( f ) ? 1 : 0 ) ); - } ); - } ); - - ntk.foreach_po( [&]( auto const& f ) { - s += fmt::format( ", {}", 2 * ntk.node_to_index( ntk.get_node( f ) ) + ( ntk.is_complemented( f ) ? 1 : 0 ) ); - }); - - s += "}"; - - return s; -} - -} - -} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/index_list.hpp b/include/mockturtle/utils/index_list.hpp new file mode 100644 index 000000000..48e558f8f --- /dev/null +++ b/include/mockturtle/utils/index_list.hpp @@ -0,0 +1,869 @@ +/* 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 index_list.hpp + \brief List of indices to represent small networks. + + \author Heinz Riener + \author Mathias Soeken +*/ + +#pragma once + +#include "../traits.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +/*! \brief An ABC-compatiable index list. + * + * Small network represented as a list of literals. The + * implementation supports AND and XOR gates and is compatiable with + * ABC's encoding. + * + * Example: The following index list creates the output function `(x1 + * AND x2) XOR (x3 AND x4)` with 4 inputs, 1 output, and 3 gates: + * `{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 12, 10, 14, 14}` + */ +struct abc_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit abc_index_list( uint32_t num_pis = 0 ) + { + /* add constants */ + values.push_back( 0u ); + values.push_back( 1u ); + + /* add inputs */ + if ( num_pis > 0 ) + { + add_inputs( num_pis ); + } + } + + explicit abc_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + { + /* parse the values to determine the number of inputs and outputs */ + auto i = 2u; + for ( ; ( i+1 ) < values.size(); i+=2 ) + { + if ( values.at( i ) == 0 && values.at( i + 1 ) == 0 ) + { + ++_num_pis; + } + else + { + break; + } + } + for ( ; ( i+1 ) < values.size(); i+=2 ) + { + assert( !( values.at( i ) == 0 && values.at( i + 1 ) == 0 ) ); + if ( values.at( i ) == values.at( i+1 ) ) + { + ++_num_pos; + } + } + } + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.size() - ( ( 1 + _num_pis + _num_pos ) << 1u ) ) >> 1u; + } + + uint64_t num_pis() const + { + return _num_pis; + } + + uint64_t num_pos() const + { + return _num_pos; + } + + template + void foreach_entry( Fn&& fn ) const + { + assert( ( values.size() % 2 ) == 0 ); + for ( uint64_t i = ( 1 + _num_pis ) << 1u; i < values.size() - ( _num_pos << 1 ); i += 2 ) + { + fn( values.at( i ), values.at( i+1 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - _num_pos; i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void add_inputs( uint32_t num_pis = 1u ) + { + _num_pis += num_pis; + for ( auto i = 0u; i < num_pis; ++i ) + { + values.push_back( 0u ); + values.push_back( 0u ); + } + } + + void add_and( element_type lit0, element_type lit1 ) + { + assert( lit0 < lit1 ); + values.push_back( lit0 ); + values.push_back( lit1 ); + } + + void add_xor( element_type lit0, element_type lit1 ) + { + assert( lit0 > lit1 ); + values.push_back( lit0 ); + values.push_back( lit1 ); + } + + void add_output( element_type lit ) + { + ++_num_pos; + values.push_back( lit ); + values.push_back( lit ); + } + +private: + uint32_t _num_pis{0}; + uint32_t _num_pos{0}; + std::vector values; +}; + +/*! \brief Generates an abc_index_list from a network + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_and` + * - `is_complemented` + * - `is_xor` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( abc_index_list& indices, Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + }); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ){ + assert( ntk.is_and( n ) || ntk.is_xor( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ){ + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2*ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + }); + + if ( ntk.is_and( n ) ) + { + indices.add_and( lits[0u], lits[1u] ); + } + else if ( ntk.is_xor( n ) ) + { + indices.add_xor( lits[0u], lits[1u] ); + } + }); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ){ + indices.add_output( 2*ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + }); + + assert( indices.size() == ( 1u + ntk.num_pis() + ntk.num_gates() + ntk.num_pos() ) << 1u ); +} + +/*! \brief Inserts an abc_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_and` + * - `create_xor` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, abc_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + 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" ); + + using signal = typename Ntk::signal; + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + signals.push_back( *it ); + } + + indices.foreach_entry( [&]( uint32_t lit0, uint32_t lit1 ){ + assert( lit0 != lit1 ); + + uint32_t const i0 = lit0 >> 1; + uint32_t const i1 = lit1 >> 1; + signal const s0 = ( lit0 % 2 ) ? !signals.at( i0 ) : signals.at( i0 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( i1 ) : signals.at( i1 ); + + signals.push_back( lit0 < lit1 ? ntk.create_and( s0, s1 ) : ntk.create_xor( s0, s1 ) ); + }); + + indices.foreach_po( [&]( uint32_t lit ){ + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + }); +} + +/*! \brief Converts an abc_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( abc_index_list const& indices ) +{ + auto const raw = indices.raw(); + + std::string s{"{"}; + auto it = std::begin( raw ); + while ( it != std::end( raw ) ) + { + s += std::to_string( *it ); + ++it; + if ( it != std::end( raw ) ) + { + s += ", "; + } + } + s += "}"; + return s; +} + +/*! \brief Index list for majority-inverter graphs. + * + * Small network consisting of majority gates and inverters + * represented as a list of literals. + * + * Example: The following index list creates the output function + * `<, x2, x4>` with 4 inputs, 1 output, and 3 gates: + * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + */ +struct mig_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit mig_index_list( uint32_t num_pis = 0 ) + : values( {num_pis} ) + { + } + + explicit mig_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_entry( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 3 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 3 ) + { + fn( values.at( i ), values.at( i+1 ), values.at( i+2 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + void add_maj( element_type lit0, element_type lit1, element_type lit2 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + values.push_back( lit2 ); + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Generates a mig_index_list from a network + * + * The function requires `ntk` to consist of majority gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_complemented` + * - `is_maj` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( mig_index_list& indices, Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_maj_v, "Ntk does not implement the is_maj method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + }); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ){ + assert( ntk.is_maj( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ){ + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2*ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + }); + indices.add_maj( lits[0u], lits[1u], lits[2u] ); + }); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ){ + indices.add_output( 2*ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + }); + + assert( indices.size() == 1u + 3u*ntk.num_gates() + ntk.num_pos() ); +} + +/*! \brief Inserts a mig_index_list into an existing network + * + * **Required network functions:** + * - `get_constant` + * - `create_maj` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, mig_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_maj_v, "Ntk does not implement the create_maj method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + 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" ); + + using signal = typename Ntk::signal; + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + signals.push_back( *it ); + } + + indices.foreach_entry( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ){ + signal const s0 = ( lit0 % 2 ) ? !signals.at( lit0 >> 1 ) : signals.at( lit0 >> 1 ); + signal const s1 = ( lit1 % 2 ) ? !signals.at( lit1 >> 1 ) : signals.at( lit1 >> 1 ); + signal const s2 = ( lit2 % 2 ) ? !signals.at( lit2 >> 1 ) : signals.at( lit2 >> 1 ); + signals.push_back( ntk.create_maj( s0, s1, s2 ) ); + }); + + indices.foreach_po( [&]( uint32_t lit ){ + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? !signals.at( i ) : signals.at( i ) ); + }); +} + +/*! \brief Converts an mig_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( mig_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_entry( [&]( uint32_t lit0, uint32_t lit1, uint32_t lit2 ){ + s += fmt::format( ", {}, {}, {}", lit0, lit1, lit2 ); + }); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + }); + + s += "}"; + + return s; +} + +/*! \brief Index list for xor-and graphs. + * + * Small network represented as a list of literals. Supports XOR and + * AND gates. The list has the following 32-bit unsigned integer + * elements. It starts with a signature whose partitioned into `| + * num_gates | num_pos | num_pis |`, where `num_gates` accounts for + * the most-significant 16 bits, `num_pos` accounts for 8 bits, and + * `num_pis` accounts for the least-significant 8 bits. Afterwards, + * gates are defined as literal indexes `(2 * i + c)`, where `i` is an + * index, with 0 indexing the constant 0, 1 to `num_pis` indexing the + * primary inputs, and all successive indexes for the gates. Gate + * literals come in pairs. If the first literal has a smaller value + * than the second one, an AND gate is created, otherwise, an XOR gate + * is created. Afterwards, all outputs are defined in terms of + * literals. + * + * Example: The following index list creates the output function `(x1 + * AND x2) XOR (x3 AND x4)` with 4 inputs, 1 output, and 3 gates: + * `{4 | 1 << 8 | 3 << 16, 2, 4, 6, 8, 12, 10, 14}` + */ +struct xag_index_list +{ +public: + using element_type = uint32_t; + +public: + explicit xag_index_list( uint32_t num_pis = 0 ) + : values( {num_pis} ) + { + } + + explicit xag_index_list( std::vector const& values ) + : values( std::begin( values ), std::end( values ) ) + {} + + std::vector raw() const + { + return values; + } + + uint64_t size() const + { + return values.size(); + } + + uint64_t num_gates() const + { + return ( values.at( 0 ) >> 16 ); + } + + uint64_t num_pis() const + { + return values.at( 0 ) & 0xff; + } + + uint64_t num_pos() const + { + return ( values.at( 0 ) >> 8 ) & 0xff; + } + + template + void foreach_entry( Fn&& fn ) const + { + assert( ( values.size() - 1u - num_pos() ) % 2 == 0 ); + for ( uint64_t i = 1u; i < values.size() - num_pos(); i += 2 ) + { + fn( values.at( i ), values.at( i+1 ) ); + } + } + + template + void foreach_po( Fn&& fn ) const + { + for ( uint64_t i = values.size() - num_pos(); i < values.size(); ++i ) + { + fn( values.at( i ) ); + } + } + + void add_inputs( uint32_t n = 1u ) + { + assert( num_pis() + n <= 0xff ); + values.at( 0u ) += n; + } + + void add_and( element_type lit0, element_type lit1 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + } + + void add_xor( element_type lit0, element_type lit1 ) + { + assert( num_gates() + 1u <= 0xffff ); + values.at( 0u ) = ( ( num_gates() + 1 ) << 16 ) | ( values.at( 0 ) & 0xffff ); + values.push_back( lit0 ); + values.push_back( lit1 ); + } + + void add_output( element_type lit ) + { + assert( num_pos() + 1 <= 0xff ); + values.at( 0u ) = ( num_pos() + 1 ) << 8 | ( values.at( 0u ) & 0xffff00ff ); + values.push_back( lit ); + } + +private: + std::vector values; +}; + +/*! \brief Generates a xag_index_list from a network + * + * The function requires `ntk` to consist of XOR and AND gates. + * + * **Required network functions:** + * - `foreach_fanin` + * - `foreach_gate` + * - `get_node` + * - `is_and` + * - `is_complemented` + * - `is_xor` + * - `node_to_index` + * - `num_gates` + * - `num_pis` + * - `num_pos` + * + * \param indices An index list + * \param ntk A logic network + */ +template +void encode( xag_index_list& indices, Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + static_assert( has_is_and_v, "Ntk does not implement the is_and method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_xor_v, "Ntk does not implement the is_xor method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_num_gates_v, "Ntk does not implement the num_gates method" ); + static_assert( has_num_pis_v, "Ntk does not implement the num_pis method" ); + static_assert( has_num_pos_v, "Ntk does not implement the num_pos method" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.foreach_pi( [&]( node const& n, uint64_t index ) { + if ( ntk.node_to_index( n ) != index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by PI {})\n", index + 1 ); + std::abort(); + } + }); + + /* inputs */ + indices.add_inputs( ntk.num_pis() ); + + /* gates */ + ntk.foreach_gate( [&]( node const& n, uint64_t index ){ + assert( ntk.is_and( n ) || ntk.is_xor( n ) ); + if ( ntk.node_to_index( n ) != ntk.num_pis() + index + 1 ) + { + fmt::print( "[e] network is not in normalized index order (violated by node {})\n", ntk.node_to_index( n ) ); + std::abort(); + } + + std::array lits; + ntk.foreach_fanin( n, [&]( signal const& fi, uint64_t index ){ + if ( ntk.node_to_index( ntk.get_node( fi ) ) > ntk.node_to_index( n ) ) + { + fmt::print( "[e] node {} not in topological order\n", ntk.node_to_index( n ) ); + std::abort(); + } + lits[index] = 2*ntk.node_to_index( ntk.get_node( fi ) ) + ntk.is_complemented( fi ); + }); + + if ( ntk.is_and( n ) ) + { + indices.add_and( lits[0u], lits[1u] ); + } + else if ( ntk.is_xor( n ) ) + { + indices.add_xor( lits[0u], lits[1u] ); + } + }); + + /* outputs */ + ntk.foreach_po( [&]( signal const& f ){ + indices.add_output( 2*ntk.node_to_index( ntk.get_node( f ) ) + ntk.is_complemented( f ) ); + }); + + assert( indices.size() == 1u + 2u*ntk.num_gates() + ntk.num_pos() ); +} + +/*! \brief Inserts a xag_index_list into an existing network + * + * **Required network functions:** + * - `create_and` + * - `create_xor` + * - `get_constant` + * + * \param ntk A logic network + * \param begin Begin iterator of signal inputs + * \param end End iterator of signal inputs + * \param indices An index list + * \param fn Callback function + */ +template +void insert( Ntk& ntk, BeginIter begin, EndIter end, xag_index_list const& indices, Fn&& fn ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_and_v, "Ntk does not implement the create_and method" ); + static_assert( has_create_xor_v, "Ntk does not implement the create_xor method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + + 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 ) ) == indices.num_pis() ); + + using signal = typename Ntk::signal; + + std::vector signals; + signals.emplace_back( ntk.get_constant( false ) ); + for ( auto it = begin; it != end; ++it ) + { + signals.push_back( *it ); + } + + indices.foreach_entry( [&]( uint32_t lit0, uint32_t lit1 ){ + assert( lit0 != lit1 ); + uint32_t const i0 = lit0 >> 1; + uint32_t const i1 = lit1 >> 1; + signal const s0 = ( lit0 % 2 ) ? ntk.create_not( signals.at( i0 ) ) : signals.at( i0 ); + signal const s1 = ( lit1 % 2 ) ? ntk.create_not( signals.at( i1 ) ) : signals.at( i1 ); + signals.push_back( lit0 > lit1 ? ntk.create_xor( s0, s1 ) : ntk.create_and( s0, s1 ) ); + }); + + indices.foreach_po( [&]( uint32_t lit ){ + uint32_t const i = lit >> 1; + fn( ( lit % 2 ) ? ntk.create_not( signals.at( i ) ) : signals.at( i ) ); + }); +} + +/*! \brief Converts an xag_index_list to a string + * + * \param indices An index list + * \return A string representation of the index list + */ +inline std::string to_index_list_string( xag_index_list const& indices ) +{ + auto s = fmt::format( "{{{} | {} << 8 | {} << 16", indices.num_pis(), indices.num_pos(), indices.num_gates() ); + + indices.foreach_entry( [&]( uint32_t lit0, uint32_t lit1 ){ + s += fmt::format( ", {}, {}", lit0, lit1 ); + }); + + indices.foreach_po( [&]( uint32_t lit ) { + s += fmt::format( ", {}", lit ); + }); + + s += "}"; + + return s; +} + +/*! \brief Generates a network from an index_list + * + * **Required network functions:** + * - `create_pi` + * - `create_po` + * + * \param ntk A logic network + * \param indices An index list + */ +template +void decode( Ntk& ntk, IndexList const& indices ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + + using signal = typename Ntk::signal; + + std::vector signals( indices.num_pis() ); + std::generate( std::begin( signals ), std::end( signals ), + [&]() { return ntk.create_pi(); }); + + insert( ntk, std::begin( signals ), std::end( signals ), indices, + [&]( signal const& s ){ ntk.create_po( s ); }); +} + +} /* mockturtle */ diff --git a/test/algorithms/equivalence_classes.cpp b/test/algorithms/equivalence_classes.cpp index afd091cf7..9f8d19d1e 100644 --- a/test/algorithms/equivalence_classes.cpp +++ b/test/algorithms/equivalence_classes.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -57,7 +57,7 @@ TEST_CASE( "Synthesize from database for 4-input functions", "[equivalence_class for ( auto i = 0u; i < 100u; ++i ) { kitty::dynamic_truth_table func( 4u ); - kitty::create_random( func ); + kitty::create_random( func, 0xcafeaffe + i ); std::vector transformations; const auto repr = kitty::hybrid_exact_spectral_canonization( func, [&]( auto const& _transformations ) { @@ -70,9 +70,14 @@ TEST_CASE( "Synthesize from database for 4-input functions", "[equivalence_class std::generate( pis.begin(), pis.end(), [&]() { return xag.create_pi(); } ); const auto f = apply_spectral_transformations( xag, transformations, pis, [&]( xag_network& ntk, std::vector const& leaves ) { - return create_from_binary_index_list( ntk, db[repr]->begin(), leaves.begin() )[0u]; + xag_index_list il{*db[repr]}; + std::vector pos; + insert( ntk, std::begin( leaves ), std::begin( leaves ) + il.num_pis(), il, + [&]( xag_network::signal const& s ){ pos.emplace_back( s ); }); + return pos[0]; } ); xag.create_po( f ); + CHECK( simulate( xag, {static_cast( func.num_vars() )} )[0] == func ); } } diff --git a/test/io/index_list.cpp b/test/algorithms/minmc_xags.cpp similarity index 63% rename from test/io/index_list.cpp rename to test/algorithms/minmc_xags.cpp index 209132e65..ea5a82f2a 100644 --- a/test/io/index_list.cpp +++ b/test/algorithms/minmc_xags.cpp @@ -1,25 +1,14 @@ #include -#include -#include -#include - #include -#include #include -#include -#include -#include -#include -#include #include -#include -#include - -#include -#include +#include #include -#include + +#include +#include +#include using namespace mockturtle; @@ -28,13 +17,7 @@ static void check_minmc_xags() { for ( auto const& [cls, tt, repr, expr] : detail::minmc_xags[NumVars] ) { xag_network xag; - std::vector pis( NumVars ); - std::generate( pis.begin(), pis.end(), [&]() { return xag.create_pi(); }); - - for ( auto const& po : create_from_binary_index_list( xag, repr.begin(), pis.begin() ) ) - { - xag.create_po( po ); - } + decode( xag, xag_index_list{repr} ); const auto f = simulate>( xag )[0]; auto f_tt = f.construct(), f_expr = f.construct(); @@ -46,7 +29,7 @@ static void check_minmc_xags() } } -TEST_CASE( "create MC-optumum XAGs from binary index list", "[index_list]" ) +TEST_CASE( "create MC-optumum XAGs from xag_index_list", "[minmc_xags]" ) { check_minmc_xags<0>(); check_minmc_xags<1>(); @@ -73,7 +56,7 @@ static void check_repr_match() } } -TEST_CASE( "check representatives for database functions", "[index_list]" ) +TEST_CASE( "check representatives for database functions", "[minmc_xags]" ) { check_repr_match<0>(); check_repr_match<1>(); diff --git a/test/utils/index_list.cpp b/test/utils/index_list.cpp new file mode 100644 index 000000000..a258de0c3 --- /dev/null +++ b/test/utils/index_list.cpp @@ -0,0 +1,125 @@ +#include + +#include +#include +#include +#include +#include + +using namespace mockturtle; + +TEST_CASE( "decode mig_index_list into mig_network", "[index_list]" ) +{ + std::vector const raw_list{4 | ( 1 << 8 ) | ( 2 << 16 ), 2, 4, 6, 10, 4, 8, 12}; + mig_index_list mig_il{raw_list}; + + mig_network mig; + decode( mig, mig_il ); + + CHECK( mig.num_gates() == 2u ); + CHECK( mig.num_pis() == 4u ); + CHECK( mig.num_pos() == 1u ); + + const auto tt = simulate>( mig )[0]; + CHECK( tt._bits == 0xecc8 ); +} + +TEST_CASE( "encode mig_network into mig_index_list", "[index_list]" ) +{ + mig_network mig; + auto const a = mig.create_pi(); + auto const b = mig.create_pi(); + auto const c = mig.create_pi(); + auto const d = mig.create_pi(); + auto const t0 = mig.create_maj( a, b, c ); + auto const t1 = mig.create_maj( t0, b, d ); + mig.create_po( t1 ); + + mig_index_list mig_il; + encode( mig_il, mig ); + + CHECK( mig_il.num_pis() == 4u ); + CHECK( mig_il.num_pos() == 1u ); + CHECK( mig_il.num_gates() == 2u ); + CHECK( mig_il.size() == 8u ); + CHECK( mig_il.raw() == std::vector{4 | ( 1 << 8 ) | ( 2 << 16 ), 2, 4, 6, 4, 8, 10, 12} ); + CHECK( to_index_list_string( mig_il ) == "{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}" ); +} + +TEST_CASE( "decode abc_index_list into xag_network", "[index_list]" ) +{ + std::vector const raw_list{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 12, 10, 14, 14}; + abc_index_list xag_il{raw_list}; + + xag_network xag; + decode( xag, xag_il ); + + CHECK( xag.num_gates() == 3u ); + CHECK( xag.num_pis() == 4u ); + CHECK( xag.num_pos() == 1u ); + + const auto tt = simulate>( xag )[0]; + CHECK( tt._bits == 0x7888 ); +} + +TEST_CASE( "encode xag_network into abc_index_list", "[index_list]" ) +{ + xag_network xag; + auto const a = xag.create_pi(); + auto const b = xag.create_pi(); + auto const c = xag.create_pi(); + auto const d = xag.create_pi(); + auto const t0 = xag.create_and( a, b ); + auto const t1 = xag.create_and( c, d ); + auto const t2 = xag.create_xor( t0, t1 ); + xag.create_po( t2 ); + + abc_index_list xag_il; + encode( xag_il, xag ); + + CHECK( xag_il.num_pis() == 4u ); + CHECK( xag_il.num_pos() == 1u ); + CHECK( xag_il.num_gates() == 3u ); + CHECK( xag_il.size() == 18u ); + CHECK( xag_il.raw() == std::vector{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 12, 10, 14, 14} ); + CHECK( to_index_list_string( xag_il ) == "{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 12, 10, 14, 14}" ); +} + +TEST_CASE( "decode xag_index_list into xag_network", "[index_list]" ) +{ + std::vector const raw_list{4 | ( 1 << 8 ) | ( 3 << 16 ), 2, 4, 6, 8, 12, 10, 14}; + xag_index_list xag_il{raw_list}; + + xag_network xag; + decode( xag, xag_il ); + + CHECK( xag.num_gates() == 3u ); + CHECK( xag.num_pis() == 4u ); + CHECK( xag.num_pos() == 1u ); + + const auto tt = simulate>( xag )[0]; + CHECK( tt._bits == 0x7888 ); +} + +TEST_CASE( "encode xag_network into xag_index_list", "[index_list]" ) +{ + xag_network xag; + auto const a = xag.create_pi(); + auto const b = xag.create_pi(); + auto const c = xag.create_pi(); + auto const d = xag.create_pi(); + auto const t0 = xag.create_and( a, b ); + auto const t1 = xag.create_and( c, d ); + auto const t2 = xag.create_xor( t0, t1 ); + xag.create_po( t2 ); + + xag_index_list xag_il; + encode( xag_il, xag ); + + CHECK( xag_il.num_pis() == 4u ); + CHECK( xag_il.num_pos() == 1u ); + CHECK( xag_il.num_gates() == 3u ); + CHECK( xag_il.size() == 8u ); + CHECK( xag_il.raw() == std::vector{4 | ( 1 << 8 ) | ( 3 << 16 ), 2, 4, 6, 8, 12, 10, 14} ); + CHECK( to_index_list_string( xag_il ) == "{4 | 1 << 8 | 3 << 16, 2, 4, 6, 8, 12, 10, 14}" ); +}