diff --git a/bench/implicant.cpp b/bench/implicant.cpp new file mode 100644 index 00000000..9977ea20 --- /dev/null +++ b/bench/implicant.cpp @@ -0,0 +1,64 @@ +/* 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 + +using namespace kitty; + +void BM_minterms( benchmark::State& state ) +{ + dynamic_truth_table tt( state.range( 0 ) ); + create_majority( tt ); + + while ( state.KeepRunning() ) + { + get_minterms( tt ); + } +} + +void BM_jbuddies( benchmark::State& state ) +{ + dynamic_truth_table tt( state.range( 0 ) ); + create_majority( tt ); + + const auto minterms = get_minterms( tt ); + + while ( state.KeepRunning() ) + { + for ( auto i = 0u; i < state.range( 0 ); ++i ) + { + get_jbuddies( minterms, 0 ); + } + } +} + +BENCHMARK( BM_minterms )->Arg( 9 )->Arg( 11 )->Arg( 13 )->Arg( 15 ); +BENCHMARK( BM_jbuddies )->Arg( 9 )->Arg( 11 )->Arg( 13 )->Arg( 15 ); + +BENCHMARK_MAIN() diff --git a/docs/changelog.rst b/docs/changelog.rst index 9681ef07..b7b0e783 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -18,6 +18,9 @@ v0.4 (not yet released) * Print all cubes: ``print_cubes`` `#44 `_ +* Generate implicants and prime implicants: ``get_minterms``, ``get_jbuddies``, ``get_prime_implicants_morreale`` + `#46 `_ + v0.3 (February 25, 2018) ------------------------ diff --git a/docs/implicant.rst b/docs/implicant.rst new file mode 100644 index 00000000..b1f1aeb9 --- /dev/null +++ b/docs/implicant.rst @@ -0,0 +1,12 @@ +Implicants +========== + +The header ```` implements methods to find implicants and prime implicants. + +.. doc_brief_table:: + get_minterms + get_jbuddies + get_prime_implicants_morreale + + + diff --git a/docs/index.rst b/docs/index.rst index 6d161a19..1427a9f6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,6 +27,7 @@ Welcome to kitty's documentation! operators print hash + implicant canonization cube permutation diff --git a/include/kitty/implicant.hpp b/include/kitty/implicant.hpp new file mode 100644 index 00000000..f4d85889 --- /dev/null +++ b/include/kitty/implicant.hpp @@ -0,0 +1,240 @@ +/* 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 implicant.hpp + \brief Find implicants and prime implicants + + \author Mathias Soeken +*/ + +#pragma once + +#include +#include + +#include "algorithm.hpp" +#include "cube.hpp" + +namespace kitty +{ + +/*! \brief Computes all minterms + + \param tt Truth table +*/ +template +std::vector get_minterms( const TT& tt ) +{ + std::vector m; + m.reserve( count_ones( tt ) ); + for_each_one_bit( tt, [&m]( auto index ) { + m.emplace_back( index ); + } ); + return m; +} + +/*! \cond PRIVATE */ +template +inline std::vector> get_jbuddies( Iterator begin, Iterator end, uint32_t j ) +{ + std::vector> buddies; + + auto mask = uint32_t( 1 ) << j; + auto k = begin; + auto kk = begin; + + while ( true ) + { + k = std::find_if( k, end, [mask]( auto m ) { return ( m & mask ) == 0; } ); + if ( k == end ) + break; + + if ( kk <= k ) + { + kk = k + 1; + } + + kk = std::find_if( kk, end, [mask, &k]( auto m ) { return m >= ( *k | mask ); } ); + if ( kk == end ) + break; + + if ( ( *k ^ *kk ) >= ( mask << 1 ) ) + { + k = kk; + continue; + } + + if ( *kk == ( *k | mask ) ) + { + buddies.emplace_back( k, kk ); + } + + ++k; + } + + return buddies; +} +/*! \endcond */ + +/*! \brief Computes all j-buddies in a list of minterms + + Computes all pairs \f$(k, k')\f$ such that \f$k < k'\f$ and the two minterms + at indexes \f$k\f$ and \f$k'\f$ only differ in bit \f$j\f$. + + This algorithm is described by Knuth in Exercise TAOCP 7.1.1-29. + + \param minterms Vector of minterms + \param j Bit position +*/ +inline std::vector::const_iterator, std::vector::const_iterator>> get_jbuddies( const std::vector& minterms, uint32_t j ) +{ + return get_jbuddies( minterms.begin(), minterms.end(), j ); +} + +/*! \brief Computes all prime implicants (from minterms) + + This algorithm computes all prime implicants for a list of minterms. The + running time is at most proportional to \f$mn\f$, where \f$m\f$ is the number + of minterms and \f$n\f$ is the number of variables. + + The algorithm is described in Exercise TAOCP 7.1.1-30 by Knuth and is inspired + by the algorithm described in [E. Morreale, IEEE Trans. EC 16(5), 1967, + 611–620]. + + \param minterms Vector of minterms (as integer values) + \param num_vars Number of variables +*/ +inline std::vector get_prime_implicants_morreale( const std::vector& minterms, unsigned num_vars ) +{ + std::vector cubes; + + const auto n = num_vars; + const auto m = minterms.size(); + + std::vector tags( 2 * m + n, 0 ); + std::vector stack( 2 * m + n , 0 ); + + uint32_t mask = ( 1 << n ) - 1; + uint32_t A{}; + + /* P1 */ + + /* Update tags using j-buddy algorithm */ + for ( auto j = 0u; j < n; ++j ) + { + for ( const auto& p : get_jbuddies( minterms, j ) ) + { + const auto k = std::distance( minterms.begin(), p.first ); + const auto kk = std::distance( minterms.begin(), p.second ); + + tags[k] |= ( 1 << j ); + tags[kk] |= ( 1 << j ); + } + } + + auto t = 0u; + for ( auto s = 0u; s < m; ++s ) + { + if ( tags[s] == 0u ) + { + cubes.emplace_back( minterms[s], mask ); + } + else + { + stack[t] = minterms[s]; + tags[t] = tags[s]; + t++; + } + } + + stack.push_back( 0 ); + + while ( true ) + { + /* P2 */ + auto j = 0u; + if ( stack[t] == t ) + { + while ( j < n && ( ( A >> j ) & 1 ) == 0 ) + { + ++j; + } + } + + while ( j < n && ( ( A >> j ) & 1 ) != 0 ) + { + t = stack[t] - 1; + A &= ~( 1 << j ); + ++j; + } + + if ( j >= n ) + { + /* terminate */ + return cubes; + } + + A |= ( 1 << j ); + + /* P3 */ + const auto r = t; + const auto s = stack.begin() + stack[t]; + + for ( const auto& p : get_jbuddies( s, stack.begin() + r, j ) ) + { + const auto k = std::distance( stack.begin(), p.first ); + const auto kk = std::distance( stack.begin(), p.second ); + const auto x = tags[k] & tags[kk] & ~( 1 << j ); + + if ( x == 0 ) + { + cubes.emplace_back( stack[k], ~A & mask ); + } + else + { + ++t; + stack[t] = stack[k]; + tags[t] = x; + } + } + + ++t; + stack[t] = r + 1; + } +} + +/*! \brief Computes all prime implicants (from truth table) + + Computes minterms from truth table and calls overloaded function. + + \param tt Truth table +*/ +template +std::vector get_prime_implicants_morreale( const TT& tt ) +{ + return get_prime_implicants_morreale( get_minterms( tt ), tt.num_vars() ); +} +} // namespace kitty diff --git a/include/kitty/kitty.hpp b/include/kitty/kitty.hpp index 574ef4ee..5ed28fbc 100644 --- a/include/kitty/kitty.hpp +++ b/include/kitty/kitty.hpp @@ -43,6 +43,7 @@ #include "cube.hpp" #include "esop.hpp" #include "hash.hpp" +#include "implicant.hpp" #include "isop.hpp" #include "npn.hpp" #include "operations.hpp" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bb9a41a9..4a40025c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,12 +2,11 @@ add_subdirectory(googletest/googletest) include_directories(googletest/googletest/include) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -Wextra") - file(GLOB FILENAMES *.cpp) add_executable(run_tests ${FILENAMES}) target_link_libraries(run_tests gtest kitty) +target_compile_options(run_tests PUBLIC -Wall -Wextra -g) include(CheckCXXCompilerFlag) check_cxx_compiler_flag("-ftime-report" HAS_FTIME_REPORT) diff --git a/test/implicant.cpp b/test/implicant.cpp new file mode 100644 index 00000000..a1a9969f --- /dev/null +++ b/test/implicant.cpp @@ -0,0 +1,92 @@ +/* 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 "utility.hpp" + +using namespace kitty; + +class ImplicantTest : public kitty::testing::Test +{ +}; + +TEST_F( ImplicantTest, get_minterms ) +{ + auto minterms = get_minterms( from_hex<3>( "e8") ); + EXPECT_EQ( minterms, ( std::vector{3u, 5u, 6u, 7u} ) ); +} + +TEST_F( ImplicantTest, get_jbuddies ) +{ + auto minterms = get_minterms( from_hex<3>( "e8" ) ); + + auto buddies = get_jbuddies( minterms, 0 ); + EXPECT_EQ( buddies.size(), 1u ); + EXPECT_EQ( *buddies.front().first, 6u ); + EXPECT_EQ( *buddies.front().second, 7u ); + + buddies = get_jbuddies( minterms, 1 ); + EXPECT_EQ( buddies.size(), 1u ); + EXPECT_EQ( *buddies.front().first, 5u ); + EXPECT_EQ( *buddies.front().second, 7u ); + + buddies = get_jbuddies( minterms, 2 ); + EXPECT_EQ( buddies.size(), 1u ); + EXPECT_EQ( *buddies.front().first, 3u ); + EXPECT_EQ( *buddies.front().second, 7u ); +} + +TEST_F( ImplicantTest, prime_implicants_morreale ) +{ + for ( auto i = 0u; i < 100u; ++i ) + { + static_truth_table<4> func; + create_random( func ); + const auto cubes = get_prime_implicants_morreale( func ); + + auto tt = func.construct(); + create_from_cubes( tt, cubes ); + EXPECT_EQ( func, tt ); + } + + for ( auto n = 1u; n < 8u; ++n ) + { + for ( auto i = 0u; i < 10u; ++i ) + { + dynamic_truth_table func( n ); + create_random( func ); + const auto cubes = get_prime_implicants_morreale( func ); + + auto tt = func.construct(); + create_from_cubes( tt, cubes ); + EXPECT_EQ( func, tt ); + } + } +}