109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
"""
|
|
graphviz должен быть доступен в PATH (недостаточно просто установить через pip)
|
|
|
|
Можно проверить командой
|
|
dot -V
|
|
"""
|
|
|
|
import random
|
|
|
|
import numpy as np
|
|
from numpy.typing import NDArray
|
|
|
|
from gp.crossovers import crossover_subtree
|
|
from gp.fitness import (
|
|
MAEFitness,
|
|
MSEFitness,
|
|
NRMSEFitness,
|
|
RMSEFitness,
|
|
)
|
|
from gp.ga import GARunConfig, genetic_algorithm
|
|
from gp.mutations import (
|
|
CombinedMutation,
|
|
GrowMutation,
|
|
HoistMutation,
|
|
NodeReplacementMutation,
|
|
ShrinkMutation,
|
|
)
|
|
from gp.ops import ADD, COS, DIV, EXP, MUL, POW, SIN, SQUARE, SUB
|
|
from gp.primitive import Var
|
|
from gp.selection import tournament_selection
|
|
from gp.utils import ramped_initialization
|
|
|
|
NUM_VARS = 8
|
|
TEST_POINTS = 10000
|
|
MAX_DEPTH = 10
|
|
MAX_GENERATIONS = 200
|
|
SEED = 17
|
|
np.random.seed(SEED)
|
|
random.seed(SEED)
|
|
X = np.random.uniform(-5.536, 5.536, size=(TEST_POINTS, NUM_VARS))
|
|
operations = [SQUARE, SIN, COS, EXP, ADD, SUB, MUL, DIV, POW]
|
|
terminals = [Var(f"x{i}") for i in range(1, NUM_VARS + 1)]
|
|
|
|
|
|
def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
"""
|
|
Векторизованная версия функции:
|
|
f(x) = sum_{i=1}^n sum_{j=1}^i x_j^2
|
|
x имеет форму (n_samples, n_vars)
|
|
"""
|
|
# Префиксные суммы квадратов по оси переменных
|
|
x_sq = x**2
|
|
prefix_sums = np.cumsum(x_sq, axis=1)
|
|
# Суммируем по i (ось 1)
|
|
return np.sum(prefix_sums, axis=1)
|
|
|
|
|
|
fitness_function = RMSEFitness(target_function, lambda: X)
|
|
combined_mutation = CombinedMutation(
|
|
mutations=[
|
|
GrowMutation(max_depth=MAX_DEPTH),
|
|
NodeReplacementMutation(),
|
|
HoistMutation(),
|
|
ShrinkMutation(),
|
|
],
|
|
probs=[0.4, 0.3, 0.15, 0.15],
|
|
)
|
|
|
|
init_population = ramped_initialization(
|
|
20, [i for i in range(MAX_DEPTH - 9, MAX_DEPTH + 1)], terminals, operations
|
|
)
|
|
|
|
print("Population size:", len(init_population))
|
|
|
|
config = GARunConfig(
|
|
fitness_func=fitness_function,
|
|
crossover_fn=lambda p1, p2: crossover_subtree(p1, p2, max_depth=MAX_DEPTH),
|
|
mutation_fn=combined_mutation,
|
|
selection_fn=lambda p, f: tournament_selection(p, f, k=3),
|
|
init_population=init_population,
|
|
seed=SEED,
|
|
pc=0.85,
|
|
pm=0.15,
|
|
elitism=15,
|
|
max_generations=MAX_GENERATIONS,
|
|
log_every_generation=True,
|
|
save_generations=[1, 10, 20, 30, 40, 50, 100, 150, 200],
|
|
)
|
|
|
|
result = genetic_algorithm(config)
|
|
|
|
|
|
# Выводим результаты
|
|
print(f"Лучшая особь: {result.best_generation.best}")
|
|
print(result.best_generation.best.root.to_str_tree())
|
|
print(f"Лучшее значение фитнеса: {result.best_generation.best_fitness:.6f}")
|
|
print(f"Количество поколений: {result.generations_count}")
|
|
print(f"Время выполнения: {result.time_ms:.2f} мс")
|
|
print("Population size:", len(init_population))
|
|
|
|
mse_fitness = MSEFitness(target_function, lambda: X)
|
|
print(f"MSE: {mse_fitness(result.best_generation.best):.6f}")
|
|
rmse_fitness = RMSEFitness(target_function, lambda: X)
|
|
print(f"RMSE: {rmse_fitness(result.best_generation.best):.6f}")
|
|
mae_fitness = MAEFitness(target_function, lambda: X)
|
|
print(f"MAE: {mae_fitness(result.best_generation.best):.6f}")
|
|
nrmse_fitness = NRMSEFitness(target_function, lambda: X)
|
|
print(f"NRMSE: {nrmse_fitness(result.best_generation.best):.6f}")
|