<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def parse_config_file(job, config_file):
 """ This module will parse the config file withing params and set up the variables that will be passed to the various tools in the pipeline. ARGUMENTS config_file: string containing path to a config file. An example config file is available at https://s3-us-west-2.amazonaws.com/pimmuno-references /input_parameters.list RETURN VALUES None """

    job.fileStore.logToMaster('Parsing config file')
    config_file = os.path.abspath(config_file)
    if not os.path.exists(config_file):
        raise ParameterError('The config file was not found at specified location. Please verify ' +
                             'and retry.')
    # Initialize variables to hold the sample sets, the universal options, and the per-tool options
    sample_set = defaultdict()
    univ_options = defaultdict()
    tool_options = defaultdict()
    # Read through the notes and the
    with open(config_file, 'r') as conf:
        for line in conf:
            line = line.strip()
            if line.startswith('##') or len(line) == 0:
                continue
            if line.startswith('BEGIN'):
                break
        # The generator function tool_specific_param_generator will yield one group name at a time
        # along with it's parameters.
        for groupname, group_params in tool_specific_param_generator(job, conf):
            if groupname == 'patient':
                if 'patient_id' not in group_params.keys():
                    raise ParameterError('A patient group is missing the patient_id flag.')
                sample_set[group_params['patient_id']] = group_params
            elif groupname == 'Universal_Options':
                univ_options = group_params
                required_options = {'java_Xmx', 'output_folder', 'storage_location'}
                missing_opts = required_options.difference(set(univ_options.keys()))
                if len(missing_opts) > 0:
                    raise ParameterError(' The following options have no arguments in the config '
                                         'file :\n' + '\n'.join(missing_opts))
                if univ_options['sse_key_is_master']:
                    assert univ_options['sse_key_is_master'] in ('True', 'true', 'False', 'false')
                    univ_options['sse_key_is_master'] = \
                        univ_options['sse_key_is_master'] in ('True', 'true')
            # If it isn't any of the above, it's a tool group
            else:
                tool_options[groupname] = group_params
    # Ensure that all tools have been provided options.
    required_tools = {'cutadapt', 'bwa', 'star', 'phlat', 'transgene', 'mut_callers', 'rsem',
                      'mhci', 'mhcii', 'snpeff', 'rank_boost'}
    #                'fusion', 'indels'}
    missing_tools = required_tools.difference(set(tool_options.keys()))
    if len(missing_tools) > 0:
        raise ParameterError(' The following tools have no arguments in the config file : \n' +
                             '\n'.join(missing_tools))
    # Start a job for each sample in the sample set
    for patient_id in sample_set.keys():
        job.addFollowOnJobFn(pipeline_launchpad, sample_set[patient_id], univ_options, tool_options)
    return None