From 39d5ae2012ac0b38e246747e67332e2ed22a6443 Mon Sep 17 00:00:00 2001 From: Pulkit Agrawal Date: Tue, 30 Mar 2021 12:29:10 +0530 Subject: [PATCH 1/4] Adding support for SD Classification. --- examples/sd.cpp | 68 +++++++++++++++ include/kitty/kitty.hpp | 3 +- include/kitty/npn.hpp | 113 ++++++++++++++++++++++-- include/kitty/operations.hpp | 59 ++++++++++--- include/kitty/sd.hpp | 162 +++++++++++++++++++++++++++++++++++ test/sd.cpp | 57 ++++++++++++ 6 files changed, 444 insertions(+), 18 deletions(-) create mode 100644 examples/sd.cpp create mode 100644 include/kitty/sd.hpp create mode 100644 test/sd.cpp diff --git a/examples/sd.cpp b/examples/sd.cpp new file mode 100644 index 00000000..8b97a566 --- /dev/null +++ b/examples/sd.cpp @@ -0,0 +1,68 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-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. + */ + +#include +#include + +#include + +int main() +{ + + uint32_t num_vars = 2u; + const auto classes_2u = kitty::calculate_sd_represtative_class( num_vars ); + + /* Print the size of the class. */ + std::cout << "[i] enumerated " + << ( 1 << ( 1 << num_vars ) ) << " functions into " + << classes_2u.size() << " classes." << std::endl; + + num_vars = 3u; + const auto classes_3u = kitty::calculate_sd_represtative_class( num_vars ); + + /* Print the size of the class. */ + std::cout << "[i] enumerated " + << ( 1 << ( 1 << num_vars ) ) << " functions into " + << classes_3u.size() << " classes." << std::endl; + + num_vars = 4u; + const auto classes_4u = kitty::calculate_sd_represtative_class( num_vars ); + + /* Print the size of the class. */ + std::cout << "[i] enumerated " + << ( 1 << ( 1 << num_vars ) ) << " functions into " + << classes_4u.size() << " classes." << std::endl; + + num_vars = 5u; + const auto classes_5u = kitty::calculate_sd_represtative_class( num_vars ); + + /* Print the size of the class.*/ + std::cout << "[i] enumerated " + << ( 1 << ( 1 << num_vars ) ) << " functions into " + << classes_5u.size() << " classes." << std::endl; + + + return 0; +} diff --git a/include/kitty/kitty.hpp b/include/kitty/kitty.hpp index b1d0d4a5..58156774 100644 --- a/include/kitty/kitty.hpp +++ b/include/kitty/kitty.hpp @@ -48,6 +48,7 @@ #include "implicant.hpp" #include "isop.hpp" #include "npn.hpp" +#include "sd.hpp" #include "operations.hpp" #include "operators.hpp" #include "permutation.hpp" @@ -64,4 +65,4 @@ / \ / ___ \ \/___\/ -*/ \ No newline at end of file +*/ diff --git a/include/kitty/npn.hpp b/include/kitty/npn.hpp index e3a654e8..31854cb2 100755 --- a/include/kitty/npn.hpp +++ b/include/kitty/npn.hpp @@ -90,7 +90,7 @@ std::tuple> exact_p_canonization( const TT& t /* Special case for n = 1 */ if ( num_vars == 1 ) { - return std::make_tuple( tt, 0u, std::vector{0} ); + return std::make_tuple( tt, 0u, std::vector{ 0 } ); } assert( num_vars >= 2 && num_vars <= 7 ); @@ -171,7 +171,7 @@ std::tuple> exact_npn_canonization( const TT& if ( num_vars == 1 ) { const auto bit1 = get_bit( tt, 1 ); - return std::make_tuple( unary_not_if( tt, bit1 ), static_cast( bit1 << 1 ), std::vector{0} ); + return std::make_tuple( unary_not_if( tt, bit1 ), static_cast( bit1 << 1 ), std::vector{ 0 } ); } assert( num_vars >= 2 && num_vars <= 6 ); @@ -262,6 +262,106 @@ std::tuple> exact_npn_canonization( const TT& return std::make_tuple( tmin, phase, perm ); } +/*! \brief Exact NPN Represtative + + Given a truth table, this function finds the lexicographically smallest truth + table in its NPN class, called NPN representative. Two functions are in the + same NPN class, if one can obtain one from the other by input negation, input + permutation, and output negation. + + The function can accept a callback as second parameter which is called for + every visited function when trying out all combinations. This allows to + exhaustively visit the whole NPN class. + + The function returns a NPN representative (truth table). + + \param tt The truth table (with at most 6 variables) + \param fn Callback for each visited truth table in the class (default does nothing) + \return NPN representative +*/ +template )> +TT exact_npn_representative( const TT& tt, Callback&& fn = kitty::detail::exact_npn_canonization_null_callback ) +{ + + static_assert( kitty::is_complete_truth_table::value, "Can only be applied on complete truth tables." ); + + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + const auto bit = get_bit( tt, 0 ); + return kitty::unary_not_if( tt, bit ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + const auto bit1 = get_bit( tt, 1 ); + return kitty::unary_not_if( tt, bit1 ); + } + + assert( num_vars >= 2 && num_vars <= 6 ); + + auto t1 = tt, t2 = ~tt; + auto tmin = std::min( t1, t2 ); + + fn( t1 ); + fn( t2 ); + + const auto& swaps = kitty::detail::swaps[num_vars - 2u]; + const auto& flips = kitty::detail::flips[num_vars - 2u]; + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + kitty::swap_adjacent_inplace( t1, pos ); + kitty::swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + tmin = std::min( t1, t2 ); + } + } + + for ( std::size_t j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + kitty::swap_adjacent_inplace( t1, 0 ); + kitty::flip_inplace( t1, pos ); + kitty::swap_adjacent_inplace( t2, 0 ); + kitty::flip_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + tmin = std::min( t1, t2 ); + } + + for ( std::size_t i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + kitty::swap_adjacent_inplace( t1, pos ); + kitty::swap_adjacent_inplace( t2, pos ); + + fn( t1 ); + fn( t2 ); + + if ( t1 < tmin || t2 < tmin ) + { + tmin = std::min( t1, t2 ); + } + } + } + + return tmin; +} + /*! \brief Flip-swap NPN heuristic This algorithm will iteratively try to reduce the numeric value of the truth @@ -292,7 +392,7 @@ std::tuple> flip_swap_npn_canonization( const std::vector perm( num_vars ); std::iota( perm.begin(), perm.end(), 0u ); - uint32_t phase{0u}; + uint32_t phase{ 0u }; auto npn = tt; auto improvement = true; @@ -438,7 +538,6 @@ void sifting_p_canonization_loop( TT& p, uint32_t& phase, std::vector& } forward = !forward; } - } } /* namespace detail */ /*! \endcond */ @@ -471,7 +570,7 @@ std::tuple> sifting_npn_canonization( const T /* initialize permutation and phase */ std::vector perm( num_vars ); std::iota( perm.begin(), perm.end(), 0u ); - uint32_t phase{0u}; + uint32_t phase{ 0u }; if ( num_vars < 2 ) { @@ -528,7 +627,7 @@ std::tuple> sifting_p_canonization( const TT& /* initialize permutation and phase */ std::vector perm( num_vars ); std::iota( perm.begin(), perm.end(), 0u ); - uint32_t phase{0u}; + uint32_t phase{ 0u }; if ( num_vars < 2u ) { @@ -593,4 +692,4 @@ TT create_from_npn_config( const std::tuple>& return res; } -} /* namespace kitty */ \ No newline at end of file +} /* namespace kitty */ diff --git a/include/kitty/operations.hpp b/include/kitty/operations.hpp index 31a8d486..c9610f96 100644 --- a/include/kitty/operations.hpp +++ b/include/kitty/operations.hpp @@ -69,12 +69,12 @@ template inline TT unary_not_if( const TT& tt, bool cond ) { #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4146) +#pragma warning( push ) +#pragma warning( disable : 4146 ) #endif const auto mask = -static_cast( cond ); #ifdef _MSC_VER -#pragma warning(pop) +#pragma warning( pop ) #endif return unary_operation( tt, [mask]( auto a ) { return a ^ mask; } ); } @@ -142,10 +142,9 @@ inline TT mux_var( uint8_t var_index, const TT& then_, const TT& else_ ) auto res = then_.construct(); std::transform( then_.begin(), then_.end(), else_.begin(), res.begin(), - [&]( auto a, auto b ) { - return ( j++ % ( 2 * step ) ) < step ? b : a; - } - ); + [&]( auto a, auto b ) { + return ( j++ % ( 2 * step ) ) < step ? b : a; + } ); return res; } @@ -483,7 +482,7 @@ void swap_adjacent_inplace( TT& tt, uint8_t var_index ) auto it = std::begin( tt._bits ); while ( it != std::end( tt._bits ) ) { - for ( auto i = decltype( step ){0}; i < step; ++i ) + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) { std::swap( *( it + i + step ), *( it + i + 2 * step ) ); } @@ -567,7 +566,7 @@ void swap_inplace( TT& tt, uint8_t var_index1, uint8_t var_index2 ) auto it = std::begin( tt._bits ); while ( it != std::end( tt._bits ) ) { - for ( auto i = decltype( step ){0}; i < step; ++i ) + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) { const auto low_to_high = ( *( it + i ) & detail::projections[var_index1] ) >> shift; const auto high_to_low = ( *( it + i + step ) << shift ) & detail::projections[var_index1]; @@ -666,7 +665,7 @@ void flip_inplace( TT& tt, uint8_t var_index ) auto it = std::begin( tt._bits ); while ( it != std::end( tt._bits ) ) { - for ( auto i = decltype( step ){0}; i < step; ++i ) + for ( auto i = decltype( step ){ 0 }; i < step; ++i ) { std::swap( *( it + i ), *( it + i + step ) ); } @@ -1191,4 +1190,44 @@ inline TT shift_with_mask( const TT& f, uint8_t mask ) return copy; } +/*! \brief Calculates the dual function of the passed truth table. + + \param tt Truth table + \return dual of given truth table +*/ +template::value>> +kitty::dynamic_truth_table dual_of( const TT& tt ) +{ + auto numvars = tt.num_vars(); + auto tt1 = tt; + auto tt2 = ~tt1; + for ( auto i = 0u; i < numvars; i++ ) + { + tt1 = flip( tt1, i ); + } + return ~tt1; +} + +/*! \brief Extends a given truth table with 'n' variables to + 'n+1' variables. To extend, it appends the given truth table + in the end of the given truth table. + + \param tt Truth table + \return extended Truth Table +*/ +template::value>> +kitty::dynamic_truth_table extend_tt( const TT& tt ) +{ + int num_vars = (int)tt.num_vars(); + kitty::dynamic_truth_table extended_tt( num_vars + 1 ); + for ( int i = 0; i < (int)extended_tt.num_bits(); i++ ) + { + if ( kitty::get_bit( tt, i % ( tt.num_bits() ) ) == 1 ) + { + kitty::set_bit( extended_tt, i ); + } + } + return extended_tt; +} + } // namespace kitty \ No newline at end of file diff --git a/include/kitty/sd.hpp b/include/kitty/sd.hpp new file mode 100644 index 00000000..0ce3fbec --- /dev/null +++ b/include/kitty/sd.hpp @@ -0,0 +1,162 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file sd.hpp + \brief Implements SD canonization algorithms + + \author Pulkit Agrawal and Shubham Rai +*/ + +#pragma once + +#include + +#include "npn.hpp" +#include "operations.hpp" +#include "constructors.hpp" +#include "hash.hpp" + +namespace kitty +{ + +/*! \brief NPN Represtatives Class + + This function returns an unordered set of all the NPN represtatives for + a given number of variables. + + \param num_vars Number of variables in the truth tables of the NPN reprentative class. + \return NPN reprentative class +*/ +std::unordered_set> calculate_npn_represtative_class( uint8_t num_vars ) +{ + /* compute NPN classe */ + std::unordered_set> classes; + kitty::dynamic_truth_table tt( num_vars ); + do + { + /* apply NPN canonization and add resulting representative to set */ + classes.insert( exact_npn_representative( tt ) ); + + /* increment truth table */ + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + + return classes; +} + +/*! \brief Exact SD Represtative + + Given a truth table, this function finds the lexicographically smallest truth + table in its SD class, called SD representative. Two functions are in the + same SD class, if the dual of the given function is equal to the given function, + also known as Self-Dual Functions. + + The function returns a SD representative (truth table). + + \param tt The truth table (with at most 6 variables) + \return SD representative +*/ +template::value>> +kitty::dynamic_truth_table exact_sd_canonization( const TT& tt ) +{ + + int num_vars = tt.num_vars(); + kitty::dynamic_truth_table sd_tt( num_vars + 1 ), a( num_vars + 1 ); + create_nth_var( a, num_vars ); + + /* extending the tt from 'num_vars' to 'num_vars+1' */ + kitty::dynamic_truth_table extended_tt( num_vars + 1 ); + + for ( int i = 0; i < (int)extended_tt.num_bits(); i++ ) + { + if ( kitty::get_bit( tt, i % ( tt.num_bits() ) ) == 1 ) + { + kitty::set_bit( extended_tt, i ); + } + } + + /* Creating the tt with dual of extended_tt*/ + auto numvars = extended_tt.num_vars(); + auto tt1 = extended_tt; + auto tt2 = ~tt1; + + for ( auto i = 0u; i < numvars; i++ ) + { + tt1 = flip( tt1, i ); + } + + auto dual_of_extended_tt = ~tt1; + + sd_tt = kitty::binary_or( kitty::binary_and( extended_tt, a ), kitty::binary_and( ~a, dual_of_extended_tt ) ); + + /* apply NPN canonization and add resulting representative to set */ + return exact_npn_representative( sd_tt ); +} + +/*! \brief SD Represtatives Class + + This function returns an unordered set of all the SD represtatives for + a given number of variables. + + \param num_vars Number of variables in the truth tables of the SD reprentative class. + \return unordered set of SD reprentative +*/ +std::unordered_set> calculate_sd_represtative_class( uint8_t num_vars ) +{ + /* compute SD classes */ + auto npn_class = calculate_npn_represtative_class( num_vars ); + + /* classes is the class which is returned containing all the tt in SD classification. */ + std::unordered_set> classes; + std::unordered_set>::iterator itr; + + /* initializing the iterator */ + itr = npn_class.begin(); + + kitty::dynamic_truth_table sd_tt( num_vars + 1 ), a( num_vars + 1 ); + create_nth_var( a, num_vars ); + + /* extending the tt from 'num_vars' to 'num_vars+1' */ + kitty::dynamic_truth_table extended_tt( num_vars + 1 ); + + do + { + + extended_tt = extend_tt( ( *itr ) ); + + sd_tt = kitty::binary_or( kitty::binary_and( extended_tt, a ), kitty::binary_and( ~a, dual_of( extended_tt ) ) ); + + /* apply NPN canonization and add resulting representative to set */ + classes.insert( kitty::exact_npn_representative( sd_tt ) ); + + /* increment to next tt in the class. */ + itr++; + } while ( itr != npn_class.end() ); + + return classes; +} + +} // namespace kitty diff --git a/test/sd.cpp b/test/sd.cpp new file mode 100644 index 00000000..8b3b564e --- /dev/null +++ b/test/sd.cpp @@ -0,0 +1,57 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2021 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include + +#include + +#include "utility.hpp" + +using namespace kitty; + +class SDTest : public kitty::testing::Test +{ +protected: + template + void check_sd( const uint32_t num_vars, const int number_of_classes ) const + { + const auto res = calculate_sd_represtative_class( num_vars ); + ASSERT_EQ( res.size(), number_of_classes ); + } +}; + +TEST_F( SDTest, class_4u_size ) +{ + check_sd<4>( 4u, 83 ); +} + +TEST_F( SDTest, class_3u_size ) +{ + check_sd<3>( 3u, 7 ); +} From 259672e9d4b7073bd22f0949822be0d0e82ff6f7 Mon Sep 17 00:00:00 2001 From: pulkitag22 Date: Tue, 30 Mar 2021 12:47:30 +0530 Subject: [PATCH 2/4] modifications in example/CmakeLists.txt to support sd.cpp example. --- examples/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3258593f..6f7bac67 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,3 +15,4 @@ add_example(spectral_enumeration spectral_enumeration.cpp) add_example(spectral_enumeration_file spectral_enumeration_file.cpp) add_example(spectral_enumeration_map spectral_enumeration_map.cpp) add_example(spectral_enumeration_fuller spectral_enumeration_fuller.cpp) +add_example(sd sd.cpp) From 315f91bb63906e422b6e7875f700d077f2098920 Mon Sep 17 00:00:00 2001 From: pulkitag22 Date: Tue, 30 Mar 2021 12:49:55 +0530 Subject: [PATCH 3/4] clang format updates. --- examples/sd.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/sd.cpp b/examples/sd.cpp index 8b97a566..b5571625 100644 --- a/examples/sd.cpp +++ b/examples/sd.cpp @@ -38,8 +38,8 @@ int main() std::cout << "[i] enumerated " << ( 1 << ( 1 << num_vars ) ) << " functions into " << classes_2u.size() << " classes." << std::endl; - - num_vars = 3u; + + num_vars = 3u; const auto classes_3u = kitty::calculate_sd_represtative_class( num_vars ); /* Print the size of the class. */ @@ -63,6 +63,5 @@ int main() << ( 1 << ( 1 << num_vars ) ) << " functions into " << classes_5u.size() << " classes." << std::endl; - return 0; } From edbfcc1a1e013cbdf190ac90d87553b174b9c751 Mon Sep 17 00:00:00 2001 From: pulkitag22 Date: Tue, 30 Mar 2021 13:01:20 +0530 Subject: [PATCH 4/4] modified example sd.cpp file to comment the example case for 5 input. --- examples/sd.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/sd.cpp b/examples/sd.cpp index b5571625..361bec64 100644 --- a/examples/sd.cpp +++ b/examples/sd.cpp @@ -55,13 +55,14 @@ int main() << ( 1 << ( 1 << num_vars ) ) << " functions into " << classes_4u.size() << " classes." << std::endl; - num_vars = 5u; + /*Disabling this example as it takes a long time to evaluate NPN (dependent) class for 6 variables.*/ + /*num_vars = 5u; const auto classes_5u = kitty::calculate_sd_represtative_class( num_vars ); - /* Print the size of the class.*/ + // Print the size of the class. std::cout << "[i] enumerated " << ( 1 << ( 1 << num_vars ) ) << " functions into " << classes_5u.size() << " classes." << std::endl; - +*/ return 0; }