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;
  }
Example #3
0
  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)); 
  }
Example #4
0
  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;
  }
Example #5
0
  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;
  }
Example #7
0
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 );
    }
  }
}