示例#1
0
    def evolve(inner_objective):
        def objective(ind):
            ind.fitness = -inner_objective(ind)
            return ind

        if individual_type == "SingleGenome":
            pop = cgp.Population(**population_params,
                                 genome_params=genome_params)
        elif individual_type == "MultiGenome":
            pop = cgp.Population(**population_params,
                                 genome_params=[genome_params])
        else:
            raise NotImplementedError

        ea = cgp.ea.MuPlusLambda(**ea_params)

        history = {}
        history["fitness_champion"] = []

        def recording_callback(pop):
            history["fitness_champion"].append(pop.champion.fitness)

        cgp.evolve(objective,
                   pop,
                   ea,
                   **evolve_params,
                   callback=recording_callback)

        return history
示例#2
0
def test_assert_mutation_rate(rng_seed, genome_params, mutation_rate):
    with pytest.raises(ValueError):
        cgp.Population(5, -0.1, rng_seed, genome_params)

    with pytest.raises(ValueError):
        cgp.Population(5, 1.1, rng_seed, genome_params)

    # assert that no error is thrown for a suitable mutation rate
    cgp.Population(5, mutation_rate, rng_seed, genome_params)
示例#3
0
def test_history_recording(population_params, genome_params, ea_params):

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    evolve_params = {"max_generations": 2, "termination_fitness": 1.0}

    history = {}
    history["fitness"] = np.empty(
        (evolve_params["max_generations"], population_params["n_parents"]))
    history["fitness_champion"] = np.empty(evolve_params["max_generations"])
    history["champion"] = []

    def recording_callback(pop):
        history["fitness"][pop.generation] = pop.fitness_parents()
        history["fitness_champion"][pop.generation] = pop.champion.fitness
        history["champion"].append(pop.champion)

    cgp.evolve(objective_history_recording,
               pop,
               ea,
               **evolve_params,
               callback=recording_callback)

    assert np.all(history["fitness"] == pytest.approx(1.0))
    assert np.all(history["fitness_champion"] == pytest.approx(1.0))
    assert "champion" in history
示例#4
0
def test_fitness_parents(population_params, genome_params, rng):
    pop = cgp.Population(**population_params, genome_params=genome_params)
    fitness_values = rng.rand(population_params["n_parents"])
    for fitness, parent in zip(fitness_values, pop.parents):
        parent.fitness = fitness

    assert np.all(pop.fitness_parents() == pytest.approx(fitness_values))
示例#5
0
def population_simple_fitness(population_params, genome_params):
    pop = cgp.Population(**population_params, genome_params=genome_params)

    for i, parent in enumerate(pop.parents):
        parent.fitness = float(i)

    return pop
示例#6
0
def test_parent_individuals_are_assigned_correct_indices(
        population_params, genome_params):

    pop = cgp.Population(**population_params, genome_params=genome_params)

    for idx, ind in enumerate(pop.parents):
        assert ind.idx == idx
示例#7
0
def main():
    objective_params = {"n_runs_per_individual": 10}

    population_params = {"n_parents": 10, "mutation_rate": 0.04, "seed": 42}

    genome_params = {
        "n_inputs": 2,
        "n_outputs": 1,
        "n_columns": 16,
        "n_rows": 1,
        "levels_back": None,
        "primitives": (
            cgp.Add,
            cgp.Sub,
            cgp.Mul,
            cgp.Div,
            cgp.ConstantFloat,
        ),
    }

    ea_params = {
        "n_offsprings": 4,
        "tournament_size": 1,
        "n_processes": 1,  # was 4
    }

    evolve_params = {"max_generations": 1000, "min_fitness": 0.0}

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    history = {"fitness_parents": [], "fitness_champion": []}

    def recording_callback(pop):
        history["fitness_parents"].append(pop.fitness_parents())
        history["fitness_champion"].append(pop.champion.fitness)

    obj = functools.partial(
        objective,
        seed=population_params["seed"],
        n_runs_per_individual=objective_params["n_runs_per_individual"],
    )

    cgp.evolve(pop,
               obj,
               ea,
               **evolve_params,
               print_progress=True,
               callback=recording_callback)

    display_msg(
        "History: {}".format(history),
        True,
    )
    display_msg(
        "Champion: {}".format(pop.champion.to_sympy()),
        True,
    )
