196 lines
6.3 KiB
Python
196 lines
6.3 KiB
Python
import random
|
||
from math import log
|
||
|
||
import numpy as np
|
||
from numpy.typing import NDArray
|
||
|
||
from gp import Chromosome
|
||
from gp.crossovers import crossover_subtree
|
||
from gp.fitness import (
|
||
HuberFitness,
|
||
MAEFitness,
|
||
MSEFitness,
|
||
NRMSEFitness,
|
||
PenalizedFitness,
|
||
RMSEFitness,
|
||
)
|
||
from gp.ga import GARunConfig, genetic_algorithm
|
||
from gp.mutations import (
|
||
grow_mutation,
|
||
hoist_mutation,
|
||
node_replacement_mutation,
|
||
shrink_mutation,
|
||
)
|
||
from gp.ops import ADD, COS, DIV, EXP, MUL, NEG, POW, SIN, SQUARE, SUB
|
||
from gp.population import ramped_initialization
|
||
from gp.primitive import Const, Var
|
||
from gp.selection import roulette_selection, tournament_selection
|
||
|
||
NUM_VARS = 9
|
||
TEST_POINTS = 10000
|
||
MAX_DEPTH = 15
|
||
MAX_GENERATIONS = 200
|
||
np.random.seed(17)
|
||
random.seed(17)
|
||
X = np.random.uniform(-5.536, 5.536, size=(TEST_POINTS, NUM_VARS))
|
||
# axes = [np.linspace(-5.536, 5.536, TEST_POINTS) for _ in range(NUM_VARS)]
|
||
# X = np.array(np.meshgrid(*axes)).T.reshape(-1, NUM_VARS)
|
||
operations = [SQUARE, SIN, COS, EXP, ADD, SUB, MUL, DIV, POW]
|
||
# operations = [SQUARE, ADD, SUB, MUL]
|
||
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) = x1 + x2 + sin(x1)
|
||
# x имеет форму (n_samples, n_vars)
|
||
# """
|
||
# x1 = x[:, 0]
|
||
# x2 = x[:, 1]
|
||
# return x1 + x2 + np.sin(x1) + np.sin(x2) + np.exp(-x1) + np.cos(x2)
|
||
|
||
|
||
# def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
||
# """
|
||
# Простая тестовая функция: сумма косинусов всех переменных.
|
||
# f(x) = sum_i cos(x_i)
|
||
# x имеет форму (n_samples, n_vars)
|
||
# """
|
||
# return np.sum(x, axis=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)
|
||
|
||
|
||
# def target_function(x: NDArray[np.float64]) -> NDArray[np.float64]:
|
||
# """
|
||
# Rastrigin function.
|
||
# f(x) = 10 * n + sum(x_i^2 - 10 * cos(2πx_i))
|
||
# x: shape (n_samples, n_vars)
|
||
# """
|
||
# n = x.shape[1]
|
||
# return 10 * n + np.sum(x**2 - 10 * np.cos(2 * np.pi * x), axis=1)
|
||
|
||
# fitness_function = MSEFitness(target_function, lambda: X)
|
||
# fitness = HuberFitness(target_function, lambda: X, delta=1.0)
|
||
# fitness_function = PenalizedFitness(
|
||
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
||
# )
|
||
fitness_function = RMSEFitness(target_function, lambda: X)
|
||
# fitness_function = PenalizedFitness(
|
||
# target_function, lambda: X, base_fitness=fitness, lambda_=0.003
|
||
# )
|
||
|
||
|
||
def adaptive_mutation(
|
||
chromosome: Chromosome,
|
||
generation: int,
|
||
max_generations: int,
|
||
max_depth: int,
|
||
) -> Chromosome:
|
||
"""Адаптивная мутация.
|
||
|
||
Меняет вероятность типов мутации по ходу эволюции:
|
||
- Ранняя фаза (<30%): 70% grow, 30% shrink
|
||
- Средняя фаза (30–70%): 40% grow, 60% shrink
|
||
- Поздняя фаза (>=70%): 20% grow, 80% shrink
|
||
"""
|
||
|
||
r = random.random()
|
||
|
||
if r < 0.4:
|
||
return grow_mutation(chromosome, max_depth=max_depth)
|
||
elif r < 0.7:
|
||
return node_replacement_mutation(chromosome)
|
||
elif r < 0.85:
|
||
return hoist_mutation(chromosome)
|
||
|
||
return shrink_mutation(chromosome)
|
||
|
||
|
||
# def adaptive_mutation(
|
||
# chromosome: Chromosome,
|
||
# generation: int,
|
||
# max_generations: int,
|
||
# max_depth: int,
|
||
# ) -> Chromosome:
|
||
# """Адаптивная мутация.
|
||
|
||
# Меняет вероятность типов мутации по ходу эволюции:
|
||
# - Ранняя фаза (<30%): 70% grow, 30% shrink
|
||
# - Средняя фаза (30–70%): 40% grow, 60% shrink
|
||
# - Поздняя фаза (>=70%): 20% grow, 80% shrink
|
||
# """
|
||
|
||
# # Вычисляем прогресс в диапазоне [0, 1]
|
||
# if max_generations <= 0:
|
||
# progress = 0.0
|
||
# else:
|
||
# progress = min(1.0, max(0.0, generation / max_generations))
|
||
|
||
# r = random.random()
|
||
|
||
# # Определяем тип мутации
|
||
# if progress < 0.3:
|
||
# do_grow = r < 0.7
|
||
# elif progress < 0.7:
|
||
# do_grow = r < 0.4
|
||
# else:
|
||
# do_grow = r < 0.2
|
||
|
||
# # Выполняем выбранную мутацию
|
||
# if do_grow:
|
||
# return grow_mutation(chromosome, max_depth=max_depth)
|
||
# return shrink_mutation(chromosome)
|
||
|
||
|
||
config = GARunConfig(
|
||
fitness_func=fitness_function,
|
||
crossover_fn=lambda p1, p2: crossover_subtree(p1, p2, max_depth=8),
|
||
mutation_fn=lambda chrom, gen_num: adaptive_mutation(
|
||
chrom, gen_num, MAX_GENERATIONS, MAX_DEPTH
|
||
),
|
||
# selection_fn=roulette_selection,
|
||
selection_fn=lambda p, f: tournament_selection(p, f, k=3),
|
||
init_population=ramped_initialization(
|
||
10, [4, 5, 6, 6, 7, 7, 8, 9, 10, 11], terminals, operations
|
||
),
|
||
seed=17,
|
||
pc=0.9,
|
||
pm=0.3,
|
||
elitism=10,
|
||
max_generations=MAX_GENERATIONS,
|
||
log_every_generation=True,
|
||
)
|
||
|
||
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} мс")
|
||
|
||
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}")
|
||
huber_fitness = HuberFitness(target_function, lambda: X, delta=1.0)
|
||
print(f"Huber: {huber_fitness(result.best_generation.best):.6f}")
|
||
nrmse_fitness = NRMSEFitness(target_function, lambda: X)
|
||
print(f"NRMSE: {nrmse_fitness(result.best_generation.best):.6f}")
|