unsigned additional_garbage( const binary_truth_table& base, std::vector<unsigned>& values ) { std::map<unsigned, unsigned> output_value_count; for ( binary_truth_table::const_iterator it = base.begin(); it != base.end(); ++it ) { binary_truth_table::cube_type out( it->second.first, it->second.second ); unsigned number = truth_table_cube_to_number( out ); if ( output_value_count.find( number ) == output_value_count.end() ) { output_value_count.insert( std::make_pair( number, 1 ) ); } else { ++output_value_count[number]; } } // copy the highest count to mu, e.g. ([0,4], [1,3]) -> 4 unsigned mu = boost::max_element( output_value_count, []( const std::map<unsigned,unsigned>::value_type& a, const std::map<unsigned,unsigned>::value_type& b ) { return a.second < b.second; } )->second; using boost::adaptors::map_keys; boost::push_back( values, output_value_count | map_keys ); // TODO map_values here instead? return (unsigned)ceil( log( (double)mu ) / log( 2.0 ) ); }
bool embed_truth_table( binary_truth_table& spec, const binary_truth_table& base, properties::ptr settings, properties::ptr statistics ) { std::string garbage_name = get<std::string>( settings, "garbage_name", "g" ); std::vector<unsigned> output_order = get<std::vector<unsigned> >( settings, "output_order", std::vector<unsigned>() ); timer<properties_timer> t; if ( statistics ) { properties_timer rt( statistics ); t.start( rt ); } /* get number of additional garbage lines needed */ std::vector<unsigned> values; unsigned ag = additional_garbage( base, values ); ag = (unsigned)std::max( (int)ag, (int)base.num_inputs() - (int)base.num_outputs() ); unsigned cons = base.num_outputs() + ag - base.num_inputs(); /* we don't need more than that */ assert( base.num_inputs() <= base.num_outputs() + ag ); /* new number of bits */ std::map<unsigned, unsigned> new_spec; unsigned new_bw = base.num_outputs() + ag; /* all outputs which are left */ std::vector<unsigned> all_outputs( 1u << new_bw ); std::copy( boost::make_counting_iterator( 0u ), boost::make_counting_iterator( 1u << new_bw ), all_outputs.begin() ); { /* greedy method */ std::map<unsigned,std::vector<unsigned> > output_assignments; /* output order */ if ( output_order.size() != base.num_outputs() ) { output_order.clear(); std::copy( boost::make_counting_iterator( 0u ), boost::make_counting_iterator( base.num_outputs() ), std::back_inserter( output_order ) ); } /* not in output order for filling up */ std::vector<unsigned> left_positions; for ( unsigned order = 0u; order < new_bw; ++order ) { if ( std::find( output_order.begin(), output_order.end(), order ) == output_order.end() ) { left_positions += order; } } /* since you append the garbage to the end, just count the numbers */ for ( std::vector<unsigned>::const_iterator itValue = values.begin(); itValue != values.end(); ++itValue ) { unsigned base = 0u; for ( unsigned j = 0u; j < output_order.size(); ++j ) { unsigned bit_in_value = !!( *itValue & ( 1u << ( output_order.size() - 1u - j ) ) ); unsigned bit_pos_in_base = new_bw - 1u - output_order.at( j ); base |= ( bit_in_value << bit_pos_in_base ); } /* create the values from (value)0..0 to (value)1..1 with rebaset to output_order */ std::vector<unsigned> assignments; for ( unsigned j = 0u; j < ( 1u << ag ); ++j ) { unsigned assignment = base; for ( unsigned k = 0; k < ag; ++k ) { unsigned bit_in_value = !!( j & ( 1u << ( ag - 1u - k ) ) ); unsigned bit_pos_in_base = new_bw - 1u - left_positions.at( k ); assignment |= ( bit_in_value << bit_pos_in_base ); } assignments += assignment; } output_assignments[*itValue] = assignments; } /* truth table is in order */ for ( binary_truth_table::const_iterator it = base.begin(); it != base.end(); ++it ) { /* input value */ binary_truth_table::cube_type in( it->first.first, it->first.second ); unsigned number_in = truth_table_cube_to_number( in ); /* output value */ binary_truth_table::cube_type out( it->second.first, it->second.second ); unsigned number = truth_table_cube_to_number( out ); /* best suiting element */ std::vector<unsigned>::iterator bestFit = std::min_element( output_assignments[number].begin(), output_assignments[number].end(), minimal_hamming_distance( number_in ) ); new_spec.insert( std::make_pair( number_in, *bestFit ) ); /* remove element from list */ all_outputs.erase( std::find( all_outputs.begin(), all_outputs.end(), *bestFit ) ); output_assignments[number].erase( bestFit ); } } for ( unsigned i = 0u; i < ( 1u << new_bw ); ++i ) { if ( new_spec.find( i ) == new_spec.end() ) { std::vector<unsigned>::iterator bestFit = std::min_element( all_outputs.begin(), all_outputs.end(), minimal_hamming_distance( i ) ); new_spec.insert( std::make_pair( i, *bestFit ) ); all_outputs.erase( bestFit ); } } spec.clear(); for ( std::map<unsigned, unsigned>::const_iterator it = new_spec.begin(); it != new_spec.end(); ++it ) { spec.add_entry( number_to_truth_table_cube( it->first, new_bw ), number_to_truth_table_cube( it->second, new_bw ) ); } /* meta-data */ std::vector<std::string> inputs( new_bw, "i" ); std::fill( inputs.begin(), inputs.begin() + cons, "0" ); std::copy( base.inputs().begin(), base.inputs().end(), inputs.begin() + cons ); spec.set_inputs( inputs ); std::vector<std::string> outputs( new_bw, garbage_name ); for ( std::vector<unsigned>::iterator it = output_order.begin(); it != output_order.end(); ++it ) { unsigned index = std::distance( output_order.begin(), it ); if ( index < base.outputs().size() ) { outputs.at( *it ) = base.outputs().at( index ); } } spec.set_outputs( outputs ); std::vector<constant> constants( new_bw ); std::fill( constants.begin(), constants.begin() + cons, false ); std::fill( constants.begin() + cons, constants.end(), constant() ); spec.set_constants( constants ); std::vector<bool> garbage( new_bw, true ); for ( std::vector<unsigned>::const_iterator it = output_order.begin(); it != output_order.end(); ++it ) { garbage.at( *it ) = false; } spec.set_garbage( garbage ); return true; }
static void buildithLine (std::vector<std::pair<fmi::bv, fmi::bv> >& network , fmi::default_solver solver , binary_truth_table const& spec , binary_truth_table::const_iterator& line , std::vector < std::vector < fmi::bv > >& gates , unsigned depth) { using fmi::_0; using fmi::_1; using fmi::_2; using fmi::_3; assert (gates.size() == 1); unsigned lines = spec.num_inputs (); unsigned copies = std::distance ( spec.begin(), spec.end() ); assert (pow (2.0, double(lines) ) == copies ); unsigned pos = std::distance ( spec.begin(), line ); fmi::bv current = gates [ 0 ] [ pos ]; for (unsigned i = 0; i <= depth; i++) { fmi::bv hit = fmi::new_variable (solver, 1); fmi::bv next = fmi::new_variable (solver, lines); fmi::generate ( solver , _0 %= ( _1 & _2) == _2 , hit, current, network[i].first ) ; fmi::bv zext = fmi::zero_extend ( solver, hit, lines - 1); fmi::generate (solver , _0 %= _1 ^ (_2 << _3) , next , current , zext , network[i].second ); current = next; } if ( std::find_if ( line->second.first, line->second.second, is_value() ) == line->second.second ) return; bool hasDontCares = std::find ( line->second.first, line->second.second, constant()) != line->second.second; std::string output, mask; foreach (const binary_truth_table::value_type& out_bit, line->second) { output += boost::lexical_cast<std::string>(out_bit && *out_bit); mask += boost::lexical_cast<std::string>(out_bit); } if (hasDontCares) { fmi::fmi_assertion ( solver, _1 == (_0 & _2) , current , fmi::make_bin_constant (solver, output) , fmi::make_bin_constant (solver, mask ) ); } else fmi::fmi_assertion (solver, _0 == _1 , current , fmi::make_bin_constant (solver, output)); }
static bool synthesis (circuit& circ , const binary_truth_table& spec , properties::ptr settings , properties::ptr statistics , timer<properties_timer>& timer ) { using fmi::_0; using fmi::_1; using fmi::_2; using fmi::_3; fmi::default_solver solver = fmi::get_solver_instance ( get<std::string>( settings, "solver", "MiniSAT" ) ); unsigned max_depth = get<unsigned>( settings, "max_depth", 20u ); unsigned lines = spec.num_inputs (); unsigned copies = std::distance ( spec.begin(), spec.end() ); assert (pow (2.0, double(lines) ) == copies ); // // network.first = control lines, network.second = target std::vector < std::pair < fmi::bv, fmi::bv > > network; // int mainGroup = fmi::new_group (solver); fmi::store_group (solver, mainGroup); std::vector < fmi::bv > currentGate ( copies ); for (binary_truth_table::const_iterator iter = spec.begin(); iter != spec.end(); ++iter) { unsigned pos = std::distance ( spec.begin(), iter ); std::string input; foreach (const binary_truth_table::value_type& in_bit, iter->first) { input += boost::lexical_cast<std::string>(*in_bit); } currentGate[ pos ] = fmi::make_bin_constant (solver, input); } fmi::bv one = fmi::make_bin_constant (solver, "1"); fmi::bv lines_constant = fmi::make_nat_constant (solver, lines, lines); std::vector < fmi::bv > nextGate ( copies ); for (unsigned i = 0; i < max_depth; i++) { //std::cout << "Depth: " << i << " Time: " << timer() << std::endl; fmi::bv control = fmi::new_variable (solver, lines); fmi::bv target = fmi::new_variable (solver, lines); fmi::bv ext = fmi::zero_extend (solver, one, lines - 1); network += std::make_pair (control, target); // target line cannot be a control line fmi::fmi_assertion (solver, (_0 | (_1 << _2)) != _0 , control, ext, target); fmi::fmi_assertion (solver, _0 < _1, target, lines_constant); for (binary_truth_table::const_iterator iter = spec.begin(); iter != spec.end(); ++iter) { unsigned pos = std::distance ( spec.begin(), iter ); nextGate [ pos ] = fmi::new_variable (solver, lines); fmi::bv hit = fmi::new_variable (solver, 1); fmi::generate (solver , _0 %= (_1 & _2) == _2 , hit, currentGate [ pos ] , control ); fmi::bv zext = fmi::zero_extend (solver, hit, lines-1); fmi::generate (solver, _0 %= _1 ^ (_2 << _3), nextGate [ pos ], currentGate [ pos ], zext, target); } currentGate = nextGate; int constraintGroup = fmi::new_group (solver); fmi::store_group (solver, constraintGroup); for (binary_truth_table::const_iterator iter = spec.begin(); iter != spec.end(); ++iter) { if ( std::find_if ( iter->second.first, iter->second.second, is_value() ) == iter->second.second ) continue; unsigned pos = std::distance ( spec.begin(), iter ); bool hasDontCares = std::find ( iter->second.first, iter->second.second, constant() ) != iter->second.second; // asserts the output std::string output; std::string mask; foreach (const binary_truth_table::value_type& out_bit, iter->second) { output += boost::lexical_cast<std::string>(out_bit && *out_bit); mask += boost::lexical_cast<std::string>(out_bit); } if (hasDontCares) { fmi::fmi_assertion ( solver, _1 == (_0 & _2), currentGate [ pos ], fmi::make_bin_constant (solver, output), fmi::make_bin_constant ( solver, mask ) ); } else fmi::fmi_assertion ( solver, _0 == _1, currentGate [ pos ], fmi::make_bin_constant (solver, output)); } fmi::solve_result result = fmi::solve (solver); if (result == fmi::UNSAT) { std::ofstream Astream ("A.cnf"); std::ofstream Bstream ("B.cnf"); fmi::dump_group ( solver, mainGroup, Astream); fmi::dump_group ( solver, constraintGroup, Bstream); fmi::delete_group ( solver, constraintGroup); fmi::set_group (solver, mainGroup); continue; } return constructCircuit (circ, solver, network.size(), lines, network); } return false; }
static bool incremental_line_synthesis (circuit& circ , binary_truth_table const& spec , properties::ptr settings , properties::ptr statistics , timer<properties_timer>& timer ) { using fmi::_0; using fmi::_1; using fmi::_2; using fmi::_3; fmi::default_solver solver = fmi::get_solver_instance ( get<std::string > ( settings, "solver", "MiniSAT") ); unsigned max_depth = get<unsigned> ( settings, "max_depth", 20u ); unsigned lines = spec.num_inputs (); unsigned copies = std::distance ( spec.begin(), spec.end() ); assert (pow (2.0, double(lines) ) == copies ); // // network.first = control lines, network.second = target std::vector < std::pair < fmi::bv, fmi::bv > > network; std::vector < fmi::bv > currentGate ( copies ); fmi::bv one = fmi::make_bin_constant (solver, "1"); fmi::bv lines_constant = fmi::make_nat_constant (solver, lines, lines); std::vector < fmi::bv > nextGate ( copies ); std::vector < std::vector < fmi::bv > > gates; std::vector< fmi::bv> gate; // input specification for (binary_truth_table::const_iterator iter = spec.begin(); iter != spec.end(); ++iter) { std::string input; foreach (const binary_truth_table::value_type& in_bit, iter->first) { input += boost::lexical_cast<std::string>(*in_bit); } gate += fmi::make_bin_constant (solver, input); } gates += gate; int mainGroup = fmi::new_group (solver); fmi::store_group (solver, mainGroup); for (unsigned i = 0; i < max_depth; ++i) { fmi::bv control = fmi::new_variable (solver, lines); fmi::bv target = fmi::new_variable (solver, lines); fmi::bv ext = fmi::zero_extend (solver, one, lines - 1); network += std::make_pair (control, target); fmi::fmi_assertion (solver , (_0 | (_1 << _2)) != _0 , control, ext, target); fmi::fmi_assertion (solver , _0 < _1 , target, lines_constant); // std::vector < fmi::bv > gate; // // for (unsigned k = 0; k < copies; k++) // // gate += fmi::new_variable (solver, lines); // gates += gate; int specGroup = fmi::new_group ( solver ); fmi::store_group (solver, specGroup); bool terminate = false; binary_truth_table::const_iterator iter = spec.begin(); do { //unsigned pos = std::distance ( spec.begin(), iter ); buildithLine ( network, solver, spec, iter, gates, i); // // setup special step functions // if (pos % 2 == 0 && pos < copies) { ++iter; continue; } fmi::solve_result result = fmi::solve (solver); iter++; // std::ofstream mainGrpStream ("main.cnf"); // std::ofstream specGrpStream ("spec.cnf"); // assert (mainGrpStream); // assert (specGrpStream); // fmi::dump_group (solver, specGroup, specGrpStream); // fmi::dump_group (solver, mainGroup, mainGrpStream); if (result == fmi::UNSAT) { //std::cout << "UNSAT: " << pos << " " << copies << std::endl; fmi::delete_group (solver, specGroup); terminate = true; fmi::set_group (solver, mainGroup); } else { assert (result == fmi::SAT); if (iter == spec.end()) return constructCircuit (circ, solver, network.size(), lines, network); } } while (!terminate); } return false; }
bool transposition_based_synthesis( circuit& circ, const binary_truth_table& spec, properties::ptr settings, properties::ptr statistics ) { // Settings parsing // Run-time measuring timer<properties_timer> t; if ( statistics ) { properties_timer rt( statistics ); t.start( rt ); } unsigned bw = spec.num_outputs(); circ.set_lines( bw ); copy_metadata( spec, circ ); std::map<unsigned, unsigned> values_map; for ( binary_truth_table::const_iterator it = spec.begin(); it != spec.end(); ++it ) { binary_truth_table::cube_type in( it->first.first, it->first.second ); binary_truth_table::cube_type out( it->second.first, it->second.second ); values_map.insert( std::make_pair( truth_table_cube_to_number( in ), truth_table_cube_to_number( out ) ) ); } // Set of cycles std::vector<std::vector<unsigned> > cycles; while ( !values_map.empty() ) { unsigned start_value = values_map.begin()->first; // first key in values_map std::vector<unsigned> cycle; unsigned target = start_value; do { cycle += target; unsigned old_target = target; target = values_map[target]; values_map.erase( old_target ); // erase this entry } while ( target != start_value ); cycles += cycle; } for ( auto& cycle : cycles ) { unsigned max_distance = 0u; unsigned max_index = 0u; for ( unsigned i = 0u; i < cycle.size(); ++i ) { unsigned first = cycle.at(i); unsigned second = cycle.at( (i + 1u) % cycle.size() ); unsigned distance = hamming_distance( first, second ); if ( distance > max_distance ) { max_distance = distance; max_index = i; } } std::vector<unsigned> tmp; std::copy( cycle.begin() + ( max_index + 1u ), cycle.end(), std::back_inserter( tmp ) ); std::copy( cycle.begin(), cycle.begin() + ( max_index + 1u ), std::back_inserter( tmp ) ); std::copy( tmp.begin(), tmp.end(), cycle.begin() ); std::reverse( cycle.begin(), cycle.end() ); } // TODO create transpositions function for ( auto& cycle : cycles ) { for ( unsigned i = 0u; i < cycle.size() - 1; ++i ) { circuit transposition_circ( spec.num_inputs() ); unsigned first = cycle.at(i); unsigned second = cycle.at( (i + 1) % cycle.size() ); boost::dynamic_bitset<> first_bits( spec.num_inputs(), first ); boost::dynamic_bitset<> second_bits( spec.num_inputs(), second ); transposition_to_circuit( transposition_circ, first_bits, second_bits ); append_circuit( circ, transposition_circ); } } return true; }
void extend_pla( binary_truth_table& base, binary_truth_table& extended, const extend_pla_settings& settings ) { // copy metadata extended.set_inputs( base.inputs() ); extended.set_outputs( base.outputs() ); // CUDD stuff Cudd mgr( 0, 0 ); std::vector<BDD> vars( base.num_inputs() ); boost::generate( vars, [&mgr]() { return mgr.bddVar(); } ); // A function to create a BDD from a cube auto bddFromCube = [&mgr, &vars]( const std::pair<binary_truth_table::cube_type::const_iterator, binary_truth_table::cube_type::const_iterator>& cube ) { BDD c = mgr.bddOne(); unsigned pos = 0u; for ( const auto& literal : boost::make_iterator_range( cube.first, cube.second ) ) { if ( literal ) c &= ( *literal ? vars.at( pos ) : !vars.at( pos ) ); ++pos; } return c; }; // A function to get truth table cubes from a BDD auto cubesFromBdd = [&mgr, &vars]( BDD& from, std::vector<binary_truth_table::cube_type>& cubes ) { char * cube = new char[vars.size()]; unsigned pos; while ( from.CountMinterm( vars.size() ) > 0.0 ) { from.PickOneCube( cube ); binary_truth_table::cube_type tcube; BDD bcube = mgr.bddOne(); for ( pos = 0u; pos < vars.size(); ++pos ) { switch ( cube[pos] ) { case 0: bcube &= !vars.at( pos ); tcube += false; break; case 1: bcube &= vars.at( pos ); tcube += true; break; case 2: tcube += boost::optional<bool>(); break; } } cubes += tcube; from &= !bcube; } delete[] cube; }; while ( std::distance( base.begin(), base.end() ) > 0 ) { // Pick one cube from base binary_truth_table::iterator base_cube = base.begin(); binary_truth_table::cube_type base_in( base_cube->first.first, base_cube->first.second ); binary_truth_table::cube_type base_out( base_cube->second.first, base_cube->second.second ); if ( settings.verbose ) { std::cout << "[I] Processing:" << std::endl; std::cout << "[I] "; std::for_each( base_cube->first.first, base_cube->first.second, []( const boost::optional<bool>& b ) { std::cout << (b ? (*b ? "1" : "0") : "-"); } ); std::cout << " "; std::for_each( base_cube->second.first, base_cube->second.second, []( const boost::optional<bool>& b ) { std::cout << (b ? (*b ? "1" : "0") : "-"); } ); std::cout << std::endl; } BDD bicube = bddFromCube( base_cube->first ); base.remove_entry( base_cube ); // Go through all cubes of extended bool found_match = false; for ( binary_truth_table::iterator extended_cube = extended.begin(); extended_cube != extended.end(); ++extended_cube ) { BDD bocube = bddFromCube( extended_cube->first ); if ( ( bicube & bocube ).CountMinterm( base.num_inputs() ) > 0.0 ) { if ( settings.verbose ) { std::cout << "[I] Intersection detected with" << std::endl; std::cout << "[I] "; std::for_each( extended_cube->first.first, extended_cube->first.second, []( const boost::optional<bool>& b ) { std::cout << (b ? (*b ? "1" : "0") : "-"); } ); std::cout << " "; std::for_each( extended_cube->second.first, extended_cube->second.second, []( const boost::optional<bool>& b ) { std::cout << (b ? (*b ? "1" : "0") : "-"); } ); std::cout << std::endl; } binary_truth_table::cube_type extended_in( extended_cube->first.first, extended_cube->first.second ); binary_truth_table::cube_type extended_out( extended_cube->second.first, extended_cube->second.second ); extended.remove_entry( extended_cube ); BDD keep_in_base = bicube & !bocube; BDD intersection = bicube & bocube; BDD keep_in_extended = !bicube & bocube; std::vector<binary_truth_table::cube_type> cubes; cubesFromBdd( keep_in_base, cubes ); for ( const auto& cube : cubes ) { bool match = false; for ( binary_truth_table::iterator base_inner_cube = base.begin(); base_inner_cube != base.end(); ++base_inner_cube ) { binary_truth_table::cube_type base_inner_in( base_inner_cube->first.first, base_inner_cube->first.second ); binary_truth_table::cube_type base_inner_out( base_inner_cube->second.first, base_inner_cube->second.second ); if ( cube == base_inner_in ) { base.remove_entry( base_inner_cube ); base.add_entry( cube, combine_pla_cube( base_out, base_inner_out ) ); match = true; break; } } if ( !match ) { base.add_entry( cube, base_out ); } } cubes.clear(); cubesFromBdd( intersection, cubes ); for ( const auto& cube : cubes ) extended.add_entry( cube, combine_pla_cube( base_out, extended_out ) ); cubes.clear(); cubesFromBdd( keep_in_extended, cubes ); for ( const auto& cube : cubes ) extended.add_entry( cube, extended_out ); found_match = true; break; } } // Copy the base_cube if no match has been found if ( !found_match ) { if ( settings.verbose ) { std::cout << "[I] Add directly!" << std::endl; } extended.add_entry( base_in, base_out ); } if ( settings.verbose ) { std::cout << "[I] base:" << std::endl; std::cout << base << std::endl; std::cout << "[I] extended:" << std::endl; std::cout << extended << std::endl << std::endl; } } /* Compact */ if ( settings.post_compact ) { // Compute compacted nouns std::map<binary_truth_table::cube_type, BDD> compacted_monoms; for ( const auto& row : extended ) { BDD in_cube = bddFromCube( row.first ); binary_truth_table::cube_type out_pattern( row.second.first, row.second.second ); auto it = compacted_monoms.find( out_pattern ); if ( it == compacted_monoms.end() ) { compacted_monoms[out_pattern] = in_cube; } else { it->second |= in_cube; } } // Clear extended PLA representation extended.clear(); // Add compacted monoms back to PLA representation for ( const auto& p : compacted_monoms ) { std::vector<binary_truth_table::cube_type> cubes; BDD bdd = p.second; cubesFromBdd( bdd, cubes ); for ( const auto& cube : cubes ) extended.add_entry( cube, p.first ); } } }