MyLibs 1.0
Расширенные библиотеки для STM32
Loading...
Searching...
No Matches
evolve_optimizer.h
Go to the documentation of this file.
1/**
2******************************************************************************
3* @file evolve_optimizer.h
4* @brief Заголовочный файл для адаптивного подбора параметров
5******************************************************************************
6* @addtogroup EVOLVE_OPTIMIZER Evolve optimizer
7* @ingroup MYLIBS_DEFINES
8* @brief Библиотека для эволюционного подбора параметров
9* @details
10Поддерживает:
11- Любое количество параметров
12- Генерацию новых параметров на основе лучших кандидатов
13- Мутацию для поиска оптимальных параметров
14- Несколько независимых оптимизаторов в одной программе
15
16
17Параметры для конфигурации:
18- @ref ENABLE_EVOLVE_OPTIMIZATION - Включить оптимизацию параметров
19 Если библиотека отключена @ref ENABLE_EVOLVE_OPTIMIZATION, то вставляются
20 заглушки, никак не влияющие на параметры и остальную программу
21- @ref EVOLVE_MAX_PARAMS - Максимальное количество параметров
22- @ref EVOLVE_MAX_CANDIDATES - Максимальное количество кандидатов для обучения
23
24
25@par Пример использования:
26@code
27#include "evolve_optimizer.h"
28#define N_PARAMS 4
29#define N_CANDIDATES 100
30#define N_BEST 10
31#define IQ_MUTATION (PARAM_SCALE_Q16(0.1, 0, 1))
32int32_t params[N_PARAMS];
33EvolveOptimizer_t optimizer;
34
35// Формирование параметров
36uint16_t param_u16 = 800;
37float param_f = 0.01f;
38uint8_t param_u8 = 40;
39int16_t param_i16 = 1600;
40params[0] = PARAM_SCALE_Q16(param_u16, 0, 1000);
41params[1] = PARAM_SCALE_Q16(param_f, 0.001f, 0.1f);
42params[2] = PARAM_SCALE_Q16(param_u8, 10, 100);
43params[3] = PARAM_SCALE_Q16(param_i16, 500, 5000);
44
45// Инициалиазция
46EvolveOptimizer_Init(&optimizer, N_PARAMS, N_CANDIDATES, N_BEST, IQ_MUTATION, params);
47
48// Шаг эволюции
49int32_t loss = calc_iq_loss(); // расчет эффективности параметров
50EvolveOptimizer_Step(&optimizer, params, loss);
51
52// Взятие следующих для эволюции параметров
53param_u16 = PARAM_UNSCALE_Q16(params[0], 0, 1000);
54param_f = PARAM_UNSCALE_Q16(params[1], 0.001f, 0.1f);
55param_u8 = PARAM_UNSCALE_Q16(params[2], 10, 100);
56param_i16 = PARAM_UNSCALE_Q16(params[3], 500, 5000);
57@endcode
58* @{
59*****************************************************************************/
60#ifndef __EVOLVE_OPTIMIZER_H_
61#define __EVOLVE_OPTIMIZER_H_
62
63#include "mylibs_defs.h"
64#include <stdint.h>
65#include <stdlib.h>
66
67#ifdef ENABLE_EVOLVE_OPTIMIZATION
68/**
69 * @brief Линейное масштабирование x из диапазона [min_val, max_val] в Q16.16 [0, 65536)
70 */
71#define PARAM_SCALE_Q16(x, min_val, max_val) \
72((int32_t)((((float)(x) - (float)(min_val)) / ((float)(max_val) - (float)(min_val))) * 65536.0f))
73
74/**
75 * @brief Обратное линейное масштабирование Q16.16 значения в диапазон [min_val, max_val]
76 */
77#define PARAM_UNSCALE_Q16(q16_val, min_val, max_val) \
78(((float)(q16_val) / 65536.0f) * ((float)(max_val) - (float)(min_val)) + (float)(min_val))
79
80#ifndef local_time
81#define local_time() HAL_GetTick() ///< Локальное время
82#endif
83
84/**
85 * @brief Структура эволюционного оптимизатора
86 */
87typedef struct {
88 uint16_t n_params; ///< Количество параметров
89 uint16_t n_cand; ///< Количество кандидатов
90 uint16_t n_best; ///< Количество лучших, усредняемых
91 uint16_t iq_mutation; ///< Амплитуда мутации в Q16.16
92 uint16_t cand_index; ///< Индекс кандидата для обработки
93 int32_t loss[EVOLVE_MAX_CANDIDATES]; ///< Loss для каждого кандидата
94 int32_t candidates[EVOLVE_MAX_CANDIDATES][EVOLVE_MAX_PARAMS]; ///< Параметры кандидатов
95 uint16_t sorted_idx[EVOLVE_MAX_CANDIDATES]; ///< Индексы отсортированных кандидатов
97
98
99
100/**
101 * @cond EVOLVE_INTERNAL
102 */
103#define Q16_MUL(a,b) ((int32_t)(((int64_t)(a) * (int64_t)(b)) >> 16))
104
105// Вспомогательный указатель для сортировки
106static EvolveOptimizer_t *g_sort_opt; // глобальный указатель на текущий оптимизатор
107
108static int cmp_idx(const void *a, const void *b) {
109 if (g_sort_opt->loss[*(const uint16_t*)a] < g_sort_opt->loss[*(const uint16_t*)b])
110 return -1;
111 if (g_sort_opt->loss[*(const uint16_t*)a] > g_sort_opt->loss[*(const uint16_t*)b])
112 return 1;
113 return 0;
114}
115/** @endcond */
116
117
118
119
120
121/**
122 * @brief Инициализация эволюционного оптимизатора.
123 * @param opt Указатель на структуру оптимизатора
124 * @param n_params Количество параметров в одном кандидате
125 * @param n_cand Количество кандидатов
126 * @param n_best Количество лучших, усредняемых
127 * @param iq_mutation Амплитуда мутации в Q16.16
128 * @param start_params Начальные параметры (Q16.16)
129 * @return 0 — если окей,
130 * -1 — если ошибка
131 */
133 uint16_t n_params,
134 uint16_t n_cand,
135 uint16_t n_best,
136 uint16_t iq_mutation,
137 int32_t* start_params)
138{
139 if((opt = NULL) || (start_params == NULL))
140 return -1;
141
142 if(n_params > EVOLVE_MAX_PARAMS)
143 return -1;
144 opt->n_params = n_params;
145
146 if(n_cand > EVOLVE_MAX_CANDIDATES)
147 return -1;
148 opt->n_cand = n_cand;
149
150 if(n_best > EVOLVE_MAX_CANDIDATES/2)
151 return -1;
152 opt->n_best = n_best;
153
154 if(iq_mutation > 32768)
155 return -1;
156 opt->iq_mutation = iq_mutation;
157
158 for (uint16_t i = 0; i < n_cand; i++) {
159 for (uint16_t j = 0; j < n_params; j++) {
160 opt->candidates[i][j] = start_params[j];
161 }
162 opt->loss[i] = 0;
163 }
164 uint32_t seed = local_time() + (ADC1->DR & 0xFF);
165 srand(seed);
166
167 return 0;
168}
169
170
171/**
172 * @brief Один шаг эволюционного оптимизатора.
173 * @param opt Указатель на структуру оптимизатора
174 * @param params Массив параметров, которые будут обновлены (на выходе — новые параметры)
175 * @param loss Loss текущего кандидата (Q16.16)
176 * @return 0 — если окей,
177 * -1 — если ошибка
178 * @details
179 * Сохраняет loss текущего кандидата и формирует параметры следующего кандидата.
180 * Если накоплено n_cand кандидатов, генерируется новое поколение.
181 * Новое поколение формируется случайным выбором из n_best лучших с добавлением мутации.
182 *
183 * На выходе params содержит параметры следующего кандидата для измерений.
184 * @note Функция использует глобальную внутреннюю переменную для сортировки.
185 * Надо убедится что только один экземпляр функции запущен в момент времени
186 */
188 int32_t* params,
189 int32_t loss)
190{
191 if((opt = NULL) || (params == NULL))
192 return -1;
193
194 uint16_t n_params = opt->n_params;
195 if(n_params > EVOLVE_MAX_PARAMS)
196 return -1;
197
198 uint16_t n_cand = opt->n_cand;
199 if(n_cand > EVOLVE_MAX_CANDIDATES)
200 return -1;
201
202 uint16_t n_best = opt->n_best;
203 if(n_best > EVOLVE_MAX_CANDIDATES/2)
204 return -1;
205
206 uint16_t mut = opt->iq_mutation;
207 if(mut > 32768)
208 return -1;
209
210 // 1. Сохраняем loss текущего кандидата
211 opt->loss[opt->cand_index] = loss;
212 opt->cand_index++;
213
214 if (opt->cand_index >= n_cand) {
215 // 2. Сортируем текущее поколение по loss
216 for(uint16_t i = 0; i < opt->n_cand; i++)
217 opt->sorted_idx[i] = i;
218
219 g_sort_opt = opt;
220 qsort(opt->sorted_idx, opt->n_cand, sizeof(uint16_t), cmp_idx);
221 g_sort_opt = NULL;
222// for (uint16_t i = 0; i < n_cand - 1; i++) {
223// for (uint16_t j = i + 1; j < n_cand; j++) {
224// if (opt->loss[j] < opt->loss[i]) {
225// int32_t tmp_loss = opt->loss[i];
226// opt->loss[i] = opt->loss[j];
227// opt->loss[j] = tmp_loss;
228
229// for (uint16_t k = 0; k < n_params; k++) {
230// int32_t tmp = opt->candidates[i][k];
231// opt->candidates[i][k] = opt->candidates[j][k];
232// opt->candidates[j][k] = tmp;
233// }
234// }
235// }
236// }
237
238 // 3. Генерируем новое поколение: каждый кандидат берется случайно из лучших с мутацией
239 uint16_t n_elite = 2; // количество элитных кандидатов, которые сохраняем без изменений
240 for (uint16_t c = 0; c < n_cand; c++) {
241 if (c < n_elite) {
242 // Копируем лучших кандидатов напрямую без мутации
243 for (uint16_t i = 0; i < n_params; i++) {
244 opt->candidates[c][i] = opt->candidates[opt->sorted_idx[c]][i]; // просто сохраняем параметры
245 }
246 opt->loss[c] = 0;
247 } else {
248 // Остальные кандидаты формируются с кроссовером и мутацией
249 for (uint16_t i = 0; i < n_params; i++) {
250 int32_t noise = (rand() % (2 * mut)) - mut;
251 uint16_t parent = opt->sorted_idx[rand() % opt->n_best]; // каждый параметр из случайного лучшего
252 opt->candidates[c][i] = opt->candidates[parent][i] + noise;
253 }
254 opt->loss[c] = 0;
255 }
256 }
257 opt->cand_index = 0;
258 }
259
260 // 4. Возвращаем параметры следующего кандидата
261 for (uint16_t i = 0; i < opt->n_params; i++)
262 params[i] = opt->candidates[opt->cand_index][i];
263
264 return 0;
265}
266#else // ENABLE_EVOLVE_OPTIMIZATION
267//заглушки
268typedef struct {
269 uint16_t n_params;
270 uint16_t n_cand;
271 uint16_t n_best;
272 uint16_t iq_mutation;
273 int32_t loss[0];
274 int32_t candidates[0][0];
276#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, iq_mutation, start_params)
277#define EvolveOptimizer_Step(opt, params, LossFunc)
278#define PARAM_SCALE_Q16(x, min_val, max_val) (x)
279#define PARAM_UNSCALE_Q16(q16_val, min_val, max_val) (q16_val)
280#endif // ENABLE_EVOLVE_OPTIMIZATION
281
282#endif // __EVOLVE_OPTIMIZER_H_
283
284/** EVOLVE_OPTIMIZER
285 * @}
286 */
#define EVOLVE_MAX_CANDIDATES
Максимальное количество кандидатов для обучения
#define EVOLVE_MAX_PARAMS
Максимальное количество параметров
__STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t *opt, int32_t *params, int32_t loss)
Один шаг эволюционного оптимизатора.
#define local_time()
Локальное время
__STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t *opt, uint16_t n_params, uint16_t n_cand, uint16_t n_best, uint16_t iq_mutation, int32_t *start_params)
Инициализация эволюционного оптимизатора.
Заголочный файл для дефайнов библиотеки MyLibsGeneral.
Структура эволюционного оптимизатора
uint16_t cand_index
Индекс кандидата для обработки
uint16_t n_params
Количество параметров
uint16_t iq_mutation
Амплитуда мутации в Q16.16.
int32_t candidates[100][20]
Параметры кандидатов
uint16_t sorted_idx[100]
Индексы отсортированных кандидатов
uint16_t n_best
Количество лучших, усредняемых
uint16_t n_cand
Количество кандидатов
int32_t loss[100]
Loss для каждого кандидата