diff --git a/docs/canonization.rst b/docs/canonization.rst index 67069564..2e62b95b 100644 --- a/docs/canonization.rst +++ b/docs/canonization.rst @@ -20,10 +20,6 @@ The header ```` implements canonization algorithms based on linear and affine transformations. .. doc_brief_table:: - delta_swap_inplace - delta_swap - permute_with_masks_inplace - permute_with_masks exact_linear_canonization exact_linear_output_canonization exact_affine_canonization diff --git a/docs/changelog.rst b/docs/changelog.rst index 6b965728..8bac499c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -33,9 +33,12 @@ v0.3 (not yet released) * Linear and affine canonization: ``exact_linear_canonization``, ``exact_linear_output_canonization``, ``exact_affine_canonization``, ``exact_affine_output_canonization`` `#36 `_ -* Compute PPRM ESOP for truth table +* Compute PPRM ESOP for truth table: ``esop_from_pprm`` `#38 `_ +* Compute permutation masks and delta-swap operations: ``delta_swap_inplace``, ``delta_swap``, ``permute_with_masks_inplace``, ``permute_with_masks``, ``compute_permutation_masks``, + `#40 `_ + v0.2 (December 21, 2017) ------------------------ diff --git a/docs/index.rst b/docs/index.rst index fa00b73f..04f134f7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,6 +29,7 @@ Welcome to kitty's documentation! hash canonization cube + permutation esop isop reference diff --git a/docs/permutation.rst b/docs/permutation.rst new file mode 100644 index 00000000..c4000383 --- /dev/null +++ b/docs/permutation.rst @@ -0,0 +1,13 @@ +Permutations +============ + +The header ```` implements permutation algorithms to +be used with truth tables. + +.. doc_brief_table:: + delta_swap_inplace + delta_swap + permute_with_masks_inplace + permute_with_masks + compute_permutation_masks + diff --git a/include/kitty/affine.hpp b/include/kitty/affine.hpp index 09d6b7e5..752d4b77 100644 --- a/include/kitty/affine.hpp +++ b/include/kitty/affine.hpp @@ -43,83 +43,48 @@ namespace kitty { -/*! \brief Applies delta-swap operation - - The delta-swap operation swaps all position pairs \f$(i, i+\delta)\f$, for - which \f$\omega\f$ is set 1 at position \f$i\f$. - - See also Eq. 7.1.3-(69) in The Art of Computer Programming. - - \param tt Truth table - \param delta Index distance delta - \param omega Enable mask -*/ +/*! \cond PRIVATE */ +namespace detail +{ +/* all delta-swap operations have been optimized to work with integer masks omega */ template -inline void delta_swap_inplace( TT& tt, uint64_t delta, uint64_t omega ) +inline void delta_swap_inplace_opt( TT& tt, uint64_t delta, uint64_t omega ) { assert( tt.num_vars() <= 6u ); const uint64_t y = ( tt._bits[0] ^ ( tt._bits[0] >> delta ) ) & omega; tt._bits[0] = tt._bits[0] ^ y ^ ( y << delta ); } -/*! \cond PRIVATE */ template -inline void delta_swap_inplace( static_truth_table& tt, uint64_t delta, uint64_t omega ) +inline void delta_swap_inplace_opt( static_truth_table& tt, uint64_t delta, uint64_t omega ) { assert ( NumVars <= 6 ); const uint64_t y = ( tt._bits ^ ( tt._bits >> delta ) ) & omega; tt._bits = tt._bits ^ y ^ ( y << delta ); } -/*! \endcond */ - -/*! \brief Applies delta-swap operation - Out-of-place variant for `delta_swap_inplace`. -*/ template -TT delta_swap( const TT& tt, uint64_t delta, uint64_t omega ) -{ - auto copy = tt; - delta_swap_inplace( copy, delta, omega ); - return copy; -} - -/*! \brief Permutes a truth table using a sequence of delta-swaps - - Masks is an array containing the \f$\omega\f$ masks. The \f$\delta\f$ values - are chosen as increasing and decreasing powers of 2, as described in Eq. - 7.1.3-(71) of The Art of Computer Programming. - - \param tt Truth table - \param masks Array of omega-masks -*/ -template -void permute_with_masks_inplace( TT& tt, uint64_t const* masks ) +void permute_with_masks_inplace_opt( TT& tt, uint64_t const* masks ) { for ( auto k = 0; k < tt.num_vars(); ++k ) { - delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[k] ); + delta_swap_inplace_opt( tt, uint64_t( 1 ) << k, masks[k] ); } for ( int k = tt.num_vars() - 2, i = tt.num_vars(); k >= 0; --k, ++i ) { - delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[i] ); + delta_swap_inplace_opt( tt, uint64_t( 1 ) << k, masks[i] ); } } -/*! \brief Permutes a truth table using a sequence of delta-swaps - - Out-of-place variant of `permute_with_masks_inplace`. -*/ template -TT permute_with_masks( const TT& tt, uint64_t const* masks ) +TT permute_with_masks_opt( const TT& tt, uint64_t const* masks ) { auto copy = tt; - permute_with_masks_inplace( copy, masks ); + permute_with_masks_inplace_opt( copy, masks ); return copy; } -/*! \cond PRIVATE */ template inline void for_each_permutation_mask( unsigned num_vars, Fn&& fn ) { @@ -135,6 +100,7 @@ inline void for_each_permutation_mask( unsigned num_vars, Fn&& fn ) fn( &detail::linear_masks[i] ); } } +} /*! \endcond */ /*! \brief Applies exact linear classification @@ -150,8 +116,8 @@ TT exact_linear_canonization( const TT& tt ) { auto min = tt; - for_each_permutation_mask( tt.num_vars(), [&min, &tt]( const auto* mask ) { - min = std::min( min, permute_with_masks( tt, mask ) ); + detail::for_each_permutation_mask( tt.num_vars(), [&min, &tt]( const auto* mask ) { + min = std::min( min, detail::permute_with_masks_opt( tt, mask ) ); }); return min; diff --git a/include/kitty/kitty.hpp b/include/kitty/kitty.hpp index 4985658d..05d332db 100644 --- a/include/kitty/kitty.hpp +++ b/include/kitty/kitty.hpp @@ -46,6 +46,7 @@ #include "npn.hpp" #include "operations.hpp" #include "operators.hpp" +#include "permutation.hpp" #include "print.hpp" #include "spectral.hpp" diff --git a/include/kitty/permutation.hpp b/include/kitty/permutation.hpp new file mode 100644 index 00000000..3c46e800 --- /dev/null +++ b/include/kitty/permutation.hpp @@ -0,0 +1,284 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2018 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 permutation.hpp + \brief Efficient permutation of truth tables + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "bit_operations.hpp" +#include "operators.hpp" +#include "print.hpp" + +namespace kitty +{ + +namespace detail +{ +template +std::pair compute_permutation_masks_pair( const TT& tt, std::vector& left, std::vector& right, unsigned step ) +{ + const auto n = tt.num_vars(); + const auto loops = ( 1 << ( n - 1 - step ) ); + const auto diff = ( 1 << step ); + + const auto offset = 1 << ( n - 1 ); + + struct node_t + { + std::vector::iterator a, b; /* numbers in left or right */ + unsigned lf, rf; /* left field right field */ + bool visited = false; + }; + + std::vector nodes( tt.num_bits() ); + + /* compute graph */ + auto idx1 = 0u, idx2 = 0u; + auto it1 = std::begin( left ); + for ( auto l1 = 0; l1 < loops; ++l1 ) + { + for ( auto c1 = 0; c1 < diff; ++c1 ) + { + auto it2 = std::begin( right ); + idx2 = offset; + + nodes[idx1].a = it1; + nodes[idx1].b = it1 + diff; + + for ( auto l2 = 0; l2 < loops; ++l2 ) + { + for ( auto c2 = 0; c2 < diff; ++c2 ) + { + if ( idx1 == 0u ) + { + nodes[idx2].a = it2; + nodes[idx2].b = it2 + diff; + } + + /* pair elements */ + auto& n1 = nodes[idx1]; + auto& n2 = nodes[idx2]; + + /* connect graph */ + if ( *n1.a == *n2.a ) + { + n1.lf = idx2; + n2.lf = idx1; + } + else if ( *n1.a == *n2.b ) + { + n1.lf = idx2; + n2.rf = idx1; + } + if ( *n1.b == *n2.a ) + { + n1.rf = idx2; + n2.lf = idx1; + } + else if ( *n1.b == *n2.b ) + { + n1.rf = idx2; + n2.rf = idx1; + } + + ++it2; + ++idx2; + } + it2 += diff; + } + ++it1; + ++idx1; + } + it1 += diff; + } + + /* traverse graph and compute masks */ + auto mask_left = tt.construct(); + auto mask_right = tt.construct(); + + while ( true ) + { + auto idx = 0; + + while ( idx < offset && nodes[idx].visited ) + { + ++idx; + } + + if ( idx == offset ) + break; + + auto left_side = true; + auto nr = *nodes[idx].a; + auto start = idx; + + do + { + auto& n = nodes[idx]; + + auto match = *n.a == nr; + + nr = match ? *n.b : *n.a; + idx = match ? n.rf : n.lf; + n.visited = true; + + if ( left_side != match ) + { + std::swap( *n.a, *n.b ); + + if ( left_side ) + { + set_bit( mask_left, std::distance( left.begin(), n.a ) ); + } + else + { + set_bit( mask_right, std::distance( right.begin(), n.a ) ); + } + } + + left_side = !left_side; + + } while ( idx != start ); + } + + return std::make_pair( mask_left, mask_right ); +} +} + +/*! \brief Applies delta-swap operation + + The delta-swap operation swaps all position pairs \f$(i, i+\delta)\f$, for + which \f$\omega\f$ is set 1 at position \f$i\f$. + + See also Eq. 7.1.3-(69) in The Art of Computer Programming. + + \param tt Truth table + \param delta Index distance delta + \param omega Enable mask +*/ +template +inline void delta_swap_inplace( TT& tt, uint64_t delta, const TT& omega ) +{ + const auto y = ( tt ^ ( tt >> delta ) ) & omega; + tt = tt ^ y ^ ( y << delta ); +} + +/*! \brief Applies delta-swap operation + + Out-of-place variant for `delta_swap_inplace`. +*/ +template +TT delta_swap( const TT& tt, uint64_t delta, const TT& omega ) +{ + auto copy = tt; + delta_swap_inplace( copy, delta, omega ); + return copy; +} + +/*! \brief Permutes a truth table using a sequence of delta-swaps + + Masks is an array containing the \f$\omega\f$ masks. The \f$\delta\f$ values + are chosen as increasing and decreasing powers of 2, as described in Eq. + 7.1.3-(71) of The Art of Computer Programming. + + \param tt Truth table + \param masks Array of omega-masks +*/ +template +void permute_with_masks_inplace( TT& tt, const std::vector& masks ) +{ + for ( auto k = 0; k < tt.num_vars(); ++k ) + { + delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[k] ); + } + + for ( int k = tt.num_vars() - 2, i = tt.num_vars(); k >= 0; --k, ++i ) + { + delta_swap_inplace( tt, uint64_t( 1 ) << k, masks[i] ); + } +} + +/*! \brief Permutes a truth table using a sequence of delta-swaps + + Out-of-place variant of `permute_with_masks_inplace`. +*/ +template +TT permute_with_masks( const TT& tt, const std::vector& masks ) +{ + auto copy = tt; + permute_with_masks_inplace( copy, masks ); + return copy; +} + +/*! \brief Computes permutation bitmasks + + These bitmasks can be used with the `permute_with_masks` algorithm. The + algorithm to compute these masks is described in The Art of Computer + Programming, Section 7.1.3 'Bit permutation in general'. + + The input truth table can be arbitrary but is used to determine the type and + size of of the returned permutation masks. + + \param tt Base truth table to derive types and size + \param permutation Permutation +*/ +template +std::vector compute_permutation_masks( const TT& tt, const std::vector& permutation ) +{ + std::vector masks; + + std::vector left( permutation.size() ), right = permutation; + std::iota( left.begin(), left.end(), 0u ); + + for ( auto i = 0u; i < tt.num_vars() - 1u; ++i ) + { + const auto pair = detail::compute_permutation_masks_pair( tt, left, right, i ); + + masks.insert( masks.begin() + i, pair.second ); + masks.insert( masks.begin() + i, pair.first ); + } + + auto mask = tt.construct(); + for ( uint64_t i = 0u; i < ( tt.num_bits() >> uint64_t( 1 ) ); ++i ) + { + if ( left[i] != right[i] ) + { + set_bit( mask, i ); + } + } + masks.insert( masks.begin() + tt.num_vars() - 1, mask ); + + return masks; +} + +} // namespace kitty \ No newline at end of file diff --git a/test/affine.cpp b/test/affine.cpp index 9805b4b4..9e71378c 100644 --- a/test/affine.cpp +++ b/test/affine.cpp @@ -74,11 +74,6 @@ class AffineTest : public kitty::testing::Test } }; -TEST_F( AffineTest, delta_swap ) -{ - EXPECT_EQ( delta_swap( from_hex<2>( "1" ), 3, 1 ), from_hex<2>( "8") ); -} - TEST_F( AffineTest, count_linear_static ) { EXPECT_EQ( canonization<2>( exact_linear_canonization> ), 8u ); diff --git a/test/permutation.cpp b/test/permutation.cpp new file mode 100644 index 00000000..2c211eae --- /dev/null +++ b/test/permutation.cpp @@ -0,0 +1,95 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017-2018 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace kitty; + +TEST( PermutationTest, small_permutations_static ) +{ + static_truth_table<3> base; + std::vector perm{0u, 2u, 3u, 5u, 7u, 1u, 4u, 6u}; + + const auto masks = compute_permutation_masks( base, perm ); + EXPECT_EQ( masks.size(), 5u ); + + for ( auto i = 0u; i < 1000u; ++i ) + { + auto f = base.construct(); + create_random( f ); + + const auto f_p = permute_with_masks( f, masks ); + + for ( auto i = 0u; i < perm.size(); ++i ) + { + EXPECT_EQ( get_bit( f, perm[i] ), get_bit( f_p, i ) ); + } + } +} + +TEST( PermutationTest, random_permutation_masks ) +{ + constexpr const auto n = 8u; + + /* create random truth table by random swaps */ + std::vector perm( 1 << n ); + std::iota( perm.begin(), perm.end(), 0u ); + + std::default_random_engine gen( std::chrono::system_clock::now().time_since_epoch().count() ); + std::uniform_int_distribution dist( 0u, perm.size() - 1 ); + + for ( auto i = 0; i < perm.size(); ++i ) + { + std::swap( perm[dist( gen )], perm[dist( gen )] ); + } + + static_truth_table base; + const auto masks = compute_permutation_masks( base, perm ); + EXPECT_EQ( masks.size(), 2 * n - 1 ); + + for ( auto i = 0u; i < 10u; ++i ) + { + auto f = base.construct(); + create_random( f ); + + const auto f_p = permute_with_masks( f, masks ); + + for ( auto i = 0u; i < perm.size(); ++i ) + { + EXPECT_EQ( get_bit( f, perm[i] ), get_bit( f_p, i ) ); + } + } +} \ No newline at end of file