<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def parallel_evaluation_pp(candidates, args):
 """Evaluate the candidates in parallel using Parallel Python. This function allows parallel evaluation of candidate solutions. It uses the `Parallel Python <http://www.parallelpython.com>`_ (pp) library to accomplish the parallelization. This library must already be installed in order to use this function. 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: - *pp_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: - *pp_dependencies* -- tuple of functional dependencies of the serial evaluator (default ()) - *pp_modules* -- tuple of modules that must be imported for the functional dependencies (default ()) - *pp_servers* -- tuple of servers (on a cluster) that will be used for parallel processing (default ("*",)) - *pp_secret* -- string representing the secret key needed to authenticate on a worker node (default "inspyred") - *pp_nprocs* -- integer representing the number of worker processes to start on the local machine (default "autodetect", which sets it to the number of processors in the system) For more information about these arguments, please consult the documentation for `Parallel Python <http://www.parallelpython.com>`_. """

    import pp
    logger = args['_ec'].logger
    
    try:
        evaluator = args['pp_evaluator']
    except KeyError:
        logger.error('parallel_evaluation_pp requires \'pp_evaluator\' be defined in the keyword arguments list')
        raise 
    secret_key = args.setdefault('pp_secret', 'inspyred')
    try:
        job_server = args['_pp_job_server']
    except KeyError:
        pp_servers = args.get('pp_servers', ("*",))
        pp_nprocs = args.get('pp_nprocs', 'autodetect')
        job_server = pp.Server(ncpus=pp_nprocs, ppservers=pp_servers, secret=secret_key)
        args['_pp_job_server'] = job_server
    pp_depends = args.setdefault('pp_dependencies', ())
    pp_modules = args.setdefault('pp_modules', ())
        
    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_pp'.format(key))
            pass
            
    func_template = pp.Template(job_server, evaluator, pp_depends, pp_modules)
    jobs = [func_template.submit([c], pickled_args) for c in candidates]
    
    fitness = []
    for i, job in enumerate(jobs):
        r = job()
        try:
            fitness.append(r[0])
        except TypeError:
            logger.warning('parallel_evaluation_pp generated an invalid fitness for candidate {0}'.format(candidates[i]))
            fitness.append(None)
    return fitness