docs: def parallel_evaluation_mp(candidates, args):
      """Evaluate the candidates in parallel using ``multiprocessing``.
  
      This function allows parallel evaluation of candidate solutions.
      It uses the standard multiprocessing library to accomplish the 
      parallelization. The function assigns the evaluation of each
      candidate to its own job, all of which are then distributed to the
      available processing units.
      
      .. note::
      
         All arguments to the evaluation function must be pickleable.
         Those that are not will not be sent through the ``args`` variable
         and will be unavailable to your function.
      
      .. Arguments:
         candidates -- the candidate solutions
         args -- a dictionary of keyword arguments
  
      Required keyword arguments in args:
      
      - *mp_evaluator* -- actual evaluation function to be used (This function
        should have the same signature as any other inspyred evaluation function.)
  
      Optional keyword arguments in args:
      
      - *mp_nprocs* -- number of processors that will be used (default machine 
        cpu count)
      
      """
      import time
      import multiprocessing
      logger = args['_ec'].logger
      
      try:
          evaluator = args['mp_evaluator']
      except KeyError:
          logger.error('parallel_evaluation_mp requires \'mp_evaluator\' be defined in the keyword arguments list')
          raise 
      try:
          nprocs = args['mp_nprocs']
      except KeyError:
          nprocs = multiprocessing.cpu_count()
          
      pickled_args = {}
      for key in args:
          try:
              pickle.dumps(args[key])
              pickled_args[key] = args[key]
          except (TypeError, pickle.PickleError, pickle.PicklingError):
              logger.debug('unable to pickle args parameter {0} in parallel_evaluation_mp'.format(key))
              pass
  
      start = time.time()
      try:
          pool = multiprocessing.Pool(processes=nprocs)
          results = [pool.apply_async(evaluator, ([c], pickled_args)) for c in candidates]
          pool.close()
          pool.join()
          return [r.get()[0] for r in results]
      except (OSError, RuntimeError) as e:
          logger.error('failed parallel_evaluation_mp: {0}'.format(str(e)))
          raise
      else:
          end = time.time()
          logger.debug('completed parallel_evaluation_mp in {0} seconds'.format(end - start))