示例#8
0
def test_individual_init(population_params, genome_params):

    dna = [
        ID_INPUT_NODE,
        ID_NON_CODING_GENE,
        ID_NON_CODING_GENE,
        0,
        0,
        0,
        1,
        1,
        1,
        2,
        2,
        2,
        ID_OUTPUT_NODE,
        3,
        ID_NON_CODING_GENE,
    ]

    def individual_init(ind):
        ind.genome.dna = dna
        return ind

    genome_params = {
        "n_inputs": 1,
        "n_outputs": 1,
        "n_columns": 3,
        "n_rows": 1,
        "levels_back": None,
        "primitives": (cgp.Add, cgp.Sub, cgp.ConstantFloat),
    }

    # without passing individual_init comparison fails
    pop = cgp.Population(**population_params, genome_params=genome_params)
    for ind in pop.parents:
        with pytest.raises(AssertionError):
            assert ind.genome.dna == dna

    # with passing individual_init comparison succeeds
    pop = cgp.Population(**population_params,
                         genome_params=genome_params,
                         individual_init=individual_init)
    for ind in pop.parents:
        assert ind.genome.dna == dna
示例#9
0
def test_create_new_parent_population(population_params, genome_params,
                                      ea_params):
    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    # Create new parent population from the parents and assert that
    # we picked the first three individuals
    new_parents = ea._create_new_parent_population(3, pop.parents)
    assert new_parents == pop.parents[:3]
示例#10
0
def test_mutate(population_params, genome_params):
    population_params["mutation_rate"] = 0.999999
    pop = cgp.Population(**population_params, genome_params=genome_params)

    offspring = pop.parents
    offspring_original = copy.deepcopy(offspring)
    offspring = pop.mutate(offspring)
    assert np.any(
        [off_orig != off_mutated for off_orig, off_mutated in zip(offspring_original, offspring)]
    )
示例#11
0
def test_sort(population_params, genome_params, ea_params):
    def objective(individual):
        individual.fitness = float(individual.idx)
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)
    sorted_parents = ea._sort(pop.parents)
    # Assert that the sorting inverted the list of parents (because the fitness is equal to the id)
    assert sorted_parents == pop.parents[::-1]
示例#12
0
def test_initialize_fitness_parents(population_params, genome_params,
                                    ea_params):
    def objective(individual):
        individual.fitness = -1.0
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)
    assert all([not ind.fitness_is_None() for ind in pop.parents])
示例#13
0
def test_raise_fitness_has_wrong_type(population_params, genome_params,
                                      ea_params):
    def objective(individual):
        individual.fitness = int(
            5.0)  # should raise error since fitness should be float
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    with pytest.raises(ValueError):
        ea.initialize_fitness_parents(pop, objective)
示例#14
0
def test_objective_must_set_valid_fitness(population_params, genome_params,
                                          ea_params):
    def objective(ind):
        # missing ind.fitness assignement
        return ind

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)
    with pytest.raises(RuntimeError):
        cgp.evolve(objective, pop, ea, max_generations=10)
    with pytest.raises(RuntimeError):
        pop.champion.fitness
示例#15
0
def test_mutate(population_params, genome_params, ea_params):
    ea_params["mutation_rate"] = 0.5
    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    offspring = pop.parents
    offspring_original = copy.deepcopy(offspring)
    offspring = ea.mutate(offspring, pop.rng)
    assert np.any([
        off_orig != off_mutated
        for off_orig, off_mutated in zip(offspring_original, offspring)
    ])
