def eval_genome(genome, config, batch_data):
    """
    Most important part of NEAT since it is here that we adapt NEAT to our problem.
    We tell what is the phenotype of a genome and how to calculate its fitness
    (same idea than a loss)
    :param config: config from the config file
    :param genome: one genome to get evaluated
    :param batch_data: data to use to evaluate the genomes
    :return fitness: returns the fitness of the genome
    this version is intented to use ParallelEvaluator and should be much faster
    """
    net = neat.nn.RecurrentNetwork.create(genome, config)
    cross_entropy = 0
    for data in batch_data:
        inputs, output = data[0], data[2]
        net.reset()
        mask, scores = gate_activation_ce(net, inputs)
        selected_score = scores[mask]
        if selected_score.size == 0:
            scores = 1 / 7 * np.ones(7)
        else:
            xo = np.sum(selected_score, axis=0) / selected_score.size
            xo[np.isinf(xo)] = 100
            xo[np.isnan(xo)] = 100
            scores = softmax(xo)
        cross_entropy -= np.log(scores[output] + 10**-20)

    return 1 - cross_entropy / normalize_fitness
def evaluate(net, data_loader):
    correct = 0
    total = 0
    net.reset()
    target_scores = []
    non_target_scores = []
    for data in tqdm(data_loader):
        inputs, output = data[0], data[2]
        mask, scores = gate_activation_ce(net, inputs)
        selected_score = scores[mask]
        if selected_score.size == 0:
            scores = 1 / 7 * np.ones(7)
        else:
            xo = np.sum(selected_score, axis=0) / selected_score.size
            scores = softmax(xo)
        total += 1
        correct += (scores.argmax() == output)
        if output == 0:
            target_scores.append(scores[0])
        else:
            non_target_scores.append(scores[0])

    target_scores = np.array(target_scores)
    non_target_scores = np.array(non_target_scores)

    pmiss, pfa = rocch(target_scores, non_target_scores)
    eer = rocch2eer(pmiss, pfa)

    return float(correct) / total, eer
def eval_genomes(genomes, config_, batch_data):
    """
    Most important part of NEAT since it is here that we adapt NEAT to our problem.
    We tell what is the phenotype of a genome and how to calculate its fitness (same idea than a loss)
    :param config_: config from the config file
    :param genomes: list of all the genomes to get evaluated
    """
    for _, genome in tqdm(genomes):
        net = neat.nn.RecurrentNet.create(genome, config_)
        cross_entropy = 0
        for data in batch_data:
            inputs, output = data[0], data[2]
            net.reset()
            mask, scores = gate_activation_ce(net, inputs)
            selected_score = scores[mask]
            if selected_score.size == 0:
                scores = 1 / 7 * np.ones(7)
            else:
                xo = np.sum(selected_score, axis=0) / selected_score.size
                print("xo =", xo)
                scores = softmax(xo)
                print("scores =", scores)
            cross_entropy -= np.log(scores[output] + 10**-20)

        genome.fitness = 1 - cross_entropy / normalize_fitness