714 lines
45 KiB
Plaintext
714 lines
45 KiB
Plaintext
//+——————————————————————————————————————————————————————————————————+
|
||||
|
|
//| C_AO_CMAES |
|
|||
|
|
//| Copyright 2007-2025, Andrey Dik |
|
|||
|
|
//| https://www.mql5.com/ru/users/joo |
|
|||
|
|
//———————————————————————————————————————————————————————————————————+
|
|||
|
|
|
|||
|
|
//Article: https://www.mql5.com/ru/articles/18227
|
|||
|
|
|
|||
|
|
#include "#C_AO.mqh"
|
|||
|
|
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
class C_AO_CMAES : public C_AO
|
|||
|
|
{
|
|||
|
|
public: //----------------------------------------------------------
|
|||
|
|
~C_AO_CMAES () { }
|
|||
|
|
C_AO_CMAES ()
|
|||
|
|
{
|
|||
|
|
ao_name = "CMAES";
|
|||
|
|
ao_desc = "Covariance Matrix Adaptation Evolution Strategy";
|
|||
|
|
ao_link = "https://www.mql5.com/ru/articles/18227";
|
|||
|
|
|
|||
|
|
// Параметры по умолчанию
|
|||
|
|
popSize = 50; // Размер популяции по умолчанию (lambda)
|
|||
|
|
mu = 25; // Количество родителей (половина популяции)
|
|||
|
|
learningRateC1 = 0.01; // Скорость обучения для ранг-1 обновления
|
|||
|
|
learningRateCMu = 0.8; // Скорость обучения для ранг-μ обновления
|
|||
|
|
stepSizeDamping = 0.6; // Демпфирование для размера шага
|
|||
|
|
|
|||
|
|
// Создание и инициализация массива параметров
|
|||
|
|
ArrayResize (params, 5);
|
|||
|
|
params [0].name = "popSize"; params [0].val = popSize;
|
|||
|
|
params [1].name = "mu"; params [1].val = mu;
|
|||
|
|
params [2].name = "learningRateC1"; params [2].val = learningRateC1;
|
|||
|
|
params [3].name = "learningRateCMu"; params [3].val = learningRateCMu;
|
|||
|
|
params [4].name = "stepSizeDamping"; params [4].val = stepSizeDamping;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void SetParams ()
|
|||
|
|
{
|
|||
|
|
popSize = (int)params [0].val;
|
|||
|
|
mu = (int)params [1].val;
|
|||
|
|
learningRateC1 = params [2].val;
|
|||
|
|
learningRateCMu = params [3].val;
|
|||
|
|
stepSizeDamping = params [4].val;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Init (const double &rangeMinP [], // минимальные значения
|
|||
|
|
const double &rangeMaxP [], // максимальные значения
|
|||
|
|
const double &rangeStepP [], // размер шага
|
|||
|
|
const int epochsP = 0);
|
|||
|
|
|
|||
|
|
void Moving ();
|
|||
|
|
void Revision ();
|
|||
|
|
|
|||
|
|
//------------------------------------------------------------------
|
|||
|
|
private: //---------------------------------------------------------
|
|||
|
|
// Специфичные параметры CMA-ES
|
|||
|
|
int mu; // Количество родителей (выбранных точек)
|
|||
|
|
double sigma; // Размер шага
|
|||
|
|
double learningRateC1; // Скорость обучения для ранг-1 обновления
|
|||
|
|
double learningRateCMu; // Скорость обучения для ранг-μ обновления
|
|||
|
|
double stepSizeDamping; // Фактор демпфирования для обновления размера шага
|
|||
|
|
|
|||
|
|
// Специфичные структуры данных CMA-ES
|
|||
|
|
double weights []; // Веса рекомбинации
|
|||
|
|
double covMatrix []; // Ковариационная матрица (хранится как одномерный массив)
|
|||
|
|
double B []; // Собственные векторы C
|
|||
|
|
double D []; // Собственные значения (квадратные корни) C
|
|||
|
|
double pc []; // Путь эволюции для C
|
|||
|
|
double ps []; // Сопряженный путь эволюции для sigma
|
|||
|
|
double mu_eff; // Эффективная масса выбора дисперсии
|
|||
|
|
int counteval; // Счетчик вычислений функции с последнего разложения
|
|||
|
|
int eigeneval; // Счетчик генераций, когда выполнялось разложение
|
|||
|
|
double chiN; // Ожидаемая норма N(0,I)
|
|||
|
|
int eigenInterval; // Интервал для разложения на собственные значения
|
|||
|
|
|
|||
|
|
// Переменные для оптимизации производительности
|
|||
|
|
double cs; // Скорость обучения для пути sigma
|
|||
|
|
double cc; // Скорость обучения для пути ранг-1
|
|||
|
|
double damps; // Демпфирование для sigma
|
|||
|
|
double hsig_threshold; // Порог для функции Хевисайда
|
|||
|
|
|
|||
|
|
// Вспомогательные массивы
|
|||
|
|
double y_vec []; // Вектор мутации
|
|||
|
|
double arindex []; // Массив индексов для сортировки
|
|||
|
|
double arfitness []; // Массив фитнеса для сортировки
|
|||
|
|
double temp_vec []; // Временный вектор для матричных операций
|
|||
|
|
double invsqrtC_times_yw []; // Временное хранилище для C^(-1/2) * y_w
|
|||
|
|
|
|||
|
|
// Переменные кэширования для Бокса-Мюллера
|
|||
|
|
double cached_normal;
|
|||
|
|
bool has_cached;
|
|||
|
|
|
|||
|
|
// Вспомогательные методы
|
|||
|
|
void InitDistribution ();
|
|||
|
|
void UpdateDistribution ();
|
|||
|
|
void ComputeEigendecomposition ();
|
|||
|
|
double GetChiN ();
|
|||
|
|
void SortPopulation ();
|
|||
|
|
void ComputeWeights ();
|
|||
|
|
void UpdateMean ();
|
|||
|
|
bool CheckPositiveDefinite ();
|
|||
|
|
void EnforcePositiveDefinite ();
|
|||
|
|
};
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
bool C_AO_CMAES::Init (const double &rangeMinP [], // минимальные значения
|
|||
|
|
const double &rangeMaxP [], // максимальные значения
|
|||
|
|
const double &rangeStepP [], // размер шага
|
|||
|
|
const int epochsP = 0) // количество эпох
|
|||
|
|
{
|
|||
|
|
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
|
|||
|
|
|
|||
|
|
//------------------------------------------------------------------
|
|||
|
|
// Инициализация кэширования Бокса-Мюллера
|
|||
|
|
has_cached = false;
|
|||
|
|
|
|||
|
|
// Инициализация специфичных переменных CMA-ES
|
|||
|
|
sigma = 0.3; // Начальный размер шага (30% от диапазона поиска)
|
|||
|
|
|
|||
|
|
// Вычисление эффективной массы выбора дисперсии
|
|||
|
|
ComputeWeights ();
|
|||
|
|
|
|||
|
|
// Ожидаемая норма N(0,I)
|
|||
|
|
chiN = GetChiN ();
|
|||
|
|
|
|||
|
|
// Вычисление и сохранение параметров стратегии
|
|||
|
|
cs = (mu_eff + 2.0) / (coords + mu_eff + 5.0);
|
|||
|
|
cc = (4.0 + mu_eff / coords) / (coords + 4.0 + 2.0 * mu_eff / coords);
|
|||
|
|
damps = 1.0 + 2.0 * MathMax (0.0, MathSqrt ((mu_eff - 1.0) / (coords + 1.0)) - 1.0) + cs;
|
|||
|
|
hsig_threshold = 1.4 + 2.0 / (coords + 1.0);
|
|||
|
|
|
|||
|
|
// Установка интервала разложения на собственные значения - настройка для производительности
|
|||
|
|
eigenInterval = (int)(coords / (10.0 * MathSqrt (learningRateC1 + learningRateCMu)));
|
|||
|
|
eigenInterval = MathMax (1, eigenInterval);
|
|||
|
|
|
|||
|
|
// Выделение массивов - выделяем только один раз
|
|||
|
|
ArrayResize (covMatrix, coords * coords);
|
|||
|
|
ArrayResize (B, coords * coords);
|
|||
|
|
ArrayResize (D, coords);
|
|||
|
|
ArrayResize (pc, coords);
|
|||
|
|
ArrayResize (ps, coords);
|
|||
|
|
ArrayResize (y_vec, coords);
|
|||
|
|
ArrayResize (arindex, popSize);
|
|||
|
|
ArrayResize (arfitness, popSize);
|
|||
|
|
ArrayResize (temp_vec, coords);
|
|||
|
|
ArrayResize (invsqrtC_times_yw, coords);
|
|||
|
|
|
|||
|
|
// Инициализация путей эволюции нулями
|
|||
|
|
ArrayInitialize (pc, 0);
|
|||
|
|
ArrayInitialize (ps, 0);
|
|||
|
|
|
|||
|
|
// Быстрая инициализация единичной ковариационной матрицы и разложения
|
|||
|
|
ArrayInitialize (covMatrix, 0.0);
|
|||
|
|
ArrayInitialize (B, 0.0);
|
|||
|
|
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
covMatrix [i * coords + i] = 1.0;
|
|||
|
|
B [i * coords + i] = 1.0;
|
|||
|
|
D [i] = 1.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Инициализация начального распределения
|
|||
|
|
InitDistribution ();
|
|||
|
|
|
|||
|
|
// Сброс счетчиков вычислений
|
|||
|
|
counteval = 0;
|
|||
|
|
eigeneval = 0;
|
|||
|
|
|
|||
|
|
// Принудительный пересчет фитнеса
|
|||
|
|
revision = true;
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
void C_AO_CMAES::Moving ()
|
|||
|
|
{
|
|||
|
|
// Генерация lambda новых потомков
|
|||
|
|
for (int k = 0; k < popSize; k++)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
// Применение преобразования y = B*D*z
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
y_vec [i] = 0.0;
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
y_vec [i] += B [i * coords + j] * D [j] * u.PowerDistribution (0.0, -8, 8, 20);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Генерация потомка: x_k = m + σ * y
|
|||
|
|
a [k].c [i] = cB [i] + sigma * y_vec [i];
|
|||
|
|
a [k].c [i] = u.SeInDiSp (a [k].c [i], rangeMin [i], rangeMax [i], rangeStep [i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отметка для пересчета фитнеса
|
|||
|
|
revision = true;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
void C_AO_CMAES::Revision ()
|
|||
|
|
{
|
|||
|
|
if (!revision) return;
|
|||
|
|
|
|||
|
|
revision = false;
|
|||
|
|
|
|||
|
|
// Сортировка популяции по фитнесу
|
|||
|
|
SortPopulation ();
|
|||
|
|
|
|||
|
|
// Обновление параметров распределения на основе выбранных особей
|
|||
|
|
UpdateDistribution ();
|
|||
|
|
|
|||
|
|
// Обновление счетчика вычислений
|
|||
|
|
counteval++;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Инициализация распределения поиска |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::InitDistribution ()
|
|||
|
|
{
|
|||
|
|
// Установка начального среднего в центр пространства поиска
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
cB [i] = u.RNDfromCI (rangeMin [i], rangeMax [i]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int k = 0; k < popSize; k++)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
// Генерация равномерно распределенной точки
|
|||
|
|
a [k].c [i] = u.RNDfromCI (rangeMin [i], rangeMax [i]);
|
|||
|
|
a [k].c [i] = u.SeInDiSp (a [k].c [i], rangeMin [i], rangeMax [i], rangeStep [i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Вычисление ожидаемой нормы N(0,I) |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
double C_AO_CMAES::GetChiN ()
|
|||
|
|
{
|
|||
|
|
double n = (double)coords;
|
|||
|
|
return MathSqrt (n) * (1.0 - 1.0 / (4.0 * n) + 1.0 / (21.0 * n * n));
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Сортировка популяции по фитнесу |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::SortPopulation ()
|
|||
|
|
{
|
|||
|
|
// Копирование значений фитнеса и индексов
|
|||
|
|
for (int i = 0; i < popSize; i++)
|
|||
|
|
{
|
|||
|
|
arindex [i] = i;
|
|||
|
|
arfitness [i] = a [i].f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int i = 1; i < popSize; i++)
|
|||
|
|
{
|
|||
|
|
double tempFitness = arfitness [i];
|
|||
|
|
double tempIndex = arindex [i];
|
|||
|
|
int j = i - 1;
|
|||
|
|
|
|||
|
|
// Сортировка по убыванию (для максимизации)
|
|||
|
|
while (j >= 0 && arfitness [j] < tempFitness)
|
|||
|
|
{
|
|||
|
|
arfitness [j + 1] = arfitness [j];
|
|||
|
|
arindex [j + 1] = arindex [j];
|
|||
|
|
j--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
arfitness [j + 1] = tempFitness;
|
|||
|
|
arindex [j + 1] = tempIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обновление лучшего решения при необходимости
|
|||
|
|
if (arfitness [0] > fB)
|
|||
|
|
{
|
|||
|
|
fB = arfitness [0];
|
|||
|
|
int bestIdx = (int)arindex [0];
|
|||
|
|
ArrayCopy (cB, a [bestIdx].c, 0, 0, coords);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Обновление среднего с использованием взвешенной рекомбинации |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::UpdateMean ()
|
|||
|
|
{
|
|||
|
|
// Взвешенная рекомбинация: m^(g+1) = Σ w_i * x_{i:λ}^(g+1)
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
double meanSum = 0.0;
|
|||
|
|
for (int i = 0; i < mu; i++)
|
|||
|
|
{
|
|||
|
|
int idx = (int)arindex [i];
|
|||
|
|
meanSum += weights [i] * a [idx].c [j];
|
|||
|
|
}
|
|||
|
|
cB [j] = meanSum;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Вычисление весов взвешенной рекомбинации |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::ComputeWeights ()
|
|||
|
|
{
|
|||
|
|
// Выделение массива весов
|
|||
|
|
ArrayResize (weights, mu);
|
|||
|
|
|
|||
|
|
// Предварительное вычисление log(mu + 0.5)
|
|||
|
|
double log_mu_plus_half = MathLog (mu + 0.5);
|
|||
|
|
|
|||
|
|
// Вычисление положительных весов
|
|||
|
|
double sum = 0.0;
|
|||
|
|
for (int i = 0; i < mu; i++)
|
|||
|
|
{
|
|||
|
|
weights [i] = log_mu_plus_half - MathLog (i + 1);
|
|||
|
|
sum += weights [i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Нормализация весов
|
|||
|
|
double sum_weights = 0.0;
|
|||
|
|
double sum_squares = 0.0;
|
|||
|
|
for (int i = 0; i < mu; i++)
|
|||
|
|
{
|
|||
|
|
weights [i] /= sum;
|
|||
|
|
sum_weights += weights [i];
|
|||
|
|
sum_squares += weights [i] * weights [i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Вычисление эффективной массы выбора дисперсии
|
|||
|
|
mu_eff = sum_weights * sum_weights / sum_squares;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Обновление параметров распределения |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::UpdateDistribution ()
|
|||
|
|
{
|
|||
|
|
// Проверка необходимости разложения на собственные значения
|
|||
|
|
if (counteval - eigeneval > eigenInterval)
|
|||
|
|
{
|
|||
|
|
ComputeEigendecomposition ();
|
|||
|
|
eigeneval = counteval;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Сохранение старого среднего
|
|||
|
|
double oldMean [];
|
|||
|
|
ArrayResize (oldMean, coords);
|
|||
|
|
ArrayCopy (oldMean, cB, 0, 0, coords);
|
|||
|
|
|
|||
|
|
// Обновление среднего
|
|||
|
|
UpdateMean ();
|
|||
|
|
|
|||
|
|
// Вычисление смещения среднего
|
|||
|
|
double y_w [];
|
|||
|
|
ArrayResize (y_w, coords);
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
y_w [j] = (cB [j] - oldMean [j]) / sigma;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Вычисление C^(-1/2) * y_w
|
|||
|
|
// Шаг 1: B^T * y_w
|
|||
|
|
ArrayInitialize (temp_vec, 0.0);
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
temp_vec [i] += B [j * coords + i] * y_w [j];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Шаг 2: D^(-1) * (B^T * y_w)
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
temp_vec [i] /= D [i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Шаг 3: B * D^(-1) * B^T * y_w
|
|||
|
|
ArrayInitialize (invsqrtC_times_yw, 0.0);
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
invsqrtC_times_yw [i] += B [i * coords + j] * temp_vec [j];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обновление пути эволюции для sigma
|
|||
|
|
double norm_ps_squared = 0.0;
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
ps [i] = (1.0 - cs) * ps [i] + MathSqrt (cs * (2.0 - cs) * mu_eff) * invsqrtC_times_yw [i];
|
|||
|
|
norm_ps_squared += ps [i] * ps [i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция Хевисайда
|
|||
|
|
double norm_ps = MathSqrt (norm_ps_squared);
|
|||
|
|
double expected_length = MathSqrt (1.0 - MathPow (1.0 - cs, 2.0 * counteval)) * chiN;
|
|||
|
|
bool hsig = norm_ps / expected_length < hsig_threshold;
|
|||
|
|
|
|||
|
|
// Обновление пути эволюции для C
|
|||
|
|
double delta_hsig = hsig ? 1.0 : 0.0;
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
pc [i] = (1.0 - cc) * pc [i] + delta_hsig * MathSqrt (cc * (2.0 - cc) * mu_eff) * y_w [i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Подготовка ранг-1 обновления
|
|||
|
|
double c1a [];
|
|||
|
|
ArrayResize (c1a, coords * coords);
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j <= i; j++)
|
|||
|
|
{
|
|||
|
|
c1a [i * coords + j] = c1a [j * coords + i] = pc [i] * pc [j];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Подготовка ранг-μ обновления
|
|||
|
|
double cmu [];
|
|||
|
|
ArrayResize (cmu, coords * coords);
|
|||
|
|
ArrayInitialize (cmu, 0.0);
|
|||
|
|
|
|||
|
|
for (int k = 0; k < mu; k++)
|
|||
|
|
{
|
|||
|
|
int idx = (int)arindex [k];
|
|||
|
|
|
|||
|
|
// Вычисление y_i = (x_i - m_old) / sigma
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
temp_vec [i] = (a [idx].c [i] - oldMean [i]) / sigma;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Добавление взвешенного внешнего произведения
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j <= i; j++)
|
|||
|
|
{
|
|||
|
|
double update = weights [k] * temp_vec [i] * temp_vec [j];
|
|||
|
|
cmu [i * coords + j] += update;
|
|||
|
|
if (i != j) cmu [j * coords + i] += update;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обновление ковариационной матрицы C
|
|||
|
|
double c1 = learningRateC1;
|
|||
|
|
double cmu_rate = learningRateCMu;
|
|||
|
|
|
|||
|
|
// Корректировка c1 если hsig false (застой прогресса)
|
|||
|
|
if (!hsig)
|
|||
|
|
{
|
|||
|
|
c1 *= (1.0 - (1.0 - delta_hsig) * cc * (2.0 - cc));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double one_minus_c1_cmu = 1.0 - c1 - cmu_rate;
|
|||
|
|
|
|||
|
|
// Обновление C с ранг-1 и ранг-μ обновлениями
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j <= i; j++)
|
|||
|
|
{
|
|||
|
|
covMatrix [i * coords + j] = one_minus_c1_cmu * covMatrix [i * coords + j] +
|
|||
|
|
c1 * c1a [i * coords + j] +
|
|||
|
|
cmu_rate * cmu [i * coords + j];
|
|||
|
|
|
|||
|
|
// Сохранение симметрии
|
|||
|
|
if (i != j)
|
|||
|
|
{
|
|||
|
|
covMatrix [j * coords + i] = covMatrix [i * coords + j];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обеспечение положительной определенности
|
|||
|
|
if (counteval % (10 * eigenInterval) == 0)
|
|||
|
|
{
|
|||
|
|
EnforcePositiveDefinite ();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обновление размера шага sigma
|
|||
|
|
double exponent = (cs / damps) * (norm_ps / chiN - 1.0);
|
|||
|
|
sigma *= MathExp (exponent);
|
|||
|
|
|
|||
|
|
// Ограничение sigma для численной стабильности
|
|||
|
|
double min_sigma = 1e-16;
|
|||
|
|
double max_eigenvalue = D [0]; // D отсортирован по убыванию
|
|||
|
|
double max_sigma = 1e4 * MathMax (1.0, MathSqrt (max_eigenvalue));
|
|||
|
|
|
|||
|
|
if (sigma < min_sigma) sigma = min_sigma;
|
|||
|
|
else
|
|||
|
|
if (sigma > max_sigma) sigma = max_sigma;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Вычисление разложения на собственные значения методом Якоби |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::ComputeEigendecomposition ()
|
|||
|
|
{
|
|||
|
|
// Создание копии ковариационной матрицы для разложения
|
|||
|
|
double C_copy [];
|
|||
|
|
ArrayResize (C_copy, coords * coords);
|
|||
|
|
ArrayCopy (C_copy, covMatrix);
|
|||
|
|
|
|||
|
|
// Инициализация B как единичной матрицы
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
B [i * coords + j] = (i == j) ? 1.0 : 0.0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Улучшенное разложение Якоби на собственные значения
|
|||
|
|
int max_iterations = 10; //50 * coords;
|
|||
|
|
double tolerance = 0.01; //1e-14 * coords * coords;
|
|||
|
|
|
|||
|
|
for (int iter = 0; iter < max_iterations; iter++)
|
|||
|
|
{
|
|||
|
|
// Поиск наибольшего внедиагонального элемента
|
|||
|
|
double max_val = 0.0;
|
|||
|
|
int p = 0, q = 1;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < coords - 1; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = i + 1; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
double val = MathAbs (C_copy [i * coords + j]);
|
|||
|
|
if (val > max_val)
|
|||
|
|
{
|
|||
|
|
max_val = val;
|
|||
|
|
p = i;
|
|||
|
|
q = j;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка сходимости
|
|||
|
|
if (max_val < tolerance) break;
|
|||
|
|
|
|||
|
|
// Вычисление угла поворота
|
|||
|
|
double app = C_copy [p * coords + p];
|
|||
|
|
double aqq = C_copy [q * coords + q];
|
|||
|
|
double apq = C_copy [p * coords + q];
|
|||
|
|
|
|||
|
|
double phi = 0.5 * MathArctan (2.0 * apq / (aqq - app + 1e-14));
|
|||
|
|
double c = MathCos (phi);
|
|||
|
|
double s = MathSin (phi);
|
|||
|
|
|
|||
|
|
// Обновление элементов матрицы
|
|||
|
|
double app_new = c * c * app - 2 * c * s * apq + s * s * aqq;
|
|||
|
|
double aqq_new = s * s * app + 2 * c * s * apq + c * c * aqq;
|
|||
|
|
|
|||
|
|
C_copy [p * coords + p] = app_new;
|
|||
|
|
C_copy [q * coords + q] = aqq_new;
|
|||
|
|
C_copy [p * coords + q] = C_copy [q * coords + p] = 0.0;
|
|||
|
|
|
|||
|
|
// Обновление других элементов в строках/столбцах p и q
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
if (i != p && i != q)
|
|||
|
|
{
|
|||
|
|
double aip = C_copy [i * coords + p];
|
|||
|
|
double aiq = C_copy [i * coords + q];
|
|||
|
|
C_copy [i * coords + p] = C_copy [p * coords + i] = c * aip - s * aiq;
|
|||
|
|
C_copy [i * coords + q] = C_copy [q * coords + i] = s * aip + c * aiq;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Обновление собственных векторов
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
double bip = B [i * coords + p];
|
|||
|
|
double biq = B [i * coords + q];
|
|||
|
|
B [i * coords + p] = c * bip - s * biq;
|
|||
|
|
B [i * coords + q] = s * bip + c * biq;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Извлечение собственных значений и обеспечение положительности
|
|||
|
|
double min_eigenvalue = 1e-14;
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
D [i] = MathSqrt (MathMax (min_eigenvalue, C_copy [i * coords + i]));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Сортировка собственных значений и векторов по убыванию
|
|||
|
|
for (int i = 0; i < coords - 1; i++)
|
|||
|
|
{
|
|||
|
|
int max_idx = i;
|
|||
|
|
for (int j = i + 1; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
if (D [j] > D [max_idx]) max_idx = j;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (max_idx != i)
|
|||
|
|
{
|
|||
|
|
// Обмен собственных значений
|
|||
|
|
double temp = D [i];
|
|||
|
|
D [i] = D [max_idx];
|
|||
|
|
D [max_idx] = temp;
|
|||
|
|
|
|||
|
|
// Обмен собственных векторов
|
|||
|
|
for (int k = 0; k < coords; k++)
|
|||
|
|
{
|
|||
|
|
temp = B [k * coords + i];
|
|||
|
|
B [k * coords + i] = B [k * coords + max_idx];
|
|||
|
|
B [k * coords + max_idx] = temp;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Проверка положительной определенности ковариационной матрицы |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
bool C_AO_CMAES::CheckPositiveDefinite ()
|
|||
|
|
{
|
|||
|
|
// Быстрая проверка: все диагональные элементы должны быть положительными
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
if (covMatrix [i * coords + i] <= 0) return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка минимального собственного значения на положительность
|
|||
|
|
double min_eigenvalue = D [coords - 1]; // D отсортирован по убыванию
|
|||
|
|
return min_eigenvalue > 1e-14;
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————
|
|||
|
|
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
//| Обеспечение положительной определенности ковариационной матрицы |
|
|||
|
|
//+------------------------------------------------------------------+
|
|||
|
|
void C_AO_CMAES::EnforcePositiveDefinite ()
|
|||
|
|
{
|
|||
|
|
// Метод 1: Добавление малого значения к диагонали
|
|||
|
|
double min_diag = 1e308; // Очень большое число
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
if (covMatrix [i * coords + i] < min_diag)
|
|||
|
|
{
|
|||
|
|
min_diag = covMatrix [i * coords + i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (min_diag < 1e-10)
|
|||
|
|
{
|
|||
|
|
double correction = 1e-10 - min_diag;
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
covMatrix [i * coords + i] += correction;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Метод 2: Обеспечение симметрии
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = i + 1; j < coords; j++)
|
|||
|
|
{
|
|||
|
|
double avg = (covMatrix [i * coords + j] + covMatrix [j * coords + i]) * 0.5;
|
|||
|
|
covMatrix [i * coords + j] = covMatrix [j * coords + i] = avg;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если все еще не положительно определена, выполнить разложение и исправить
|
|||
|
|
if (!CheckPositiveDefinite ())
|
|||
|
|
{
|
|||
|
|
ComputeEigendecomposition ();
|
|||
|
|
|
|||
|
|
double min_eigenvalue = 1e-10;
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
if (D [i] < MathSqrt (min_eigenvalue))
|
|||
|
|
{
|
|||
|
|
D [i] = MathSqrt (min_eigenvalue);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Восстановление C = B * D^2 * B^T
|
|||
|
|
ArrayInitialize (covMatrix, 0.0);
|
|||
|
|
for (int i = 0; i < coords; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j <= i; j++)
|
|||
|
|
{
|
|||
|
|
double sum = 0.0;
|
|||
|
|
for (int k = 0; k < coords; k++)
|
|||
|
|
{
|
|||
|
|
sum += B [i * coords + k] * D [k] * D [k] * B [j * coords + k];
|
|||
|
|
}
|
|||
|
|
covMatrix [i * coords + j] = covMatrix [j * coords + i] = sum;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//————————————————————————————————————————————————————————————————————�
|