示例#16
0
def evolve(seed):

    objective_params = {"n_runs_per_individual": 3, "n_total_steps": 2000}

    genome_params = {
        "n_inputs":
        2,
        "primitives": (
            cgp.Add,
            cgp.Sub,
            cgp.Mul,
            cgp.Div,
            cgp.ConstantFloat,
            ConstantFloatZeroPointOne,
            ConstantFloatTen,
        ),
    }

    ea_params = {"n_processes": 4}

    evolve_params = {
        "max_generations": int(args["--max-generations"]),
        "termination_fitness": 100.0,
    }

    pop = cgp.Population(genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(**ea_params)

    history = {}
    history["expr_champion"] = []
    history["fitness_champion"] = []

    def recording_callback(pop):
        history["expr_champion"].append(pop.champion.to_sympy())
        history["fitness_champion"].append(pop.champion.fitness)

    obj = functools.partial(
        objective,
        seed=seed,
        n_runs_per_individual=objective_params["n_runs_per_individual"],
        n_total_steps=objective_params["n_total_steps"],
    )

    pop = cgp.evolve(obj,
                     pop,
                     ea,
                     **evolve_params,
                     print_progress=True,
                     callback=recording_callback)

    return history, pop.champion
示例#17
0
def test_fitness_contains_nan(population_params, genome_params):
    def objective(individual):
        if np.random.rand() < 0.5:
            individual.fitness = np.nan
        else:
            individual.fitness = np.random.rand()
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(10, 10, 1)
    ea.initialize_fitness_parents(pop, objective)
    ea.step(pop, objective)
示例#18
0
def test_ncolumns_zero(population_params):
    sympy = pytest.importorskip("sympy")
    genome_params = {
        "n_inputs": 1,
        "n_outputs": 1,
        "n_columns": 0,
        "n_rows": 1,
        "primitives": (cgp.Mul, cgp.Sub, cgp.Add, cgp.ConstantFloat),
    }
    pop = cgp.Population(**population_params, genome_params=genome_params)
    for ind in pop:
        sympy_expr = ind.to_sympy()
        assert sympy_expr == sympy.sympify("x_0")
示例#19
0
def test_hurdles(population_params, genome_params, ea_params):

    # make sure all offsprings are assigned fitness None
    population_params["n_parents"] = 3
    ea_params["mutation_rate"] = 1.0
    ea_params["n_offsprings"] = 3
    ea_params["hurdle_percentile"] = [0.1, 0.0]

    def objective_one(ind):
        # assign low fitness to individuals 4 and 5 to check blocking via hurdle
        if ind.idx in (4, 5):
            ind.fitness = -float(ind.idx)
        else:
            ind.fitness = float(ind.idx)
        return ind

    def objective_two(ind):
        if ind.idx == 4:
            ind.fitness = -((1.0 + float(ind.idx))**2)
        else:
            ind.fitness = (1.0 + float(ind.idx))**2
        return ind

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    ea.initialize_fitness_parents(pop, [objective_one, objective_two])

    # while initializing parents, both objectives should have been
    # evaluated for all parents; the parents fitness is hence the sum
    # of both objectives
    parents_expected = [(0, 1), (1, 5), (2, 11)]
    for ind, ind_expected in zip(pop.parents, parents_expected):
        assert ind.idx == ind_expected[0]
        assert ind.fitness == pytest.approx(ind_expected[1])

    # code below implements `ea.step`, but keeps offsprings around to
    # check combined population
    offsprings = ea._create_new_offspring_generation(pop)
    combined = offsprings + pop.parents

    combined = ea._compute_fitness(combined, [objective_one, objective_two])
    combined = ea._sort(combined)

    # individual 4 has higher fitness as individual 5 as the latter
    # didn't make it past the first hurdle
    combined_expected = [(3, 19), (2, 11), (1, 5), (0, 1), (4, -29), (5, -5)]
    assert len(combined) == len(combined_expected)
    for ind, ind_expected in zip(combined, combined_expected):
        assert ind.idx == ind_expected[0]
        assert ind.fitness == pytest.approx(ind_expected[1])
示例#20
0
def evolution(f_target):
    """Execute CGP on a regression task for a given target function.

    Parameters
    ----------
    f_target : Callable
        Target function

    Returns
    -------
    dict
        Dictionary containing the history of the evolution
    Individual
        Individual with the highest fitness in the last generation
    """
    population_params = {"n_parents": 10, "seed": 818821}

    genome_params = {
        "n_inputs": 2,
        "n_outputs": 1,
        "n_columns": 12,
        "n_rows": 2,
        "levels_back": 5,
        "primitives": (cgp.Add, cgp.Sub, cgp.Mul, cgp.Div, cgp.ConstantFloat),
    }

    ea_params = {"n_offsprings": 10, "tournament_size": 2, "mutation_rate": 0.03, "n_processes": 2}

    evolve_params = {"max_generations": int(args["--max-generations"]), "termination_fitness": 0.0}

    # create population that will be evolved
    pop = cgp.Population(**population_params, genome_params=genome_params)

    # create instance of evolutionary algorithm
    ea = cgp.ea.MuPlusLambda(**ea_params)

    # define callback for recording of fitness over generations
    history = {}
    history["fitness_parents"] = []

    def recording_callback(pop):
        history["fitness_parents"].append(pop.fitness_parents())

    # the objective passed to evolve should only accept one argument,
    # the individual
    obj = functools.partial(objective, target_function=f_target, seed=population_params["seed"])

    # Perform the evolution
    cgp.evolve(pop, obj, ea, **evolve_params, print_progress=True, callback=recording_callback)
    return history, pop.champion
示例#21
0
def evolution(data, population_params, genome_params, ea_params, evolve_params,
              learning_rate, alpha, fitness_mode):
    """Execute CGP for given target function.

    Parameters
    ----------
    data: List(dict)
     Fields in dict:
        data_train: np.array
        data_validate: np.array
        initial_weights: np.array
            -> pre defined so that every individual (resp learning rule) has the same starting condition
        pc0: First principal components (Eigenvectors of the co-variance matrix)
    population_params: dict with n_parents, mutation_rate, seed
    genome_params:
        dict with  n_inputs, n_outputs, n_columns, n_rows, levels_back, primitives (allowed function gene values)
    ea_params: dict with n_offsprings, n_breeding, tournament_size, n_processes,
    evolve_params: dict with max_generations, min_fitness
    alpha: Hyperparameter weighting the second term of the fitness function
    fitness_mode: str ("angle" or "variance")

    Returns
    -------
    dict
        Dictionary containing the history of the evolution
    Individual
        Individual with the highest fitness in the last generation
    """

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)

    history = {}
    history["fitness_parents"] = []
    history["champion_sympy_expression"] = []

    def recording_callback(pop):
        history["fitness_parents"].append(pop.fitness_parents())
        history["champion_sympy_expression"].append(
            pop.champion.to_sympy()
        )

    obj = functools.partial(
        objective, data=data, learning_rate=learning_rate,
        alpha=alpha, mode=fitness_mode)

    cgp.evolve(
        pop, obj, ea, **evolve_params, print_progress=True, callback=recording_callback,
    )
    return history, pop.champion
示例#22
0
def test_offspring_individuals_are_assigned_correct_indices(
        population_params, genome_params, ea_params):
    def objective(ind):
        ind.fitness = 0.0
        return ind

    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)

    offsprings = ea._create_new_offspring_generation(pop)

    for idx, ind in enumerate(offsprings):
        assert ind.idx == len(pop.parents) + idx
示例#23
0
def test_update_n_objective_calls_mutation_rate_one(population_params,
                                                    genome_params, ea_params):
    def objective(individual):
        individual.fitness = float(individual.idx)
        return individual

    ea_params["mutation_rate"] = 1.0
    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)
    n_objective_calls_expected = population_params["n_parents"]
    n_step_calls = 100
    for idx_current_step in range(n_step_calls):
        ea.step(pop, objective)
        n_objective_calls_expected += ea_params["n_offsprings"]
        assert ea.n_objective_calls == n_objective_calls_expected
示例#24
0
def test_fitness_contains_and_maintains_nan(population_params, genome_params,
                                            ea_params, rng_seed):
    def objective(individual):
        rng = np.random.RandomState(rng_seed)
        if rng.rand() < 0.95:
            individual.fitness = np.nan
        else:
            individual.fitness = rng.rand()
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)
    ea.step(pop, objective)
    assert any([np.isnan(ind.fitness) for ind in pop])
示例#25
0
def test_finite_max_generations_or_max_objective_calls(population_params,
                                                       genome_params,
                                                       ea_params):
    def objective(individual):
        individual.fitness = float(individual.idx)
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)
    evolve_params = {
        "max_generations": np.inf,
        "min_fitness": 0,
        "max_objective_calls": np.inf,
    }
    with pytest.raises(ValueError):
        cgp.evolve(pop, objective, ea, **evolve_params)
示例#26
0
def test_local_search_is_only_applied_to_best_k_individuals(
        population_params, local_search_params, ea_params):

    torch = pytest.importorskip("torch")

    def inner_objective(f):
        return torch.nn.MSELoss()(torch.DoubleTensor([[1.1]]),
                                  f(torch.zeros(1, 1, dtype=torch.double)))

    def objective(ind):
        if not ind.fitness_is_None():
            return ind

        f = ind.to_torch()
        ind.fitness = -inner_objective(f).item()
        return ind

    genome_params = {
        "n_inputs": 1,
        "n_outputs": 1,
        "n_columns": 1,
        "n_rows": 1,
        "levels_back": None,
        "primitives": (cgp.Parameter, ),
    }

    k_local_search = 2

    pop = cgp.Population(**population_params, genome_params=genome_params)

    local_search = functools.partial(cgp.local_search.gradient_based,
                                     objective=inner_objective,
                                     **local_search_params)

    ea = cgp.ea.MuPlusLambda(**ea_params,
                             local_search=local_search,
                             k_local_search=k_local_search)
    ea.initialize_fitness_parents(pop, objective)
    ea.step(pop, objective)

    for idx in range(k_local_search):
        assert pop[idx].genome._parameter_names_to_values[
            "<p1>"] != pytest.approx(1.0)

    for idx in range(k_local_search, population_params["n_parents"]):
        assert pop[idx].genome._parameter_names_to_values[
            "<p1>"] == pytest.approx(1.0)
示例#27
0
def test_min_fitness_deprecation(population_params, genome_params, ea_params):
    def objective(individual):
        individual.fitness = 1.0
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)
    ea = cgp.ea.MuPlusLambda(**ea_params)
    with pytest.warns(DeprecationWarning):
        cgp.evolve(pop, objective, ea, min_fitness=2.0, max_generations=10)

    with pytest.raises(RuntimeError):
        cgp.evolve(pop,
                   objective,
                   ea,
                   min_fitness=2.0,
                   termination_fitness=1.5,
                   max_generations=10)
示例#28
0
def test_offspring_individuals_are_assigned_correct_parent_indices(
        population_params, genome_params, ea_params):
    def objective(ind):
        ind.fitness = 0.0
        return ind

    population_params["n_parents"] = 1
    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea_params["tournament_size"] = 1
    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)

    offsprings = ea._create_new_offspring_generation(pop)

    for ind in offsprings:
        assert ind.parent_idx == 0
示例#29
0
def test_step(population_params, genome_params, ea_params):
    def objective(individual):
        individual.fitness = float(individual.idx)
        return individual

    pop = cgp.Population(**population_params, genome_params=genome_params)

    ea = cgp.ea.MuPlusLambda(**ea_params)
    ea.initialize_fitness_parents(pop, objective)
    old_parent_ids = sorted([ind.idx for ind in pop.parents])
    ea.step(pop, objective)
    new_parent_ids = sorted([ind.idx for ind in pop.parents])
    # After one step, the new parent population should have IDs that
    # are offset from the old parent ids by n_offsprings
    # This is by construction in this test because the fitness is equal to the id
    assert all([
        new_id == old_id + ea_params["n_offsprings"]
        for new_id, old_id in zip(new_parent_ids, old_parent_ids)
    ])
示例#30
0
def test_speedup_parallel_evolve(population_params, genome_params, ea_params):

    # use 4 parents and 4 offsprings to achieve even load on 2, 4
    # cores
    population_params["n_parents"] = 4
    ea_params["n_offsprings"] = 4

    evolve_params = {"max_generations": 5, "min_fitness": np.inf}

    # Number of calls to objective: Number of parents + (Number of
    # parents + offspring) * (N_generations - 1) Initially, we need to
    # compute the fitness for all parents. Then we compute the fitness
    # for each parents and offspring in each iteration.
    n_calls_objective = population_params["n_parents"] + (
        population_params["n_parents"] +
        ea_params["n_offsprings"]) * (evolve_params["max_generations"] - 1)
    np.random.seed(population_params["seed"])

    time_per_objective_call = 0.25

    # Serial execution

    for n_processes in [1, 2, 4]:
        pop = cgp.Population(**population_params, genome_params=genome_params)

        ea = cgp.ea.MuPlusLambda(**ea_params, n_processes=n_processes)

        t0 = time.time()
        cgp.evolve(pop, _objective_speedup_parallel_evolve, ea,
                   **evolve_params)
        T = time.time() - t0

        if n_processes == 1:
            T_baseline = T
            # assert that total execution time is roughly equal to
            # number of objective calls x time per call; serves as a
            # baseline for subsequent parallel evolutions
            assert T == pytest.approx(n_calls_objective *
                                      time_per_objective_call,
                                      rel=0.25)
        else:
            # assert that multiprocessing roughly follows a linear speedup.
            assert T == pytest.approx(T_baseline / n_processes, rel=0.25)