style: run clang-format and configure pre-commit hooks
This commit is contained in:
51
.pre-commit-config.yaml
Normal file
51
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Pre-commit configuration for FCES-native
|
||||||
|
repos:
|
||||||
|
# 1. Standard hooks for general file cleanliness
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.6.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
|
||||||
|
# 2. C++ Formatting using clang-format (fetched dynamically)
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
|
rev: v18.1.5
|
||||||
|
hooks:
|
||||||
|
- id: clang-format
|
||||||
|
types_or: [c++, c]
|
||||||
|
|
||||||
|
# 3. C++ Static Analysis using local cppcheck
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: cppcheck
|
||||||
|
name: cppcheck
|
||||||
|
entry: cppcheck
|
||||||
|
language: system
|
||||||
|
types_or: [c++, c]
|
||||||
|
args: [
|
||||||
|
"--enable=warning,portability",
|
||||||
|
"--suppress=missingIncludeSystem",
|
||||||
|
"--suppress=unusedFunction",
|
||||||
|
"--suppress=normalCheckLevelMaxBranches",
|
||||||
|
"--inline-suppr",
|
||||||
|
"--error-exitcode=1",
|
||||||
|
"-Iinclude"
|
||||||
|
]
|
||||||
|
|
||||||
|
# 4. Python Linter and Formatter (ruff)
|
||||||
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.4.4
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
args: [ --fix ]
|
||||||
|
- id: ruff-format
|
||||||
|
|
||||||
|
# 5. Python Type Checking (mypy)
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v1.10.0
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
args: [ "--ignore-missing-imports", "--strict" ]
|
||||||
|
additional_dependencies: [ "types-requests", "pydantic" ]
|
||||||
@@ -1,45 +1,47 @@
|
|||||||
#include <benchmark/benchmark.h>
|
|
||||||
#include "fces/population.hpp"
|
|
||||||
#include "fces/controller.hpp"
|
#include "fces/controller.hpp"
|
||||||
|
#include "fces/population.hpp"
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
static void BM_ControllerDecideUpdate(benchmark::State& state) {
|
static void BM_ControllerDecideUpdate(benchmark::State &state) {
|
||||||
FuzzyController ctrl;
|
FuzzyController ctrl;
|
||||||
std::vector<std::vector<float>> stats(state.range(0), {0.1f, 0.2f, 0.3f, 0.4f, 0.5f});
|
std::vector<std::vector<float>> stats(state.range(0),
|
||||||
|
{0.1f, 0.2f, 0.3f, 0.4f, 0.5f});
|
||||||
|
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
auto actions = ctrl.decide_update(stats, 0.0f, 0.5f, 0.0f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f);
|
auto actions = ctrl.decide_update(stats, 0.0f, 0.5f, 0.0f, 0.1f, 0.0f, 0.0f,
|
||||||
benchmark::DoNotOptimize(actions);
|
1.0f, 0.0f);
|
||||||
}
|
benchmark::DoNotOptimize(actions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_ControllerDecideUpdate)->Arg(10)->Arg(50)->Arg(200);
|
BENCHMARK(BM_ControllerDecideUpdate)->Arg(10)->Arg(50)->Arg(200);
|
||||||
|
|
||||||
static void BM_Evolve(benchmark::State& state) {
|
static void BM_Evolve(benchmark::State &state) {
|
||||||
Population pop(state.range(0));
|
Population pop(state.range(0));
|
||||||
|
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
pop.evolve(2.0f, -0.01f, 0.5f);
|
pop.evolve(2.0f, -0.01f, 0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Evolve)->Arg(50)->Arg(100)->Arg(200);
|
BENCHMARK(BM_Evolve)->Arg(50)->Arg(100)->Arg(200);
|
||||||
|
|
||||||
static void BM_Mutation(benchmark::State& state) {
|
static void BM_Mutation(benchmark::State &state) {
|
||||||
FuzzyController ctrl;
|
FuzzyController ctrl;
|
||||||
|
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
auto child = ctrl.mutate(2.0f, 1.0f);
|
auto child = ctrl.mutate(2.0f, 1.0f);
|
||||||
benchmark::DoNotOptimize(child);
|
benchmark::DoNotOptimize(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Mutation);
|
BENCHMARK(BM_Mutation);
|
||||||
|
|
||||||
static void BM_Crossover(benchmark::State& state) {
|
static void BM_Crossover(benchmark::State &state) {
|
||||||
FuzzyController a, b;
|
FuzzyController a, b;
|
||||||
|
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
auto child = a.crossover(b);
|
auto child = a.crossover(b);
|
||||||
benchmark::DoNotOptimize(child);
|
benchmark::DoNotOptimize(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_Crossover);
|
BENCHMARK(BM_Crossover);
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
|
#include "fces/optimizer.hpp"
|
||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
#include <torch/torch.h>
|
#include <torch/torch.h>
|
||||||
#include "fces/optimizer.hpp"
|
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
static void BM_OptimizerStep(benchmark::State& state) {
|
static void BM_OptimizerStep(benchmark::State &state) {
|
||||||
auto model = torch::nn::Linear(state.range(0), state.range(0) / 2);
|
auto model = torch::nn::Linear(state.range(0), state.range(0) / 2);
|
||||||
std::vector<torch::Tensor> params;
|
std::vector<torch::Tensor> params;
|
||||||
for (auto& p : model->parameters()) params.push_back(p);
|
for (auto &p : model->parameters())
|
||||||
|
params.push_back(p);
|
||||||
|
|
||||||
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
||||||
|
|
||||||
auto x = torch::randn({8, state.range(0)});
|
auto x = torch::randn({8, state.range(0)});
|
||||||
|
|
||||||
for (auto _ : state) {
|
for (auto _ : state) {
|
||||||
auto y = model->forward(x);
|
auto y = model->forward(x);
|
||||||
auto loss = y.sum();
|
auto loss = y.sum();
|
||||||
loss.backward();
|
loss.backward();
|
||||||
opt.step();
|
opt.step();
|
||||||
opt.zero_grad();
|
opt.zero_grad();
|
||||||
benchmark::DoNotOptimize(loss);
|
benchmark::DoNotOptimize(loss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(BM_OptimizerStep)->Arg(64)->Arg(256)->Arg(1024);
|
BENCHMARK(BM_OptimizerStep)->Arg(64)->Arg(256)->Arg(1024);
|
||||||
|
|||||||
@@ -3,59 +3,57 @@
|
|||||||
* @brief Example: train a small neural network with FCES via libtorch.
|
* @brief Example: train a small neural network with FCES via libtorch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "fces/optimizer.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <torch/torch.h>
|
#include <torch/torch.h>
|
||||||
#include "fces/optimizer.hpp"
|
|
||||||
|
|
||||||
struct TinyNet : torch::nn::Module {
|
struct TinyNet : torch::nn::Module {
|
||||||
torch::nn::Linear fc1{nullptr}, fc2{nullptr};
|
torch::nn::Linear fc1{nullptr}, fc2{nullptr};
|
||||||
|
|
||||||
TinyNet() {
|
TinyNet() {
|
||||||
fc1 = register_module("fc1", torch::nn::Linear(10, 32));
|
fc1 = register_module("fc1", torch::nn::Linear(10, 32));
|
||||||
fc2 = register_module("fc2", torch::nn::Linear(32, 1));
|
fc2 = register_module("fc2", torch::nn::Linear(32, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
torch::Tensor forward(torch::Tensor x) {
|
torch::Tensor forward(torch::Tensor x) {
|
||||||
x = torch::relu(fc1->forward(x));
|
x = torch::relu(fc1->forward(x));
|
||||||
return fc2->forward(x);
|
return fc2->forward(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
auto model = std::make_shared<TinyNet>();
|
auto model = std::make_shared<TinyNet>();
|
||||||
|
|
||||||
std::vector<torch::Tensor> params;
|
std::vector<torch::Tensor> params;
|
||||||
for (auto& p : model->parameters()) params.push_back(p);
|
for (auto &p : model->parameters())
|
||||||
|
params.push_back(p);
|
||||||
|
|
||||||
fces::FCESOptimizer optimizer(
|
fces::FCESOptimizer optimizer(params, fces::FCESConfig{}
|
||||||
params,
|
.set_lr(1.6e-3f)
|
||||||
fces::FCESConfig{}
|
.set_population_size(200)
|
||||||
.set_lr(1.6e-3f)
|
.set_total_steps(1000));
|
||||||
.set_population_size(200)
|
|
||||||
.set_total_steps(1000)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generate synthetic regression data
|
// Generate synthetic regression data
|
||||||
auto x_train = torch::randn({100, 10});
|
auto x_train = torch::randn({100, 10});
|
||||||
auto y_train = torch::sin(x_train.sum(1, true));
|
auto y_train = torch::sin(x_train.sum(1, true));
|
||||||
|
|
||||||
for (int epoch = 0; epoch < 100; ++epoch) {
|
for (int epoch = 0; epoch < 100; ++epoch) {
|
||||||
optimizer.zero_grad();
|
optimizer.zero_grad();
|
||||||
auto pred = model->forward(x_train);
|
auto pred = model->forward(x_train);
|
||||||
auto loss = torch::mse_loss(pred, y_train);
|
auto loss = torch::mse_loss(pred, y_train);
|
||||||
loss.backward();
|
loss.backward();
|
||||||
optimizer.step();
|
optimizer.step();
|
||||||
optimizer.update_fitness(loss.item<float>());
|
optimizer.update_fitness(loss.item<float>());
|
||||||
|
|
||||||
if (epoch % 10 == 0) {
|
if (epoch % 10 == 0) {
|
||||||
std::cout << "Epoch " << epoch
|
std::cout << "Epoch " << epoch << " | Loss: " << loss.item<float>()
|
||||||
<< " | Loss: " << loss.item<float>() << std::endl;
|
<< std::endl;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "\nTraining complete. Final loss: "
|
std::cout << "\nTraining complete. Final loss: "
|
||||||
<< torch::mse_loss(model->forward(x_train), y_train).item<float>()
|
<< torch::mse_loss(model->forward(x_train), y_train).item<float>()
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,34 +3,33 @@
|
|||||||
* @brief Minimal example: optimize a quadratic function with FCES.
|
* @brief Minimal example: optimize a quadratic function with FCES.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "fces/optimizer.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <torch/torch.h>
|
#include <torch/torch.h>
|
||||||
#include "fces/optimizer.hpp"
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Target: minimize f(x) = ||x - target||^2
|
// Target: minimize f(x) = ||x - target||^2
|
||||||
auto target = torch::tensor({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});
|
auto target = torch::tensor({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});
|
||||||
auto x = torch::randn({5}, torch::requires_grad());
|
auto x = torch::randn({5}, torch::requires_grad());
|
||||||
|
|
||||||
std::vector<torch::Tensor> params = {x};
|
std::vector<torch::Tensor> params = {x};
|
||||||
fces::FCESOptimizer optimizer(params, fces::FCESConfig{}.set_lr(1e-2f));
|
fces::FCESOptimizer optimizer(params, fces::FCESConfig{}.set_lr(1e-2f));
|
||||||
|
|
||||||
for (int step = 0; step < 500; ++step) {
|
for (int step = 0; step < 500; ++step) {
|
||||||
optimizer.zero_grad();
|
optimizer.zero_grad();
|
||||||
auto loss = (x - target).pow(2).sum();
|
auto loss = (x - target).pow(2).sum();
|
||||||
loss.backward();
|
loss.backward();
|
||||||
optimizer.step();
|
optimizer.step();
|
||||||
optimizer.update_fitness(loss.item<float>());
|
optimizer.update_fitness(loss.item<float>());
|
||||||
|
|
||||||
if (step % 50 == 0) {
|
if (step % 50 == 0) {
|
||||||
std::cout << "Step " << step
|
std::cout << "Step " << step << " | Loss: " << loss.item<float>()
|
||||||
<< " | Loss: " << loss.item<float>()
|
<< " | x: " << x << std::endl;
|
||||||
<< " | x: " << x << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "\nFinal x: " << x << std::endl;
|
std::cout << "\nFinal x: " << x << std::endl;
|
||||||
std::cout << "Target: " << target << std::endl;
|
std::cout << "Target: " << target << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,112 +3,124 @@
|
|||||||
* @brief Example showcasing telemetry instrumentation and model inference.
|
* @brief Example showcasing telemetry instrumentation and model inference.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include "fces/optimizer.hpp"
|
#include "fces/optimizer.hpp"
|
||||||
#include "fces/telemetry.hpp"
|
#include "fces/telemetry.hpp"
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <torch/torch.h>
|
||||||
|
|
||||||
// Define a simple neural network for nonlinear regression: y = x^2
|
// Define a simple neural network for nonlinear regression: y = x^2
|
||||||
struct RegressionNet : torch::nn::Module {
|
struct RegressionNet : torch::nn::Module {
|
||||||
torch::nn::Linear fc1{nullptr}, fc2{nullptr};
|
torch::nn::Linear fc1{nullptr}, fc2{nullptr};
|
||||||
|
|
||||||
RegressionNet() {
|
RegressionNet() {
|
||||||
fc1 = register_module("fc1", torch::nn::Linear(1, 16));
|
fc1 = register_module("fc1", torch::nn::Linear(1, 16));
|
||||||
fc2 = register_module("fc2", torch::nn::Linear(16, 1));
|
fc2 = register_module("fc2", torch::nn::Linear(16, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
torch::Tensor forward(torch::Tensor x) {
|
torch::Tensor forward(torch::Tensor x) {
|
||||||
x = torch::tanh(fc1->forward(x));
|
x = torch::tanh(fc1->forward(x));
|
||||||
return fc2->forward(x);
|
return fc2->forward(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
fces::Telemetry::get().info("app_start", "Telemetry and Inference demo initialized.");
|
fces::Telemetry::get().info("app_start",
|
||||||
|
"Telemetry and Inference demo initialized.");
|
||||||
|
|
||||||
// 1. Create Model and Data
|
// 1. Create Model and Data
|
||||||
auto model = std::make_shared<RegressionNet>();
|
auto model = std::make_shared<RegressionNet>();
|
||||||
|
|
||||||
// Generate training data: x in [-2, 2], y = x^2 + noise
|
|
||||||
auto x_train = torch::linspace(-2.0, 2.0, 100).unsqueeze(1);
|
|
||||||
auto y_train = x_train.pow(2) + 0.1 * torch::randn({100, 1});
|
|
||||||
|
|
||||||
// 2. Configure Optimizer
|
// Generate training data: x in [-2, 2], y = x^2 + noise
|
||||||
std::vector<torch::Tensor> params;
|
auto x_train = torch::linspace(-2.0, 2.0, 100).unsqueeze(1);
|
||||||
for (auto& p : model->parameters()) {
|
auto y_train = x_train.pow(2) + 0.1 * torch::randn({100, 1});
|
||||||
params.push_back(p);
|
|
||||||
|
// 2. Configure Optimizer
|
||||||
|
std::vector<torch::Tensor> params;
|
||||||
|
for (auto &p : model->parameters()) {
|
||||||
|
params.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
fces::FCESOptimizer optimizer(
|
||||||
|
params,
|
||||||
|
fces::FCESConfig{}.set_lr(2e-3f).set_population_size(150).set_total_steps(
|
||||||
|
100));
|
||||||
|
|
||||||
|
fces::Telemetry::get().info("training_start",
|
||||||
|
"Beginning neural net optimization with FCES.");
|
||||||
|
|
||||||
|
auto start_train = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
// 3. Optimization Loop
|
||||||
|
for (int epoch = 0; epoch <= 100; ++epoch) {
|
||||||
|
optimizer.zero_grad();
|
||||||
|
auto pred = model->forward(x_train);
|
||||||
|
auto loss = torch::mse_loss(pred, y_train);
|
||||||
|
loss.backward();
|
||||||
|
optimizer.step();
|
||||||
|
optimizer.update_fitness(loss.item<float>());
|
||||||
|
|
||||||
|
if (epoch % 20 == 0) {
|
||||||
|
fces::Telemetry::get().info(
|
||||||
|
"epoch_checkpoint", "Epoch " + std::to_string(epoch) + " | Loss: " +
|
||||||
|
std::to_string(loss.item<float>()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fces::FCESOptimizer optimizer(
|
auto end_train = std::chrono::high_resolution_clock::now();
|
||||||
params,
|
double train_duration =
|
||||||
fces::FCESConfig{}
|
std::chrono::duration<double, std::milli>(end_train - start_train)
|
||||||
.set_lr(2e-3f)
|
.count();
|
||||||
.set_population_size(150)
|
|
||||||
.set_total_steps(100)
|
|
||||||
);
|
|
||||||
|
|
||||||
fces::Telemetry::get().info("training_start", "Beginning neural net optimization with FCES.");
|
fces::Telemetry::get().info("training_complete",
|
||||||
|
"Duration: " + std::to_string(train_duration) +
|
||||||
|
" ms");
|
||||||
|
|
||||||
auto start_train = std::chrono::high_resolution_clock::now();
|
// 4. Inference Phase
|
||||||
|
fces::Telemetry::get().info("inference_phase_start",
|
||||||
|
"Evaluating model on new test inputs.");
|
||||||
|
|
||||||
// 3. Optimization Loop
|
// Generate test inputs
|
||||||
for (int epoch = 0; epoch <= 100; ++epoch) {
|
auto x_test = torch::tensor({-1.5f, -0.5f, 0.0f, 0.5f, 1.5f}).unsqueeze(1);
|
||||||
optimizer.zero_grad();
|
auto y_expected = x_test.pow(2);
|
||||||
auto pred = model->forward(x_train);
|
|
||||||
auto loss = torch::mse_loss(pred, y_train);
|
|
||||||
loss.backward();
|
|
||||||
optimizer.step();
|
|
||||||
optimizer.update_fitness(loss.item<float>());
|
|
||||||
|
|
||||||
if (epoch % 20 == 0) {
|
// Switch model to evaluation mode
|
||||||
fces::Telemetry::get().info("epoch_checkpoint",
|
model->eval();
|
||||||
"Epoch " + std::to_string(epoch) + " | Loss: " + std::to_string(loss.item<float>()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end_train = std::chrono::high_resolution_clock::now();
|
// Run inference and measure latency
|
||||||
double train_duration = std::chrono::duration<double, std::milli>(end_train - start_train).count();
|
auto start_inf = std::chrono::high_resolution_clock::now();
|
||||||
|
torch::Tensor y_pred;
|
||||||
|
{
|
||||||
|
torch::NoGradGuard no_grad;
|
||||||
|
y_pred = model->forward(x_test);
|
||||||
|
}
|
||||||
|
auto end_inf = std::chrono::high_resolution_clock::now();
|
||||||
|
double inf_duration =
|
||||||
|
std::chrono::duration<double, std::milli>(end_inf - start_inf).count();
|
||||||
|
|
||||||
fces::Telemetry::get().info("training_complete",
|
// Log telemetry for inference performance
|
||||||
"Duration: " + std::to_string(train_duration) + " ms");
|
fces::Telemetry::get().info(
|
||||||
|
"inference_perf", "Inputs: " + std::to_string(x_test.size(0)) +
|
||||||
|
" | Latency: " + std::to_string(inf_duration) +
|
||||||
|
" ms");
|
||||||
|
|
||||||
// 4. Inference Phase
|
// Print predictions and expected values side-by-side
|
||||||
fces::Telemetry::get().info("inference_phase_start", "Evaluating model on new test inputs.");
|
std::cout << "\n================ INFERENCE RESULTS ================"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "Input (x) | Predicted (y_pred) | Expected (y_expected)"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "----------------------------------------------------"
|
||||||
|
<< std::endl;
|
||||||
|
for (int i = 0; i < x_test.size(0); ++i) {
|
||||||
|
float x_val = x_test[i][0].item<float>();
|
||||||
|
float pred_val = y_pred[i][0].item<float>();
|
||||||
|
float exp_val = y_expected[i][0].item<float>();
|
||||||
|
std::printf(" %7.2f | %7.4f | %7.4f\n", x_val,
|
||||||
|
pred_val, exp_val);
|
||||||
|
}
|
||||||
|
std::cout << "====================================================\n"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
// Generate test inputs
|
fces::Telemetry::get().info("app_finish", "Exiting demo successfully.");
|
||||||
auto x_test = torch::tensor({-1.5f, -0.5f, 0.0f, 0.5f, 1.5f}).unsqueeze(1);
|
return 0;
|
||||||
auto y_expected = x_test.pow(2);
|
|
||||||
|
|
||||||
// Switch model to evaluation mode
|
|
||||||
model->eval();
|
|
||||||
|
|
||||||
// Run inference and measure latency
|
|
||||||
auto start_inf = std::chrono::high_resolution_clock::now();
|
|
||||||
torch::Tensor y_pred;
|
|
||||||
{
|
|
||||||
torch::NoGradGuard no_grad;
|
|
||||||
y_pred = model->forward(x_test);
|
|
||||||
}
|
|
||||||
auto end_inf = std::chrono::high_resolution_clock::now();
|
|
||||||
double inf_duration = std::chrono::duration<double, std::milli>(end_inf - start_inf).count();
|
|
||||||
|
|
||||||
// Log telemetry for inference performance
|
|
||||||
fces::Telemetry::get().info("inference_perf",
|
|
||||||
"Inputs: " + std::to_string(x_test.size(0)) + " | Latency: " + std::to_string(inf_duration) + " ms");
|
|
||||||
|
|
||||||
// Print predictions and expected values side-by-side
|
|
||||||
std::cout << "\n================ INFERENCE RESULTS ================" << std::endl;
|
|
||||||
std::cout << "Input (x) | Predicted (y_pred) | Expected (y_expected)" << std::endl;
|
|
||||||
std::cout << "----------------------------------------------------" << std::endl;
|
|
||||||
for (int i = 0; i < x_test.size(0); ++i) {
|
|
||||||
float x_val = x_test[i][0].item<float>();
|
|
||||||
float pred_val = y_pred[i][0].item<float>();
|
|
||||||
float exp_val = y_expected[i][0].item<float>();
|
|
||||||
std::printf(" %7.2f | %7.4f | %7.4f\n", x_val, pred_val, exp_val);
|
|
||||||
}
|
|
||||||
std::cout << "====================================================\n" << std::endl;
|
|
||||||
|
|
||||||
fces::Telemetry::get().info("app_finish", "Exiting demo successfully.");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,65 +18,80 @@ namespace fces {
|
|||||||
* All fields have sensible defaults matching the Python V49.0 implementation.
|
* All fields have sensible defaults matching the Python V49.0 implementation.
|
||||||
*/
|
*/
|
||||||
struct FCESConfig {
|
struct FCESConfig {
|
||||||
// Learning rate (V49 optimal default)
|
// Learning rate (V49 optimal default)
|
||||||
float lr = 1.6e-3f;
|
float lr = 1.6e-3f;
|
||||||
|
|
||||||
// Weight decay coefficient
|
// Weight decay coefficient
|
||||||
float weight_decay = 0.0f;
|
float weight_decay = 0.0f;
|
||||||
|
|
||||||
// Population size for evolutionary search
|
// Population size for evolutionary search
|
||||||
int population_size = 200;
|
int population_size = 200;
|
||||||
|
|
||||||
// Total training steps (for progress-aware scheduling)
|
// Total training steps (for progress-aware scheduling)
|
||||||
int total_steps = 5000;
|
int total_steps = 5000;
|
||||||
|
|
||||||
// Signal mode for loss velocity calculation
|
// Signal mode for loss velocity calculation
|
||||||
std::string signal_mode = "relative";
|
std::string signal_mode = "relative";
|
||||||
|
|
||||||
// Grokking awareness coefficient (0.0 = disabled)
|
// Grokking awareness coefficient (0.0 = disabled)
|
||||||
float grokking_coefficient = 0.1f;
|
float grokking_coefficient = 0.1f;
|
||||||
|
|
||||||
// Spectral sensing frequency (every N steps)
|
// Spectral sensing frequency (every N steps)
|
||||||
int spectral_frequency = 10;
|
int spectral_frequency = 10;
|
||||||
|
|
||||||
// Curriculum Spectral Regularization
|
// Curriculum Spectral Regularization
|
||||||
bool csr_enabled = false;
|
bool csr_enabled = false;
|
||||||
int csr_warmup_steps = 500;
|
int csr_warmup_steps = 500;
|
||||||
int csr_ramp_steps = 1000;
|
int csr_ramp_steps = 1000;
|
||||||
|
|
||||||
// Trust region clipping
|
// Trust region clipping
|
||||||
float trust_region_clip = 0.01f;
|
float trust_region_clip = 0.01f;
|
||||||
|
|
||||||
// Rollback threshold
|
// Rollback threshold
|
||||||
float rollback_threshold = 1.5f;
|
float rollback_threshold = 1.5f;
|
||||||
|
|
||||||
// Adaptive weight decay
|
// Adaptive weight decay
|
||||||
bool adaptive_wd = false;
|
bool adaptive_wd = false;
|
||||||
|
|
||||||
// Parasitic mode (gradient alignment reward)
|
// Parasitic mode (gradient alignment reward)
|
||||||
bool parasitic_mode = false;
|
bool parasitic_mode = false;
|
||||||
|
|
||||||
// Ablation mode: "", "force_sign", "force_grad"
|
// Ablation mode: "", "force_sign", "force_grad"
|
||||||
std::string ablation_mode = "";
|
std::string ablation_mode = "";
|
||||||
|
|
||||||
// Fractional factorial scoring (CRO trick)
|
// Fractional factorial scoring (CRO trick)
|
||||||
bool use_fractional_scoring = false;
|
bool use_fractional_scoring = false;
|
||||||
|
|
||||||
// Direct construction mode (pop_size=1)
|
// Direct construction mode (pop_size=1)
|
||||||
bool direct_construction = false;
|
bool direct_construction = false;
|
||||||
|
|
||||||
// Banach-Tarski fission
|
// Banach-Tarski fission
|
||||||
bool use_banach_fission = false;
|
bool use_banach_fission = false;
|
||||||
|
|
||||||
// Auto-population (stabilize on divergence)
|
// Auto-population (stabilize on divergence)
|
||||||
bool auto_population = false;
|
bool auto_population = false;
|
||||||
|
|
||||||
// Builder pattern
|
// Builder pattern
|
||||||
FCESConfig& set_lr(float v) { lr = v; return *this; }
|
FCESConfig &set_lr(float v) {
|
||||||
FCESConfig& set_population_size(int v) { population_size = v; return *this; }
|
lr = v;
|
||||||
FCESConfig& set_total_steps(int v) { total_steps = v; return *this; }
|
return *this;
|
||||||
FCESConfig& set_grokking_coefficient(float v) { grokking_coefficient = v; return *this; }
|
}
|
||||||
FCESConfig& set_direct_construction(bool v) { direct_construction = v; return *this; }
|
FCESConfig &set_population_size(int v) {
|
||||||
|
population_size = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FCESConfig &set_total_steps(int v) {
|
||||||
|
total_steps = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FCESConfig &set_grokking_coefficient(float v) {
|
||||||
|
grokking_coefficient = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FCESConfig &set_direct_construction(bool v) {
|
||||||
|
direct_construction = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -27,28 +27,29 @@ constexpr int GENOME_INPUT_DIM = 14;
|
|||||||
constexpr int GENOME_HIDDEN_DIM = 8;
|
constexpr int GENOME_HIDDEN_DIM = 8;
|
||||||
// Controller output dimension: [multiplier, sign_gate, wd_mult]
|
// Controller output dimension: [multiplier, sign_gate, wd_mult]
|
||||||
constexpr int GENOME_OUTPUT_DIM = 3;
|
constexpr int GENOME_OUTPUT_DIM = 3;
|
||||||
// Total genome size: input->hidden weights + hidden biases + hidden->output weights + output biases
|
// Total genome size: input->hidden weights + hidden biases + hidden->output
|
||||||
|
// weights + output biases
|
||||||
constexpr int GENOME_SIZE =
|
constexpr int GENOME_SIZE =
|
||||||
(GENOME_INPUT_DIM * GENOME_HIDDEN_DIM) + // input -> hidden weights
|
(GENOME_INPUT_DIM * GENOME_HIDDEN_DIM) + // input -> hidden weights
|
||||||
GENOME_HIDDEN_DIM + // hidden biases
|
GENOME_HIDDEN_DIM + // hidden biases
|
||||||
(GENOME_HIDDEN_DIM * GENOME_OUTPUT_DIM) + // hidden -> output weights
|
(GENOME_HIDDEN_DIM * GENOME_OUTPUT_DIM) + // hidden -> output weights
|
||||||
GENOME_OUTPUT_DIM; // output biases
|
GENOME_OUTPUT_DIM; // output biases
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Genome — the "DNA" of a fuzzy controller.
|
* Genome — the "DNA" of a fuzzy controller.
|
||||||
* A flat array of floats encoding a micro-MLP.
|
* A flat array of floats encoding a micro-MLP.
|
||||||
*/
|
*/
|
||||||
struct Genome {
|
struct Genome {
|
||||||
std::array<float, GENOME_SIZE> weights{};
|
std::array<float, GENOME_SIZE> weights{};
|
||||||
std::array<float, GENOME_SIZE> gene_success{};
|
std::array<float, GENOME_SIZE> gene_success{};
|
||||||
float sigma_gene = 0.1f;
|
float sigma_gene = 0.1f;
|
||||||
float plasticity = 1.0f;
|
float plasticity = 1.0f;
|
||||||
|
|
||||||
/// Initialize with random weights from a normal distribution
|
/// Initialize with random weights from a normal distribution
|
||||||
void randomize(std::mt19937& rng);
|
void randomize(std::mt19937 &rng);
|
||||||
|
|
||||||
/// Deep copy
|
/// Deep copy
|
||||||
Genome clone() const;
|
Genome clone() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,84 +63,81 @@ struct Genome {
|
|||||||
*/
|
*/
|
||||||
class FuzzyController {
|
class FuzzyController {
|
||||||
public:
|
public:
|
||||||
/// Unique identifier
|
/// Unique identifier
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
|
|
||||||
/// The neural genome
|
/// The neural genome
|
||||||
Genome genome;
|
Genome genome;
|
||||||
|
|
||||||
/// Fitness scores
|
/// Fitness scores
|
||||||
float fitness = 0.0f;
|
float fitness = 0.0f;
|
||||||
float lifetime_fitness = 0.0f;
|
float lifetime_fitness = 0.0f;
|
||||||
float ema_fitness = 0.0f;
|
float ema_fitness = 0.0f;
|
||||||
int evaluation_count = 0;
|
int evaluation_count = 0;
|
||||||
int age = 0;
|
int age = 0;
|
||||||
|
|
||||||
/// Origin tracking
|
/// Origin tracking
|
||||||
std::string origin = "random";
|
std::string origin = "random";
|
||||||
|
|
||||||
/// Trust region violation counter
|
/// Trust region violation counter
|
||||||
int trust_violations = 0;
|
int trust_violations = 0;
|
||||||
|
|
||||||
/// Rolling fitness history (for Phase 23 strategies)
|
/// Rolling fitness history (for Phase 23 strategies)
|
||||||
std::vector<float> fitness_history;
|
std::vector<float> fitness_history;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Construction
|
// Construction
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
FuzzyController();
|
FuzzyController();
|
||||||
explicit FuzzyController(Genome genome);
|
explicit FuzzyController(Genome genome);
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Core Operations
|
// Core Operations
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forward pass through the micro-MLP to produce update decisions.
|
* Forward pass through the micro-MLP to produce update decisions.
|
||||||
*
|
*
|
||||||
* @param layer_stats Vector of per-layer feature maps
|
* @param layer_stats Vector of per-layer feature maps
|
||||||
* @param loss_trend Current loss velocity
|
* @param loss_trend Current loss velocity
|
||||||
* @param step_pct Training progress [0, 1]
|
* @param step_pct Training progress [0, 1]
|
||||||
* @param rollback_rate Rolling average rollback frequency
|
* @param rollback_rate Rolling average rollback frequency
|
||||||
* @param grad_stability Gradient coefficient of variation
|
* @param grad_stability Gradient coefficient of variation
|
||||||
* @param spectral_alpha Log spectral rank
|
* @param spectral_alpha Log spectral rank
|
||||||
* @param stagnation_intensity Stagnation counter / 500
|
* @param stagnation_intensity Stagnation counter / 500
|
||||||
* @param kzm_damping Kibble-Zurek damping factor
|
* @param kzm_damping Kibble-Zurek damping factor
|
||||||
* @param projected_drift Projected loss drift
|
* @param projected_drift Projected loss drift
|
||||||
* @return Tensor of shape [num_groups, 3] — (mult, sign_gate, wd_mult)
|
* @return Tensor of shape [num_groups, 3] — (mult, sign_gate, wd_mult)
|
||||||
*/
|
*/
|
||||||
torch::Tensor decide_update(
|
torch::Tensor
|
||||||
const std::vector<std::vector<float>>& layer_stats,
|
decide_update(const std::vector<std::vector<float>> &layer_stats,
|
||||||
float loss_trend,
|
float loss_trend, float step_pct, float rollback_rate,
|
||||||
float step_pct,
|
float grad_stability, float spectral_alpha,
|
||||||
float rollback_rate,
|
float stagnation_intensity, float kzm_damping,
|
||||||
float grad_stability,
|
float projected_drift);
|
||||||
float spectral_alpha,
|
|
||||||
float stagnation_intensity,
|
|
||||||
float kzm_damping,
|
|
||||||
float projected_drift
|
|
||||||
);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Evolutionary Operators
|
// Evolutionary Operators
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
/// Create a mutated child
|
/// Create a mutated child
|
||||||
FuzzyController mutate(float current_loss, float sigma_scale = 1.0f) const;
|
FuzzyController mutate(float current_loss, float sigma_scale = 1.0f) const;
|
||||||
|
|
||||||
/// Crossover with another controller
|
/// Crossover with another controller
|
||||||
FuzzyController crossover(const FuzzyController& partner, bool use_alignment = true) const;
|
FuzzyController crossover(const FuzzyController &partner,
|
||||||
|
bool use_alignment = true) const;
|
||||||
|
|
||||||
/// Create an orthogonal counter-strategy (Phoenix Rebirth)
|
/// Create an orthogonal counter-strategy (Phoenix Rebirth)
|
||||||
FuzzyController create_orthogonal_child(float intensity = 1.0f) const;
|
FuzzyController create_orthogonal_child(float intensity = 1.0f) const;
|
||||||
|
|
||||||
/// Banach-Tarski fission: split into two complementary children
|
/// Banach-Tarski fission: split into two complementary children
|
||||||
std::pair<FuzzyController, FuzzyController> banach_tarski_fission(float intensity = 1.0f) const;
|
std::pair<FuzzyController, FuzzyController>
|
||||||
|
banach_tarski_fission(float intensity = 1.0f) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::atomic<uint64_t> next_id_;
|
static std::atomic<uint64_t> next_id_;
|
||||||
static thread_local std::mt19937 rng_;
|
static thread_local std::mt19937 rng_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -21,34 +21,27 @@ namespace fces {
|
|||||||
*/
|
*/
|
||||||
class EvolutionManager {
|
class EvolutionManager {
|
||||||
public:
|
public:
|
||||||
explicit EvolutionManager(
|
explicit EvolutionManager(Population &population, int selection_interval = 50,
|
||||||
Population& population,
|
bool auto_population = false,
|
||||||
int selection_interval = 50,
|
bool direct_construction = false);
|
||||||
bool auto_population = false,
|
|
||||||
bool direct_construction = false
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Get the currently active controller
|
/// Get the currently active controller
|
||||||
FuzzyController& get_active_controller();
|
FuzzyController &get_active_controller();
|
||||||
|
|
||||||
/// Update population dynamics based on current training state
|
/// Update population dynamics based on current training state
|
||||||
void update_population_dynamics(
|
void update_population_dynamics(float loss_velocity, float ema_loss,
|
||||||
float loss_velocity,
|
int step_counter, int total_steps);
|
||||||
float ema_loss,
|
|
||||||
int step_counter,
|
|
||||||
int total_steps
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Steps the active controller has been in control
|
/// Steps the active controller has been in control
|
||||||
int steps_active = 0;
|
int steps_active = 0;
|
||||||
|
|
||||||
/// Selection interval (how long a controller stays active)
|
/// Selection interval (how long a controller stays active)
|
||||||
int selection_interval;
|
int selection_interval;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Population& population_;
|
Population &population_;
|
||||||
bool auto_population_;
|
bool auto_population_;
|
||||||
bool direct_construction_;
|
bool direct_construction_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @file fitness.hpp
|
* @file fitness.hpp
|
||||||
* @brief Fitness evaluation — loss signal processing and multi-objective evaluation.
|
* @brief Fitness evaluation — loss signal processing and multi-objective
|
||||||
|
* evaluation.
|
||||||
*
|
*
|
||||||
* Port of: packages/fces/core/fitness_engine.py + fitness.py
|
* Port of: packages/fces/core/fitness_engine.py + fitness.py
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
@@ -19,18 +20,18 @@ namespace fces {
|
|||||||
*/
|
*/
|
||||||
class RunningStats {
|
class RunningStats {
|
||||||
public:
|
public:
|
||||||
void update(float value);
|
void update(float value);
|
||||||
float z_score(float value) const;
|
float z_score(float value) const;
|
||||||
float get_mean() const { return mean_; }
|
float get_mean() const { return mean_; }
|
||||||
float get_std() const;
|
float get_std() const;
|
||||||
int get_count() const { return count_; }
|
int get_count() const { return count_; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count_ = 0;
|
int count_ = 0;
|
||||||
float mean_ = 0.0f;
|
float mean_ = 0.0f;
|
||||||
float m2_ = 0.0f;
|
float m2_ = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,26 +39,27 @@ private:
|
|||||||
*/
|
*/
|
||||||
class FitnessEngine {
|
class FitnessEngine {
|
||||||
public:
|
public:
|
||||||
explicit FitnessEngine(float grokking_coefficient = 0.1f);
|
explicit FitnessEngine(float grokking_coefficient = 0.1f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate loss velocity signal.
|
* Calculate loss velocity signal.
|
||||||
*
|
*
|
||||||
* @param current_loss Current step loss
|
* @param current_loss Current step loss
|
||||||
* @param ema_loss Exponential moving average loss
|
* @param ema_loss Exponential moving average loss
|
||||||
* @param mode "relative" or "absolute"
|
* @param mode "relative" or "absolute"
|
||||||
* @return Velocity signal (negative = improving)
|
* @return Velocity signal (negative = improving)
|
||||||
*/
|
*/
|
||||||
float calculate_loss_signal(float current_loss, float ema_loss, const std::string& mode = "relative") const;
|
float calculate_loss_signal(float current_loss, float ema_loss,
|
||||||
|
const std::string &mode = "relative") const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute Kibble-Zurek Mechanism damping factor.
|
* Compute Kibble-Zurek Mechanism damping factor.
|
||||||
* Prevents topological defects during phase transitions.
|
* Prevents topological defects during phase transitions.
|
||||||
*/
|
*/
|
||||||
float compute_kzm_damping(float spectral_alpha) const;
|
float compute_kzm_damping(float spectral_alpha) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float grokking_coefficient_;
|
float grokking_coefficient_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,75 +67,76 @@ private:
|
|||||||
*/
|
*/
|
||||||
class FuzzySet {
|
class FuzzySet {
|
||||||
public:
|
public:
|
||||||
FuzzySet(std::string name, float a, float b, float c, float d) noexcept
|
FuzzySet(std::string name, float a, float b, float c, float d) noexcept
|
||||||
: name_(std::move(name)), a_(a), b_(b), c_(c), d_(d) {}
|
: name_(std::move(name)), a_(a), b_(b), c_(c), d_(d) {}
|
||||||
|
|
||||||
float membership(float x) const noexcept {
|
float membership(float x) const noexcept {
|
||||||
if (!std::isfinite(x)) {
|
if (!std::isfinite(x)) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
if (x <= a_ || x >= d_) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
if (x >= b_ && x <= c_) {
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
if (x > a_ && x < b_) {
|
|
||||||
float range = b_ - a_;
|
|
||||||
return (x - a_) / (range > 0.0f ? range : 1e-9f);
|
|
||||||
}
|
|
||||||
if (x > c_ && x < d_) {
|
|
||||||
float range = d_ - c_;
|
|
||||||
return (d_ - x) / (range > 0.0f ? range : 1e-9f);
|
|
||||||
}
|
|
||||||
return 0.0f;
|
|
||||||
}
|
}
|
||||||
|
if (x <= a_ || x >= d_) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
if (x >= b_ && x <= c_) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
if (x > a_ && x < b_) {
|
||||||
|
float range = b_ - a_;
|
||||||
|
return (x - a_) / (range > 0.0f ? range : 1e-9f);
|
||||||
|
}
|
||||||
|
if (x > c_ && x < d_) {
|
||||||
|
float range = d_ - c_;
|
||||||
|
return (d_ - x) / (range > 0.0f ? range : 1e-9f);
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& name() const noexcept { return name_; }
|
const std::string &name() const noexcept { return name_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
float a_;
|
float a_;
|
||||||
float b_;
|
float b_;
|
||||||
float c_;
|
float c_;
|
||||||
float d_;
|
float d_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fitness metrics for multi-objective evaluation.
|
* Fitness metrics for multi-objective evaluation.
|
||||||
*/
|
*/
|
||||||
struct FitnessMetrics {
|
struct FitnessMetrics {
|
||||||
float training_advantage = 0.0f;
|
float training_advantage = 0.0f;
|
||||||
float validation_advantage = 0.0f;
|
float validation_advantage = 0.0f;
|
||||||
float grad_cv = 0.0f;
|
float grad_cv = 0.0f;
|
||||||
float sparsity_delta = 0.0f;
|
float sparsity_delta = 0.0f;
|
||||||
float consistency_gap = 0.0f;
|
float consistency_gap = 0.0f;
|
||||||
float stable_rank = 0.0f;
|
float stable_rank = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FuzzyFitnessEvaluator — multi-objective fitness evaluation with fuzzy weighting.
|
* FuzzyFitnessEvaluator — multi-objective fitness evaluation with fuzzy
|
||||||
|
* weighting.
|
||||||
*/
|
*/
|
||||||
class FuzzyFitnessEvaluator {
|
class FuzzyFitnessEvaluator {
|
||||||
public:
|
public:
|
||||||
FuzzyFitnessEvaluator() noexcept;
|
FuzzyFitnessEvaluator() noexcept;
|
||||||
|
|
||||||
float evaluate(const FitnessMetrics& metrics) const noexcept;
|
float evaluate(const FitnessMetrics &metrics) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FuzzySet stability_set_;
|
FuzzySet stability_set_;
|
||||||
FuzzySet train_set_;
|
FuzzySet train_set_;
|
||||||
FuzzySet val_set_;
|
FuzzySet val_set_;
|
||||||
FuzzySet sparsity_set_;
|
FuzzySet sparsity_set_;
|
||||||
FuzzySet consistency_set_;
|
FuzzySet consistency_set_;
|
||||||
FuzzySet rank_set_;
|
FuzzySet rank_set_;
|
||||||
|
|
||||||
float w_stability_ = 0.2f;
|
float w_stability_ = 0.2f;
|
||||||
float w_train_ = 0.2f;
|
float w_train_ = 0.2f;
|
||||||
float w_val_ = 0.3f;
|
float w_val_ = 0.3f;
|
||||||
float w_sparsity_ = 0.1f;
|
float w_sparsity_ = 0.1f;
|
||||||
float w_consistency_ = 0.2f;
|
float w_consistency_ = 0.2f;
|
||||||
float w_rank_ = 0.1f;
|
float w_rank_ = 0.1f;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
* @brief FCESOptimizer — the main entry point. libtorch-compatible optimizer.
|
* @brief FCESOptimizer — the main entry point. libtorch-compatible optimizer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "population.hpp"
|
|
||||||
#include "fitness.hpp"
|
|
||||||
#include "evolution.hpp"
|
#include "evolution.hpp"
|
||||||
#include "spectral.hpp"
|
#include "fitness.hpp"
|
||||||
#include "oscillation.hpp"
|
#include "oscillation.hpp"
|
||||||
|
#include "population.hpp"
|
||||||
|
#include "spectral.hpp"
|
||||||
#include "telemetry.hpp"
|
#include "telemetry.hpp"
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
@@ -24,7 +24,8 @@ namespace fces {
|
|||||||
* FCESOptimizer — Fuzzy Controlled Evolutionary Search V49.0 (C++ Port).
|
* FCESOptimizer — Fuzzy Controlled Evolutionary Search V49.0 (C++ Port).
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* auto optimizer = FCESOptimizer(model->parameters(), FCESConfig{}.set_lr(1.6e-3));
|
* auto optimizer = FCESOptimizer(model->parameters(),
|
||||||
|
* FCESConfig{}.set_lr(1.6e-3));
|
||||||
* // In training loop:
|
* // In training loop:
|
||||||
* optimizer.zero_grad();
|
* optimizer.zero_grad();
|
||||||
* auto loss = model->forward(input);
|
* auto loss = model->forward(input);
|
||||||
@@ -32,73 +33,72 @@ namespace fces {
|
|||||||
* optimizer.step();
|
* optimizer.step();
|
||||||
* optimizer.update_fitness(loss.item<float>());
|
* optimizer.update_fitness(loss.item<float>());
|
||||||
*/
|
*/
|
||||||
struct FCESOptimizerOptions : public torch::optim::OptimizerCloneableOptions<FCESOptimizerOptions> {
|
struct FCESOptimizerOptions
|
||||||
explicit FCESOptimizerOptions(double lr = 0.01) : lr_(lr) {}
|
: public torch::optim::OptimizerCloneableOptions<FCESOptimizerOptions> {
|
||||||
|
explicit FCESOptimizerOptions(double lr = 0.01) : lr_(lr) {}
|
||||||
|
|
||||||
double get_lr() const override { return lr_; }
|
double get_lr() const override { return lr_; }
|
||||||
void set_lr(const double lr) override { lr_ = lr; }
|
void set_lr(const double lr) override { lr_ = lr; }
|
||||||
|
|
||||||
double lr_;
|
double lr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FCESOptimizer : public torch::optim::Optimizer {
|
class FCESOptimizer : public torch::optim::Optimizer {
|
||||||
public:
|
public:
|
||||||
explicit FCESOptimizer(
|
explicit FCESOptimizer(std::vector<torch::Tensor> params,
|
||||||
std::vector<torch::Tensor> params,
|
FCESConfig config = FCESConfig{});
|
||||||
FCESConfig config = FCESConfig{}
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Perform a single optimization step
|
/// Perform a single optimization step
|
||||||
torch::Tensor step(LossClosure closure = nullptr) override;
|
torch::Tensor step(LossClosure closure = nullptr) override;
|
||||||
|
|
||||||
/// Update evolutionary fitness with current loss
|
/// Update evolutionary fitness with current loss
|
||||||
void update_fitness(float loss);
|
void update_fitness(float loss);
|
||||||
|
|
||||||
/// Backup model weights to CPU RAM
|
/// Backup model weights to CPU RAM
|
||||||
void backup_to_ram();
|
void backup_to_ram();
|
||||||
|
|
||||||
/// Restore model weights from CPU RAM backup
|
/// Restore model weights from CPU RAM backup
|
||||||
void restore_from_ram();
|
void restore_from_ram();
|
||||||
|
|
||||||
/// Get current step count
|
/// Get current step count
|
||||||
int step_count() const { return step_counter_; }
|
int step_count() const { return step_counter_; }
|
||||||
|
|
||||||
/// Calculate model sparsity
|
/// Calculate model sparsity
|
||||||
float calculate_sparsity() const;
|
float calculate_sparsity() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FCESConfig config_;
|
FCESConfig config_;
|
||||||
Population population_;
|
Population population_;
|
||||||
FitnessEngine fitness_engine_;
|
FitnessEngine fitness_engine_;
|
||||||
FuzzyFitnessEvaluator fitness_evaluator_;
|
FuzzyFitnessEvaluator fitness_evaluator_;
|
||||||
std::unique_ptr<EvolutionManager> evolution_manager_;
|
std::unique_ptr<EvolutionManager> evolution_manager_;
|
||||||
OscillationDetector oscillation_detector_;
|
OscillationDetector oscillation_detector_;
|
||||||
RunningStats grad_norm_tracker_;
|
RunningStats grad_norm_tracker_;
|
||||||
|
|
||||||
// State
|
// State
|
||||||
int step_counter_ = 0;
|
int step_counter_ = 0;
|
||||||
float ema_loss_ = 0.0f;
|
float ema_loss_ = 0.0f;
|
||||||
float last_step_loss_ = 0.0f;
|
float last_step_loss_ = 0.0f;
|
||||||
float best_loss_window_ = std::numeric_limits<float>::infinity();
|
float best_loss_window_ = std::numeric_limits<float>::infinity();
|
||||||
float rollback_ema_ = 0.0f;
|
float rollback_ema_ = 0.0f;
|
||||||
int stagnation_counter_ = 0;
|
int stagnation_counter_ = 0;
|
||||||
float last_loss_velocity_ = 0.0f;
|
float last_loss_velocity_ = 0.0f;
|
||||||
float last_sparsity_ = 0.0f;
|
float last_sparsity_ = 0.0f;
|
||||||
|
|
||||||
// RAM backup
|
// RAM backup
|
||||||
std::vector<torch::Tensor> ram_backup_;
|
std::vector<torch::Tensor> ram_backup_;
|
||||||
|
|
||||||
// Layer stats and group mappings
|
// Layer stats and group mappings
|
||||||
std::vector<std::vector<float>> layer_stats_;
|
std::vector<std::vector<float>> layer_stats_;
|
||||||
std::vector<int> param_group_mapping_;
|
std::vector<int> param_group_mapping_;
|
||||||
std::unique_ptr<SpectralSensor> spectral_sensor_;
|
std::unique_ptr<SpectralSensor> spectral_sensor_;
|
||||||
SpectralController spectral_controller_;
|
SpectralController spectral_controller_;
|
||||||
float last_spectral_rank_ = 0.0f;
|
float last_spectral_rank_ = 0.0f;
|
||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
void gather_stats();
|
void gather_stats();
|
||||||
void apply_parameter_updates(const torch::Tensor& actions);
|
void apply_parameter_updates(const torch::Tensor &actions);
|
||||||
void handle_rollback();
|
void handle_rollback();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -11,21 +11,22 @@ namespace fces {
|
|||||||
|
|
||||||
class OscillationDetector {
|
class OscillationDetector {
|
||||||
public:
|
public:
|
||||||
static constexpr int WINDOW_SIZE = 64;
|
static constexpr int WINDOW_SIZE = 64;
|
||||||
static constexpr float POWER_THRESHOLD = 0.5f;
|
static constexpr float POWER_THRESHOLD = 0.5f;
|
||||||
static constexpr int MIN_PERIOD = 4;
|
static constexpr int MIN_PERIOD = 4;
|
||||||
static constexpr int MAX_PERIOD = 16;
|
static constexpr int MAX_PERIOD = 16;
|
||||||
|
|
||||||
void update(float loss);
|
void update(float loss);
|
||||||
bool detect() const;
|
bool detect() const;
|
||||||
float get_score() const;
|
float get_score() const;
|
||||||
float get_variance_50() const;
|
float get_variance_50() const;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<float> loss_history_;
|
std::vector<float> loss_history_;
|
||||||
static std::vector<float> detrend(const std::vector<float>& signal);
|
static std::vector<float> detrend(const std::vector<float> &signal);
|
||||||
static std::vector<float> compute_power_spectrum(const std::vector<float>& signal);
|
static std::vector<float>
|
||||||
|
compute_power_spectrum(const std::vector<float> &signal);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
* Port of: packages/fces/core/population.py (~1260 LOC)
|
* Port of: packages/fces/core/population.py (~1260 LOC)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "controller.hpp"
|
#include "controller.hpp"
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ namespace fces {
|
|||||||
* Elite selection strategy for stale elite mitigation (Phase 23).
|
* Elite selection strategy for stale elite mitigation (Phase 23).
|
||||||
*/
|
*/
|
||||||
enum class EliteStrategy {
|
enum class EliteStrategy {
|
||||||
Cumulative, // Raw cumulative fitness
|
Cumulative, // Raw cumulative fitness
|
||||||
EMA, // Exponential moving average
|
EMA, // Exponential moving average
|
||||||
Rolling, // Rolling window average
|
Rolling, // Rolling window average
|
||||||
Reset, // Periodic reset every 500 steps
|
Reset, // Periodic reset every 500 steps
|
||||||
AgePenalty // fitness / log(age + 2)
|
AgePenalty // fitness / log(age + 2)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,126 +40,124 @@ enum class EliteStrategy {
|
|||||||
*/
|
*/
|
||||||
class Population {
|
class Population {
|
||||||
public:
|
public:
|
||||||
// Configuration constants
|
// Configuration constants
|
||||||
static constexpr int ELITE_COUNT = 2;
|
static constexpr int ELITE_COUNT = 2;
|
||||||
static constexpr float NOVELTY_WEIGHT = 0.1f;
|
static constexpr float NOVELTY_WEIGHT = 0.1f;
|
||||||
static constexpr float ISLAND_MIGRATION_RATE = 0.05f;
|
static constexpr float ISLAND_MIGRATION_RATE = 0.05f;
|
||||||
static constexpr int BEHAVIORAL_ARCHIVE_SIZE = 100;
|
static constexpr int BEHAVIORAL_ARCHIVE_SIZE = 100;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Construction
|
// Construction
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
explicit Population(
|
explicit Population(int active_size = 75, int repo_size = 10000,
|
||||||
int active_size = 75,
|
EliteStrategy elite_strategy = EliteStrategy::Cumulative,
|
||||||
int repo_size = 10000,
|
bool link_mutation = false, bool link_elite = false,
|
||||||
EliteStrategy elite_strategy = EliteStrategy::Cumulative,
|
bool link_violator = false, bool use_fuzzy_pacer = false,
|
||||||
bool link_mutation = false,
|
bool use_fuzzy_importance = false,
|
||||||
bool link_elite = false,
|
bool direct_construction = false,
|
||||||
bool link_violator = false,
|
bool use_banach_fission = false);
|
||||||
bool use_fuzzy_pacer = false,
|
|
||||||
bool use_fuzzy_importance = false,
|
|
||||||
bool direct_construction = false,
|
|
||||||
bool use_banach_fission = false
|
|
||||||
);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Core API
|
// Core API
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
/// Get the currently active controller (sticky selection)
|
/// Get the currently active controller (sticky selection)
|
||||||
FuzzyController& get_active_controller();
|
FuzzyController &get_active_controller();
|
||||||
|
|
||||||
/// Select a controller via fitness-weighted tournament
|
/// Select a controller via fitness-weighted tournament
|
||||||
FuzzyController& select_weighted();
|
FuzzyController &select_weighted();
|
||||||
|
|
||||||
/// Get the best controller in the active population
|
/// Get the best controller in the active population
|
||||||
FuzzyController& get_best_active();
|
FuzzyController &get_best_active();
|
||||||
|
|
||||||
/// Get the worst non-elite controller
|
/// Get the worst non-elite controller
|
||||||
FuzzyController& get_worst_active();
|
FuzzyController &get_worst_active();
|
||||||
|
|
||||||
/// Remove a specific controller (unless elite)
|
/// Remove a specific controller (unless elite)
|
||||||
void kill(FuzzyController& controller);
|
void kill(FuzzyController &controller);
|
||||||
|
|
||||||
/// Update a controller's fitness
|
/// Update a controller's fitness
|
||||||
void update_controller_fitness(FuzzyController& controller, float reward, bool increment_eval = true);
|
void update_controller_fitness(FuzzyController &controller, float reward,
|
||||||
|
bool increment_eval = true);
|
||||||
|
|
||||||
/// Mark a controller as a violator (rollback)
|
/// Mark a controller as a violator (rollback)
|
||||||
void mark_violated(FuzzyController& controller);
|
void mark_violated(FuzzyController &controller);
|
||||||
|
|
||||||
/// Get the effective fitness considering elite strategy and training progress
|
/// Get the effective fitness considering elite strategy and training progress
|
||||||
float get_effective_fitness(const FuzzyController& controller, float training_progress) const;
|
float get_effective_fitness(const FuzzyController &controller,
|
||||||
|
float training_progress) const;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Evolution
|
// Evolution
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evolve the population: select parents, crossover/mutate, replace worst.
|
* Evolve the population: select parents, crossover/mutate, replace worst.
|
||||||
*
|
*
|
||||||
* @param current_loss Current training loss
|
* @param current_loss Current training loss
|
||||||
* @param velocity Loss velocity
|
* @param velocity Loss velocity
|
||||||
* @param training_progress Training progress [0, 1]
|
* @param training_progress Training progress [0, 1]
|
||||||
*/
|
*/
|
||||||
void evolve(float current_loss, float velocity = 0.0f, float training_progress = 0.0f);
|
void evolve(float current_loss, float velocity = 0.0f,
|
||||||
|
float training_progress = 0.0f);
|
||||||
|
|
||||||
/// Resize the population (dynamic expansion/contraction)
|
/// Resize the population (dynamic expansion/contraction)
|
||||||
void resize(int target_size, float training_progress = 0.5f);
|
void resize(int target_size, float training_progress = 0.5f);
|
||||||
|
|
||||||
/// Reduce mutation variance after rollback
|
/// Reduce mutation variance after rollback
|
||||||
void calm_down();
|
void calm_down();
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Accessors
|
// Accessors
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
int size() const { return static_cast<int>(gladiators_.size()); }
|
int size() const { return static_cast<int>(gladiators_.size()); }
|
||||||
float global_sigma_modifier() const { return global_sigma_modifier_; }
|
float global_sigma_modifier() const { return global_sigma_modifier_; }
|
||||||
|
|
||||||
/// Compute diversity index (behavioral spread)
|
/// Compute diversity index (behavioral spread)
|
||||||
float get_diversity_index() const;
|
float get_diversity_index() const;
|
||||||
|
|
||||||
/// Serialization
|
/// Serialization
|
||||||
// TODO: state_dict / load_state_dict
|
// TODO: state_dict / load_state_dict
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<FuzzyController> gladiators_;
|
std::vector<FuzzyController> gladiators_;
|
||||||
std::vector<FuzzyController> repository_;
|
std::vector<FuzzyController> repository_;
|
||||||
std::vector<FuzzyController> violated_controllers_;
|
std::vector<FuzzyController> violated_controllers_;
|
||||||
|
|
||||||
float global_sigma_modifier_ = 1.0f;
|
float global_sigma_modifier_ = 1.0f;
|
||||||
|
|
||||||
// Sticky controller selection
|
// Sticky controller selection
|
||||||
FuzzyController* active_controller_ = nullptr;
|
FuzzyController *active_controller_ = nullptr;
|
||||||
int steps_active_ = 0;
|
int steps_active_ = 0;
|
||||||
int selection_interval_ = 20;
|
int selection_interval_ = 20;
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
EliteStrategy elite_strategy_;
|
EliteStrategy elite_strategy_;
|
||||||
bool link_mutation_;
|
bool link_mutation_;
|
||||||
bool link_elite_;
|
bool link_elite_;
|
||||||
bool link_violator_;
|
bool link_violator_;
|
||||||
bool use_fuzzy_pacer_;
|
bool use_fuzzy_pacer_;
|
||||||
bool use_fuzzy_importance_;
|
bool use_fuzzy_importance_;
|
||||||
bool direct_construction_;
|
bool direct_construction_;
|
||||||
bool use_banach_fission_;
|
bool use_banach_fission_;
|
||||||
|
|
||||||
// Novelty search
|
// Novelty search
|
||||||
std::vector<std::vector<float>> behavioral_archive_;
|
std::vector<std::vector<float>> behavioral_archive_;
|
||||||
|
|
||||||
// Fitness history for fuzzy pacer
|
// Fitness history for fuzzy pacer
|
||||||
std::vector<float> fitness_history_;
|
std::vector<float> fitness_history_;
|
||||||
|
|
||||||
// Phase 23: periodic reset counter
|
// Phase 23: periodic reset counter
|
||||||
int reset_step_counter_ = 0;
|
int reset_step_counter_ = 0;
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Internal
|
// Internal
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
std::vector<FuzzyController*> get_elites();
|
std::vector<FuzzyController *> get_elites();
|
||||||
void add_to_repository(const FuzzyController& controller);
|
void add_to_repository(const FuzzyController &controller);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -23,23 +23,23 @@ namespace fces {
|
|||||||
*/
|
*/
|
||||||
class SpectralSensor {
|
class SpectralSensor {
|
||||||
public:
|
public:
|
||||||
SpectralSensor() = default;
|
SpectralSensor() = default;
|
||||||
explicit SpectralSensor(torch::nn::Module& model);
|
explicit SpectralSensor(torch::nn::Module &model);
|
||||||
|
|
||||||
/// Track a layer's weight tensor
|
/// Track a layer's weight tensor
|
||||||
void track_layer(const std::string& name, const torch::Tensor& weight);
|
void track_layer(const std::string &name, const torch::Tensor &weight);
|
||||||
|
|
||||||
/// Get the global (average) effective rank
|
/// Get the global (average) effective rank
|
||||||
float get_global_rank() const;
|
float get_global_rank() const;
|
||||||
|
|
||||||
/// Reset all tracked layers
|
/// Reset all tracked layers
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, float> layer_ranks_;
|
std::unordered_map<std::string, float> layer_ranks_;
|
||||||
|
|
||||||
/// Compute effective rank via SVD
|
/// Compute effective rank via SVD
|
||||||
static float compute_effective_rank(const torch::Tensor& weight);
|
static float compute_effective_rank(const torch::Tensor &weight);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,8 +47,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
class SpectralController {
|
class SpectralController {
|
||||||
public:
|
public:
|
||||||
/// Compute the spectral alpha (gating factor for rank-aware updates)
|
/// Compute the spectral alpha (gating factor for rank-aware updates)
|
||||||
float compute_alpha(float global_rank, float grokking_coefficient) const;
|
float compute_alpha(float global_rank, float grokking_coefficient) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ namespace fces {
|
|||||||
|
|
||||||
class Telemetry {
|
class Telemetry {
|
||||||
public:
|
public:
|
||||||
static Telemetry& get();
|
static Telemetry &get();
|
||||||
|
|
||||||
void info(const std::string& event, const std::string& detail = "");
|
void info(const std::string &event, const std::string &detail = "");
|
||||||
void warning(const std::string& event, const std::string& detail = "");
|
void warning(const std::string &event, const std::string &detail = "");
|
||||||
void error(const std::string& event, const std::string& detail = "");
|
void error(const std::string &event, const std::string &detail = "");
|
||||||
|
|
||||||
void push_to_remote();
|
void push_to_remote();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Telemetry() = default;
|
Telemetry() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -13,39 +13,40 @@
|
|||||||
#include <pybind11/stl.h>
|
#include <pybind11/stl.h>
|
||||||
#include <torch/extension.h>
|
#include <torch/extension.h>
|
||||||
|
|
||||||
#include "fces/optimizer.hpp"
|
|
||||||
#include "fces/config.hpp"
|
#include "fces/config.hpp"
|
||||||
|
#include "fces/optimizer.hpp"
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
PYBIND11_MODULE(fces_native, m) {
|
PYBIND11_MODULE(fces_native, m) {
|
||||||
m.doc() = "FCES-native: High-performance C++ FCES optimizer";
|
m.doc() = "FCES-native: High-performance C++ FCES optimizer";
|
||||||
|
|
||||||
py::class_<fces::FCESConfig>(m, "FCESConfig")
|
py::class_<fces::FCESConfig>(m, "FCESConfig")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def_readwrite("lr", &fces::FCESConfig::lr)
|
.def_readwrite("lr", &fces::FCESConfig::lr)
|
||||||
.def_readwrite("population_size", &fces::FCESConfig::population_size)
|
.def_readwrite("population_size", &fces::FCESConfig::population_size)
|
||||||
.def_readwrite("total_steps", &fces::FCESConfig::total_steps)
|
.def_readwrite("total_steps", &fces::FCESConfig::total_steps)
|
||||||
.def_readwrite("grokking_coefficient", &fces::FCESConfig::grokking_coefficient)
|
.def_readwrite("grokking_coefficient",
|
||||||
.def_readwrite("direct_construction", &fces::FCESConfig::direct_construction);
|
&fces::FCESConfig::grokking_coefficient)
|
||||||
|
.def_readwrite("direct_construction",
|
||||||
|
&fces::FCESConfig::direct_construction);
|
||||||
|
|
||||||
py::class_<fces::FCESOptimizer>(m, "FCESOptimizer")
|
py::class_<fces::FCESOptimizer>(m, "FCESOptimizer")
|
||||||
.def(py::init<std::vector<torch::Tensor>, fces::FCESConfig>(),
|
.def(py::init<std::vector<torch::Tensor>, fces::FCESConfig>(),
|
||||||
py::arg("params"),
|
py::arg("params"), py::arg("config") = fces::FCESConfig{})
|
||||||
py::arg("config") = fces::FCESConfig{})
|
.def("step", &fces::FCESOptimizer::step)
|
||||||
.def("step", &fces::FCESOptimizer::step)
|
.def("update_fitness", &fces::FCESOptimizer::update_fitness)
|
||||||
.def("update_fitness", &fces::FCESOptimizer::update_fitness)
|
.def("backup_to_ram", &fces::FCESOptimizer::backup_to_ram)
|
||||||
.def("backup_to_ram", &fces::FCESOptimizer::backup_to_ram)
|
.def("restore_from_ram", &fces::FCESOptimizer::restore_from_ram)
|
||||||
.def("restore_from_ram", &fces::FCESOptimizer::restore_from_ram)
|
.def("step_count", &fces::FCESOptimizer::step_count)
|
||||||
.def("step_count", &fces::FCESOptimizer::step_count)
|
.def("calculate_sparsity", &fces::FCESOptimizer::calculate_sparsity)
|
||||||
.def("calculate_sparsity", &fces::FCESOptimizer::calculate_sparsity)
|
.def("zero_grad", [](fces::FCESOptimizer &self) {
|
||||||
.def("zero_grad", [](fces::FCESOptimizer& self) {
|
for (auto &group : self.param_groups()) {
|
||||||
for (auto& group : self.param_groups()) {
|
for (auto &p : group.params()) {
|
||||||
for (auto& p : group.params()) {
|
if (p.grad().defined()) {
|
||||||
if (p.grad().defined()) {
|
p.grad().zero_();
|
||||||
p.grad().zero_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,341 +13,354 @@ thread_local std::mt19937 FuzzyController::rng_{std::random_device{}()};
|
|||||||
// Genome
|
// Genome
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
void Genome::randomize(std::mt19937& rng) {
|
void Genome::randomize(std::mt19937 &rng) {
|
||||||
std::normal_distribution<float> dist(0.0f, 0.5f);
|
std::normal_distribution<float> dist(0.0f, 0.5f);
|
||||||
for (auto& w : weights) {
|
for (auto &w : weights) {
|
||||||
w = dist(rng);
|
w = dist(rng);
|
||||||
}
|
}
|
||||||
gene_success.fill(0.0f);
|
gene_success.fill(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Genome Genome::clone() const {
|
Genome Genome::clone() const {
|
||||||
return *this; // Copy all fields
|
return *this; // Copy all fields
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// FuzzyController
|
// FuzzyController
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
FuzzyController::FuzzyController()
|
FuzzyController::FuzzyController() : id(next_id_++), origin("random") {
|
||||||
: id(next_id_++), origin("random") {
|
genome.randomize(rng_);
|
||||||
genome.randomize(rng_);
|
// Bias output toward acceleration (V2.1 insight)
|
||||||
// Bias output toward acceleration (V2.1 insight)
|
// Set output biases (last GENOME_OUTPUT_DIM elements) to +2.0, -1.0, 0.0 with
|
||||||
// Set output biases (last GENOME_OUTPUT_DIM elements) to +2.0, -1.0, 0.0 with noise
|
// noise
|
||||||
constexpr int bias_start = GENOME_SIZE - GENOME_OUTPUT_DIM;
|
constexpr int bias_start = GENOME_SIZE - GENOME_OUTPUT_DIM;
|
||||||
std::normal_distribution<float> bias_noise(0.0f, 0.5f);
|
std::normal_distribution<float> bias_noise(0.0f, 0.5f);
|
||||||
genome.weights[bias_start] = 2.0f + bias_noise(rng_);
|
genome.weights[bias_start] = 2.0f + bias_noise(rng_);
|
||||||
genome.weights[bias_start + 1] = -1.0f + bias_noise(rng_);
|
genome.weights[bias_start + 1] = -1.0f + bias_noise(rng_);
|
||||||
genome.weights[bias_start + 2] = 0.0f + bias_noise(rng_);
|
genome.weights[bias_start + 2] = 0.0f + bias_noise(rng_);
|
||||||
|
|
||||||
// Initialize plasticity with variance to seed evolution
|
// Initialize plasticity with variance to seed evolution
|
||||||
std::normal_distribution<float> plast_noise(0.0f, 0.05f);
|
std::normal_distribution<float> plast_noise(0.0f, 0.05f);
|
||||||
genome.plasticity = std::max(0.01f, 0.1f + plast_noise(rng_));
|
genome.plasticity = std::max(0.01f, 0.1f + plast_noise(rng_));
|
||||||
genome.sigma_gene = 0.1f;
|
genome.sigma_gene = 0.1f;
|
||||||
genome.gene_success.fill(1.0f);
|
genome.gene_success.fill(1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuzzyController::FuzzyController(Genome genome)
|
FuzzyController::FuzzyController(Genome genome)
|
||||||
: id(next_id_++), genome(std::move(genome)), origin("constructed") {}
|
: id(next_id_++), genome(std::move(genome)), origin("constructed") {}
|
||||||
|
|
||||||
torch::Tensor FuzzyController::decide_update(
|
torch::Tensor FuzzyController::decide_update(
|
||||||
const std::vector<std::vector<float>>& layer_stats,
|
const std::vector<std::vector<float>> &layer_stats, float loss_trend,
|
||||||
float loss_trend,
|
float step_pct, float rollback_rate, float grad_stability,
|
||||||
float step_pct,
|
float spectral_alpha, float stagnation_intensity, float kzm_damping,
|
||||||
float rollback_rate,
|
float projected_drift) {
|
||||||
float grad_stability,
|
const int num_groups = static_cast<int>(layer_stats.size());
|
||||||
float spectral_alpha,
|
auto actions = torch::zeros({num_groups, GENOME_OUTPUT_DIM});
|
||||||
float stagnation_intensity,
|
|
||||||
float kzm_damping,
|
|
||||||
float projected_drift
|
|
||||||
) {
|
|
||||||
const int num_groups = static_cast<int>(layer_stats.size());
|
|
||||||
auto actions = torch::zeros({num_groups, GENOME_OUTPUT_DIM});
|
|
||||||
|
|
||||||
// Extract weight views for the micro-MLP
|
// Extract weight views for the micro-MLP
|
||||||
const float* w = genome.weights.data();
|
const float *w = genome.weights.data();
|
||||||
|
|
||||||
// Layer 1: input -> hidden
|
// Layer 1: input -> hidden
|
||||||
const float* W1 = w; // [(GENOME_INPUT_DIM + 1) x GENOME_HIDDEN_DIM]
|
const float *W1 = w; // [(GENOME_INPUT_DIM + 1) x GENOME_HIDDEN_DIM]
|
||||||
// Layer 2: hidden -> output
|
// Layer 2: hidden -> output
|
||||||
const float* W2 = w + ((GENOME_INPUT_DIM + 1) * GENOME_HIDDEN_DIM); // [(GENOME_HIDDEN_DIM + 1) x GENOME_OUTPUT_DIM]
|
const float *W2 =
|
||||||
|
w + ((GENOME_INPUT_DIM + 1) *
|
||||||
|
GENOME_HIDDEN_DIM); // [(GENOME_HIDDEN_DIM + 1) x GENOME_OUTPUT_DIM]
|
||||||
|
|
||||||
for (int g = 0; g < num_groups; ++g) {
|
for (int g = 0; g < num_groups; ++g) {
|
||||||
// One-Hot Layer Type: Clamp to 5 to avoid overflow for new categories
|
// One-Hot Layer Type: Clamp to 5 to avoid overflow for new categories
|
||||||
float layer_type_val = (layer_stats[g].size() >= 3) ? layer_stats[g][2] : 5.0f;
|
float layer_type_val =
|
||||||
int type_idx = std::min(5, static_cast<int>(layer_type_val));
|
(layer_stats[g].size() >= 3) ? layer_stats[g][2] : 5.0f;
|
||||||
std::array<float, 5> type_onehot{0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
int type_idx = std::min(5, static_cast<int>(layer_type_val));
|
||||||
if (type_idx >= 0 && type_idx < 5) {
|
std::array<float, 5> type_onehot{0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
type_onehot[type_idx] = 1.0f;
|
if (type_idx >= 0 && type_idx < 5) {
|
||||||
}
|
type_onehot[type_idx] = 1.0f;
|
||||||
|
|
||||||
// Build input vector
|
|
||||||
std::array<float, GENOME_INPUT_DIM> input{};
|
|
||||||
float gn = (layer_stats[g].size() >= 1) ? layer_stats[g][0] : 0.0f;
|
|
||||||
float sp = (layer_stats[g].size() >= 2) ? layer_stats[g][1] : 0.0f;
|
|
||||||
|
|
||||||
// Sanitization matching nan_to_num
|
|
||||||
if (!std::isfinite(gn) || std::isnan(gn)) gn = 0.0f;
|
|
||||||
if (gn > 10.0f) gn = 10.0f;
|
|
||||||
if (gn < 0.0f) gn = 0.0f;
|
|
||||||
|
|
||||||
if (!std::isfinite(sp) || std::isnan(sp)) sp = 0.0f;
|
|
||||||
if (sp > 1.0f) sp = 1.0f;
|
|
||||||
if (sp < 0.0f) sp = 0.0f;
|
|
||||||
|
|
||||||
input[0] = gn;
|
|
||||||
input[1] = sp;
|
|
||||||
input[2] = loss_trend;
|
|
||||||
input[3] = step_pct;
|
|
||||||
input[4] = (num_groups > 1) ? static_cast<float>(g) / (num_groups - 1.0f) : 0.0f;
|
|
||||||
input[5] = rollback_rate;
|
|
||||||
input[6] = grad_stability;
|
|
||||||
input[7] = spectral_alpha;
|
|
||||||
input[8] = type_onehot[0];
|
|
||||||
input[9] = type_onehot[1];
|
|
||||||
input[10] = type_onehot[2];
|
|
||||||
input[11] = type_onehot[3];
|
|
||||||
input[12] = type_onehot[4];
|
|
||||||
input[13] = projected_drift;
|
|
||||||
|
|
||||||
// Forward pass: hidden = tanh(W1 * [input, 1])
|
|
||||||
std::array<float, GENOME_HIDDEN_DIM> hidden{};
|
|
||||||
for (int h = 0; h < GENOME_HIDDEN_DIM; ++h) {
|
|
||||||
float sum = W1[GENOME_INPUT_DIM * GENOME_HIDDEN_DIM + h]; // Bias weight
|
|
||||||
for (int i = 0; i < GENOME_INPUT_DIM; ++i) {
|
|
||||||
sum += input[i] * W1[i * GENOME_HIDDEN_DIM + h];
|
|
||||||
}
|
|
||||||
hidden[h] = std::tanh(sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output = W2 * [hidden, 1]
|
|
||||||
std::array<float, GENOME_OUTPUT_DIM> out_layer{};
|
|
||||||
for (int o = 0; o < GENOME_OUTPUT_DIM; ++o) {
|
|
||||||
float sum = W2[GENOME_HIDDEN_DIM * GENOME_OUTPUT_DIM + o]; // Bias weight
|
|
||||||
for (int h = 0; h < GENOME_HIDDEN_DIM; ++h) {
|
|
||||||
sum += hidden[h] * W2[h * GENOME_OUTPUT_DIM + o];
|
|
||||||
}
|
|
||||||
out_layer[o] = sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Output
|
|
||||||
float log_mult_raw = out_layer[0];
|
|
||||||
float log_wd_raw = out_layer[2];
|
|
||||||
float sign_logit = out_layer[1];
|
|
||||||
|
|
||||||
if (!std::isfinite(sign_logit) || std::isnan(sign_logit)) {
|
|
||||||
sign_logit = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float noise_std = 0.0f;
|
|
||||||
if (genome.plasticity > 0.01f) {
|
|
||||||
noise_std += genome.plasticity;
|
|
||||||
}
|
|
||||||
if (stagnation_intensity > 0.0f) {
|
|
||||||
noise_std += stagnation_intensity * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kzm_damping > 0.0f) {
|
|
||||||
noise_std *= (1.0f - kzm_damping);
|
|
||||||
log_mult_raw *= (1.0f - kzm_damping);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (noise_std > 0.0f) {
|
|
||||||
std::normal_distribution<float> noise_dist(0.0f, noise_std);
|
|
||||||
log_mult_raw += noise_dist(rng_);
|
|
||||||
log_wd_raw += noise_dist(rng_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::isfinite(log_mult_raw) || std::isnan(log_mult_raw)) {
|
|
||||||
log_mult_raw = 0.0f;
|
|
||||||
}
|
|
||||||
log_mult_raw = std::max(-6.0f, std::min(6.0f, log_mult_raw));
|
|
||||||
float mult = std::pow(2.0f, log_mult_raw);
|
|
||||||
|
|
||||||
if (!std::isfinite(log_wd_raw) || std::isnan(log_wd_raw)) {
|
|
||||||
log_wd_raw = 0.0f;
|
|
||||||
}
|
|
||||||
log_wd_raw = std::max(-6.0f, std::min(6.0f, log_wd_raw));
|
|
||||||
float wd_mult = std::pow(2.0f, log_wd_raw);
|
|
||||||
|
|
||||||
actions[g][0] = mult;
|
|
||||||
actions[g][1] = sign_logit;
|
|
||||||
actions[g][2] = wd_mult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions;
|
// Build input vector
|
||||||
|
std::array<float, GENOME_INPUT_DIM> input{};
|
||||||
|
float gn = (layer_stats[g].size() >= 1) ? layer_stats[g][0] : 0.0f;
|
||||||
|
float sp = (layer_stats[g].size() >= 2) ? layer_stats[g][1] : 0.0f;
|
||||||
|
|
||||||
|
// Sanitization matching nan_to_num
|
||||||
|
if (!std::isfinite(gn) || std::isnan(gn))
|
||||||
|
gn = 0.0f;
|
||||||
|
if (gn > 10.0f)
|
||||||
|
gn = 10.0f;
|
||||||
|
if (gn < 0.0f)
|
||||||
|
gn = 0.0f;
|
||||||
|
|
||||||
|
if (!std::isfinite(sp) || std::isnan(sp))
|
||||||
|
sp = 0.0f;
|
||||||
|
if (sp > 1.0f)
|
||||||
|
sp = 1.0f;
|
||||||
|
if (sp < 0.0f)
|
||||||
|
sp = 0.0f;
|
||||||
|
|
||||||
|
input[0] = gn;
|
||||||
|
input[1] = sp;
|
||||||
|
input[2] = loss_trend;
|
||||||
|
input[3] = step_pct;
|
||||||
|
input[4] =
|
||||||
|
(num_groups > 1) ? static_cast<float>(g) / (num_groups - 1.0f) : 0.0f;
|
||||||
|
input[5] = rollback_rate;
|
||||||
|
input[6] = grad_stability;
|
||||||
|
input[7] = spectral_alpha;
|
||||||
|
input[8] = type_onehot[0];
|
||||||
|
input[9] = type_onehot[1];
|
||||||
|
input[10] = type_onehot[2];
|
||||||
|
input[11] = type_onehot[3];
|
||||||
|
input[12] = type_onehot[4];
|
||||||
|
input[13] = projected_drift;
|
||||||
|
|
||||||
|
// Forward pass: hidden = tanh(W1 * [input, 1])
|
||||||
|
std::array<float, GENOME_HIDDEN_DIM> hidden{};
|
||||||
|
for (int h = 0; h < GENOME_HIDDEN_DIM; ++h) {
|
||||||
|
float sum = W1[GENOME_INPUT_DIM * GENOME_HIDDEN_DIM + h]; // Bias weight
|
||||||
|
for (int i = 0; i < GENOME_INPUT_DIM; ++i) {
|
||||||
|
sum += input[i] * W1[i * GENOME_HIDDEN_DIM + h];
|
||||||
|
}
|
||||||
|
hidden[h] = std::tanh(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output = W2 * [hidden, 1]
|
||||||
|
std::array<float, GENOME_OUTPUT_DIM> out_layer{};
|
||||||
|
for (int o = 0; o < GENOME_OUTPUT_DIM; ++o) {
|
||||||
|
float sum = W2[GENOME_HIDDEN_DIM * GENOME_OUTPUT_DIM + o]; // Bias weight
|
||||||
|
for (int h = 0; h < GENOME_HIDDEN_DIM; ++h) {
|
||||||
|
sum += hidden[h] * W2[h * GENOME_OUTPUT_DIM + o];
|
||||||
|
}
|
||||||
|
out_layer[o] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse Output
|
||||||
|
float log_mult_raw = out_layer[0];
|
||||||
|
float log_wd_raw = out_layer[2];
|
||||||
|
float sign_logit = out_layer[1];
|
||||||
|
|
||||||
|
if (!std::isfinite(sign_logit) || std::isnan(sign_logit)) {
|
||||||
|
sign_logit = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float noise_std = 0.0f;
|
||||||
|
if (genome.plasticity > 0.01f) {
|
||||||
|
noise_std += genome.plasticity;
|
||||||
|
}
|
||||||
|
if (stagnation_intensity > 0.0f) {
|
||||||
|
noise_std += stagnation_intensity * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kzm_damping > 0.0f) {
|
||||||
|
noise_std *= (1.0f - kzm_damping);
|
||||||
|
log_mult_raw *= (1.0f - kzm_damping);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noise_std > 0.0f) {
|
||||||
|
std::normal_distribution<float> noise_dist(0.0f, noise_std);
|
||||||
|
log_mult_raw += noise_dist(rng_);
|
||||||
|
log_wd_raw += noise_dist(rng_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::isfinite(log_mult_raw) || std::isnan(log_mult_raw)) {
|
||||||
|
log_mult_raw = 0.0f;
|
||||||
|
}
|
||||||
|
log_mult_raw = std::max(-6.0f, std::min(6.0f, log_mult_raw));
|
||||||
|
float mult = std::pow(2.0f, log_mult_raw);
|
||||||
|
|
||||||
|
if (!std::isfinite(log_wd_raw) || std::isnan(log_wd_raw)) {
|
||||||
|
log_wd_raw = 0.0f;
|
||||||
|
}
|
||||||
|
log_wd_raw = std::max(-6.0f, std::min(6.0f, log_wd_raw));
|
||||||
|
float wd_mult = std::pow(2.0f, log_wd_raw);
|
||||||
|
|
||||||
|
actions[g][0] = mult;
|
||||||
|
actions[g][1] = sign_logit;
|
||||||
|
actions[g][2] = wd_mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuzzyController FuzzyController::mutate(float current_loss, float sigma_scale) const {
|
FuzzyController FuzzyController::mutate(float current_loss,
|
||||||
Genome child_genome = genome.clone();
|
float sigma_scale) const {
|
||||||
std::normal_distribution<float> std_normal(0.0f, 1.0f);
|
Genome child_genome = genome.clone();
|
||||||
|
std::normal_distribution<float> std_normal(0.0f, 1.0f);
|
||||||
|
|
||||||
float tau = 0.2f;
|
float tau = 0.2f;
|
||||||
float new_sigma = genome.sigma_gene * std::exp(tau * std_normal(rng_));
|
float new_sigma = genome.sigma_gene * std::exp(tau * std_normal(rng_));
|
||||||
new_sigma = std::max(0.001f, std::min(0.8f, new_sigma));
|
new_sigma = std::max(0.001f, std::min(0.8f, new_sigma));
|
||||||
|
|
||||||
float new_plast = genome.plasticity * std::exp(tau * std_normal(rng_));
|
float new_plast = genome.plasticity * std::exp(tau * std_normal(rng_));
|
||||||
new_plast = std::max(0.0f, std::min(0.5f, new_plast));
|
new_plast = std::max(0.0f, std::min(0.5f, new_plast));
|
||||||
|
|
||||||
float loss_val = std::max(0.0f, current_loss);
|
float loss_val = std::max(0.0f, current_loss);
|
||||||
float annealing_factor = std::sqrt(loss_val + 0.1f);
|
float annealing_factor = std::sqrt(loss_val + 0.1f);
|
||||||
float effective_sigma = new_sigma * sigma_scale * annealing_factor;
|
float effective_sigma = new_sigma * sigma_scale * annealing_factor;
|
||||||
|
|
||||||
std::normal_distribution<float> noise(0.0f, effective_sigma);
|
std::normal_distribution<float> noise(0.0f, effective_sigma);
|
||||||
for (size_t i = 0; i < child_genome.weights.size(); ++i) {
|
for (size_t i = 0; i < child_genome.weights.size(); ++i) {
|
||||||
child_genome.weights[i] += noise(rng_);
|
child_genome.weights[i] += noise(rng_);
|
||||||
child_genome.gene_success[i] = genome.gene_success[i] * 0.95f;
|
child_genome.gene_success[i] = genome.gene_success[i] * 0.95f;
|
||||||
}
|
}
|
||||||
|
|
||||||
child_genome.sigma_gene = new_sigma;
|
child_genome.sigma_gene = new_sigma;
|
||||||
child_genome.plasticity = new_plast;
|
child_genome.plasticity = new_plast;
|
||||||
|
|
||||||
FuzzyController child(child_genome);
|
FuzzyController child(child_genome);
|
||||||
child.origin = "mutation";
|
child.origin = "mutation";
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuzzyController FuzzyController::crossover(const FuzzyController& partner, bool /*use_alignment*/) const {
|
FuzzyController FuzzyController::crossover(const FuzzyController &partner,
|
||||||
Genome child_genome;
|
bool /*use_alignment*/) const {
|
||||||
std::uniform_real_distribution<float> u_dist(0.0f, 1.0f);
|
Genome child_genome;
|
||||||
|
std::uniform_real_distribution<float> u_dist(0.0f, 1.0f);
|
||||||
|
|
||||||
for (size_t i = 0; i < child_genome.weights.size(); ++i) {
|
for (size_t i = 0; i < child_genome.weights.size(); ++i) {
|
||||||
float success_self = genome.gene_success[i];
|
float success_self = genome.gene_success[i];
|
||||||
float success_partner = partner.genome.gene_success[i];
|
float success_partner = partner.genome.gene_success[i];
|
||||||
float prob_a = success_self / (success_self + success_partner + 1e-9f);
|
float prob_a = success_self / (success_self + success_partner + 1e-9f);
|
||||||
|
|
||||||
bool choose_self = false;
|
bool choose_self = false;
|
||||||
if (u_dist(rng_) < 0.1f) {
|
if (u_dist(rng_) < 0.1f) {
|
||||||
// 10% Random injection
|
// 10% Random injection
|
||||||
choose_self = (u_dist(rng_) < 0.5f);
|
choose_self = (u_dist(rng_) < 0.5f);
|
||||||
} else {
|
} else {
|
||||||
// 90% Meritocratic
|
// 90% Meritocratic
|
||||||
choose_self = (u_dist(rng_) < prob_a);
|
choose_self = (u_dist(rng_) < prob_a);
|
||||||
}
|
|
||||||
|
|
||||||
if (choose_self) {
|
|
||||||
child_genome.weights[i] = genome.weights[i];
|
|
||||||
child_genome.gene_success[i] = success_self;
|
|
||||||
} else {
|
|
||||||
child_genome.weights[i] = partner.genome.weights[i];
|
|
||||||
child_genome.gene_success[i] = success_partner;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
child_genome.sigma_gene = (genome.sigma_gene + partner.genome.sigma_gene) * 0.5f;
|
if (choose_self) {
|
||||||
child_genome.plasticity = (genome.plasticity + partner.genome.plasticity) * 0.5f;
|
child_genome.weights[i] = genome.weights[i];
|
||||||
|
child_genome.gene_success[i] = success_self;
|
||||||
|
} else {
|
||||||
|
child_genome.weights[i] = partner.genome.weights[i];
|
||||||
|
child_genome.gene_success[i] = success_partner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FuzzyController child(child_genome);
|
child_genome.sigma_gene =
|
||||||
child.origin = "crossover";
|
(genome.sigma_gene + partner.genome.sigma_gene) * 0.5f;
|
||||||
return child;
|
child_genome.plasticity =
|
||||||
|
(genome.plasticity + partner.genome.plasticity) * 0.5f;
|
||||||
|
|
||||||
|
FuzzyController child(child_genome);
|
||||||
|
child.origin = "crossover";
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
FuzzyController FuzzyController::create_orthogonal_child(float intensity) const {
|
FuzzyController
|
||||||
Genome child_genome;
|
FuzzyController::create_orthogonal_child(float intensity) const {
|
||||||
std::normal_distribution<float> norm_dist(0.0f, 1.0f);
|
Genome child_genome;
|
||||||
|
std::normal_distribution<float> norm_dist(0.0f, 1.0f);
|
||||||
|
|
||||||
float norm_elite = 0.0f;
|
float norm_elite = 0.0f;
|
||||||
for (float w : genome.weights) {
|
for (float w : genome.weights) {
|
||||||
norm_elite += w * w;
|
norm_elite += w * w;
|
||||||
}
|
}
|
||||||
norm_elite = std::sqrt(norm_elite) + 1e-9f;
|
norm_elite = std::sqrt(norm_elite) + 1e-9f;
|
||||||
|
|
||||||
std::array<float, GENOME_SIZE> random_vec{};
|
std::array<float, GENOME_SIZE> random_vec{};
|
||||||
float dot_product = 0.0f;
|
float dot_product = 0.0f;
|
||||||
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
|
random_vec[i] = norm_dist(rng_);
|
||||||
|
dot_product += random_vec[i] * genome.weights[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<float, GENOME_SIZE> orthogonal_vec{};
|
||||||
|
float norm_ortho = 0.0f;
|
||||||
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
|
float projection =
|
||||||
|
(dot_product / (norm_elite * norm_elite)) * genome.weights[i];
|
||||||
|
orthogonal_vec[i] = random_vec[i] - projection;
|
||||||
|
norm_ortho += orthogonal_vec[i] * orthogonal_vec[i];
|
||||||
|
}
|
||||||
|
norm_ortho = std::sqrt(norm_ortho) + 1e-9f;
|
||||||
|
|
||||||
|
std::array<float, GENOME_SIZE> scaled_vec{};
|
||||||
|
float final_norm = 0.0f;
|
||||||
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
|
scaled_vec[i] = orthogonal_vec[i] * (norm_elite / norm_ortho) * intensity;
|
||||||
|
final_norm += scaled_vec[i] * scaled_vec[i];
|
||||||
|
}
|
||||||
|
final_norm = std::sqrt(final_norm);
|
||||||
|
|
||||||
|
float max_allowed = std::max(norm_elite, 10.0f);
|
||||||
|
if (final_norm > max_allowed) {
|
||||||
|
float scale = max_allowed / (final_norm + 1e-9f);
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
random_vec[i] = norm_dist(rng_);
|
scaled_vec[i] *= scale;
|
||||||
dot_product += random_vec[i] * genome.weights[i];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::array<float, GENOME_SIZE> orthogonal_vec{};
|
child_genome.weights = scaled_vec;
|
||||||
float norm_ortho = 0.0f;
|
child_genome.gene_success.fill(1.0f);
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
child_genome.sigma_gene = 0.2f;
|
||||||
float projection = (dot_product / (norm_elite * norm_elite)) * genome.weights[i];
|
child_genome.plasticity = 0.2f;
|
||||||
orthogonal_vec[i] = random_vec[i] - projection;
|
|
||||||
norm_ortho += orthogonal_vec[i] * orthogonal_vec[i];
|
|
||||||
}
|
|
||||||
norm_ortho = std::sqrt(norm_ortho) + 1e-9f;
|
|
||||||
|
|
||||||
std::array<float, GENOME_SIZE> scaled_vec{};
|
FuzzyController child(child_genome);
|
||||||
float final_norm = 0.0f;
|
child.origin = "phoenix_rebirth";
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
return child;
|
||||||
scaled_vec[i] = orthogonal_vec[i] * (norm_elite / norm_ortho) * intensity;
|
|
||||||
final_norm += scaled_vec[i] * scaled_vec[i];
|
|
||||||
}
|
|
||||||
final_norm = std::sqrt(final_norm);
|
|
||||||
|
|
||||||
float max_allowed = std::max(norm_elite, 10.0f);
|
|
||||||
if (final_norm > max_allowed) {
|
|
||||||
float scale = max_allowed / (final_norm + 1e-9f);
|
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
|
||||||
scaled_vec[i] *= scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
child_genome.weights = scaled_vec;
|
|
||||||
child_genome.gene_success.fill(1.0f);
|
|
||||||
child_genome.sigma_gene = 0.2f;
|
|
||||||
child_genome.plasticity = 0.2f;
|
|
||||||
|
|
||||||
FuzzyController child(child_genome);
|
|
||||||
child.origin = "phoenix_rebirth";
|
|
||||||
return child;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<FuzzyController, FuzzyController> FuzzyController::banach_tarski_fission(float intensity) const {
|
std::pair<FuzzyController, FuzzyController>
|
||||||
Genome plus_genome;
|
FuzzyController::banach_tarski_fission(float intensity) const {
|
||||||
Genome minus_genome;
|
Genome plus_genome;
|
||||||
|
Genome minus_genome;
|
||||||
|
|
||||||
float norm_parent = 0.0f;
|
float norm_parent = 0.0f;
|
||||||
float max_success = 0.0f;
|
float max_success = 0.0f;
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
norm_parent += genome.weights[i] * genome.weights[i];
|
norm_parent += genome.weights[i] * genome.weights[i];
|
||||||
if (genome.gene_success[i] > max_success) {
|
if (genome.gene_success[i] > max_success) {
|
||||||
max_success = genome.gene_success[i];
|
max_success = genome.gene_success[i];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
norm_parent = std::sqrt(norm_parent) + 1e-9f;
|
}
|
||||||
max_success += 1e-9f;
|
norm_parent = std::sqrt(norm_parent) + 1e-9f;
|
||||||
|
max_success += 1e-9f;
|
||||||
|
|
||||||
std::normal_distribution<float> norm_dist(0.0f, 1.0f);
|
std::normal_distribution<float> norm_dist(0.0f, 1.0f);
|
||||||
std::array<float, GENOME_SIZE> noise{};
|
std::array<float, GENOME_SIZE> noise{};
|
||||||
float dot_product = 0.0f;
|
float dot_product = 0.0f;
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
float saliency = genome.gene_success[i] / max_success;
|
float saliency = genome.gene_success[i] / max_success;
|
||||||
noise[i] = norm_dist(rng_) * saliency;
|
noise[i] = norm_dist(rng_) * saliency;
|
||||||
dot_product += noise[i] * genome.weights[i];
|
dot_product += noise[i] * genome.weights[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<float, GENOME_SIZE> fission_vec{};
|
std::array<float, GENOME_SIZE> fission_vec{};
|
||||||
float norm_fission = 0.0f;
|
float norm_fission = 0.0f;
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
fission_vec[i] = noise[i] - (dot_product / (norm_parent * norm_parent)) * genome.weights[i];
|
fission_vec[i] = noise[i] - (dot_product / (norm_parent * norm_parent)) *
|
||||||
norm_fission += fission_vec[i] * fission_vec[i];
|
genome.weights[i];
|
||||||
}
|
norm_fission += fission_vec[i] * fission_vec[i];
|
||||||
norm_fission = std::sqrt(norm_fission) + 1e-9f;
|
}
|
||||||
|
norm_fission = std::sqrt(norm_fission) + 1e-9f;
|
||||||
|
|
||||||
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
for (size_t i = 0; i < GENOME_SIZE; ++i) {
|
||||||
float scaled_fission = fission_vec[i] * (norm_parent / norm_fission) * 0.1f * intensity;
|
float scaled_fission =
|
||||||
plus_genome.weights[i] = genome.weights[i] + scaled_fission;
|
fission_vec[i] * (norm_parent / norm_fission) * 0.1f * intensity;
|
||||||
minus_genome.weights[i] = genome.weights[i] - scaled_fission;
|
plus_genome.weights[i] = genome.weights[i] + scaled_fission;
|
||||||
|
minus_genome.weights[i] = genome.weights[i] - scaled_fission;
|
||||||
|
|
||||||
plus_genome.gene_success[i] = 1.0f;
|
plus_genome.gene_success[i] = 1.0f;
|
||||||
minus_genome.gene_success[i] = 1.0f;
|
minus_genome.gene_success[i] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
plus_genome.sigma_gene = genome.sigma_gene * 0.9f;
|
plus_genome.sigma_gene = genome.sigma_gene * 0.9f;
|
||||||
minus_genome.sigma_gene = genome.sigma_gene * 0.9f;
|
minus_genome.sigma_gene = genome.sigma_gene * 0.9f;
|
||||||
|
|
||||||
plus_genome.plasticity = genome.plasticity;
|
plus_genome.plasticity = genome.plasticity;
|
||||||
minus_genome.plasticity = genome.plasticity;
|
minus_genome.plasticity = genome.plasticity;
|
||||||
|
|
||||||
FuzzyController child_plus(plus_genome);
|
FuzzyController child_plus(plus_genome);
|
||||||
child_plus.origin = "fission_plus";
|
child_plus.origin = "fission_plus";
|
||||||
|
|
||||||
FuzzyController child_minus(minus_genome);
|
FuzzyController child_minus(minus_genome);
|
||||||
child_minus.origin = "fission_minus";
|
child_minus.origin = "fission_minus";
|
||||||
|
|
||||||
return {child_plus, child_minus};
|
return {child_plus, child_minus};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -2,51 +2,46 @@
|
|||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
EvolutionManager::EvolutionManager(
|
EvolutionManager::EvolutionManager(Population &population,
|
||||||
Population& population, int selection_interval,
|
int selection_interval, bool auto_population,
|
||||||
bool auto_population, bool direct_construction
|
bool direct_construction)
|
||||||
)
|
: population_(population), selection_interval(selection_interval),
|
||||||
: population_(population),
|
|
||||||
selection_interval(selection_interval),
|
|
||||||
auto_population_(auto_population),
|
auto_population_(auto_population),
|
||||||
direct_construction_(direct_construction) {}
|
direct_construction_(direct_construction) {}
|
||||||
|
|
||||||
FuzzyController& EvolutionManager::get_active_controller() {
|
FuzzyController &EvolutionManager::get_active_controller() {
|
||||||
return population_.get_active_controller();
|
return population_.get_active_controller();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvolutionManager::update_population_dynamics(
|
void EvolutionManager::update_population_dynamics(float loss_velocity,
|
||||||
float loss_velocity, float ema_loss, int step_counter, int total_steps
|
float ema_loss,
|
||||||
) {
|
int step_counter,
|
||||||
float progress = static_cast<float>(step_counter) / std::max(1, total_steps);
|
int total_steps) {
|
||||||
|
float progress = static_cast<float>(step_counter) / std::max(1, total_steps);
|
||||||
|
|
||||||
if (step_counter % 20 == 0) {
|
if (step_counter % 20 == 0) {
|
||||||
population_.evolve(
|
population_.evolve(std::abs(loss_velocity), loss_velocity, progress);
|
||||||
std::abs(loss_velocity),
|
}
|
||||||
loss_velocity,
|
|
||||||
progress
|
if (!auto_population_ || step_counter % 50 != 0) {
|
||||||
);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int current_pop = population_.size();
|
||||||
|
float adaptive_threshold = 0.05f * (1.0f + ema_loss);
|
||||||
|
adaptive_threshold = std::min(0.5f, adaptive_threshold);
|
||||||
|
|
||||||
|
if (std::abs(loss_velocity) < adaptive_threshold) {
|
||||||
|
int target_pop = 200;
|
||||||
|
if (target_pop > current_pop) {
|
||||||
|
population_.resize(target_pop, progress);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (!auto_population_ || step_counter % 50 != 0) {
|
int target_pop = 40;
|
||||||
return;
|
if (target_pop < current_pop) {
|
||||||
}
|
population_.resize(target_pop, progress);
|
||||||
|
|
||||||
int current_pop = population_.size();
|
|
||||||
float adaptive_threshold = 0.05f * (1.0f + ema_loss);
|
|
||||||
adaptive_threshold = std::min(0.5f, adaptive_threshold);
|
|
||||||
|
|
||||||
if (std::abs(loss_velocity) < adaptive_threshold) {
|
|
||||||
int target_pop = 200;
|
|
||||||
if (target_pop > current_pop) {
|
|
||||||
population_.resize(target_pop, progress);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int target_pop = 40;
|
|
||||||
if (target_pop < current_pop) {
|
|
||||||
population_.resize(target_pop, progress);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "fces/fitness.hpp"
|
#include "fces/fitness.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
@@ -11,28 +11,30 @@ namespace fces {
|
|||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
void RunningStats::update(float value) {
|
void RunningStats::update(float value) {
|
||||||
count_++;
|
count_++;
|
||||||
float delta = value - mean_;
|
float delta = value - mean_;
|
||||||
mean_ += delta / static_cast<float>(count_);
|
mean_ += delta / static_cast<float>(count_);
|
||||||
float delta2 = value - mean_;
|
float delta2 = value - mean_;
|
||||||
m2_ += delta * delta2;
|
m2_ += delta * delta2;
|
||||||
}
|
}
|
||||||
|
|
||||||
float RunningStats::z_score(float value) const {
|
float RunningStats::z_score(float value) const {
|
||||||
float s = get_std();
|
float s = get_std();
|
||||||
if (s < 1e-8f) return 0.0f;
|
if (s < 1e-8f)
|
||||||
return (value - mean_) / s;
|
return 0.0f;
|
||||||
|
return (value - mean_) / s;
|
||||||
}
|
}
|
||||||
|
|
||||||
float RunningStats::get_std() const {
|
float RunningStats::get_std() const {
|
||||||
if (count_ < 2) return 1.0f;
|
if (count_ < 2)
|
||||||
return std::sqrt(m2_ / static_cast<float>(count_ - 1));
|
return 1.0f;
|
||||||
|
return std::sqrt(m2_ / static_cast<float>(count_ - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunningStats::reset() {
|
void RunningStats::reset() {
|
||||||
count_ = 0;
|
count_ = 0;
|
||||||
mean_ = 0.0f;
|
mean_ = 0.0f;
|
||||||
m2_ = 0.0f;
|
m2_ = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
@@ -42,18 +44,20 @@ void RunningStats::reset() {
|
|||||||
FitnessEngine::FitnessEngine(float grokking_coefficient)
|
FitnessEngine::FitnessEngine(float grokking_coefficient)
|
||||||
: grokking_coefficient_(grokking_coefficient) {}
|
: grokking_coefficient_(grokking_coefficient) {}
|
||||||
|
|
||||||
float FitnessEngine::calculate_loss_signal(float current_loss, float ema_loss, const std::string& mode) const {
|
float FitnessEngine::calculate_loss_signal(float current_loss, float ema_loss,
|
||||||
if (ema_loss < 1e-8f) return 0.0f;
|
const std::string &mode) const {
|
||||||
|
if (ema_loss < 1e-8f)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
if (mode == "relative") {
|
if (mode == "relative") {
|
||||||
return (current_loss - ema_loss) / (ema_loss + 1e-8f);
|
return (current_loss - ema_loss) / (ema_loss + 1e-8f);
|
||||||
}
|
}
|
||||||
return current_loss - ema_loss;
|
return current_loss - ema_loss;
|
||||||
}
|
}
|
||||||
|
|
||||||
float FitnessEngine::compute_kzm_damping(float spectral_alpha) const {
|
float FitnessEngine::compute_kzm_damping(float spectral_alpha) const {
|
||||||
// Kibble-Zurek damping: high spectral rank = more damping
|
// Kibble-Zurek damping: high spectral rank = more damping
|
||||||
return 1.0f / (1.0f + grokking_coefficient_ * spectral_alpha);
|
return 1.0f / (1.0f + grokking_coefficient_ * spectral_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
@@ -68,30 +72,28 @@ FuzzyFitnessEvaluator::FuzzyFitnessEvaluator() noexcept
|
|||||||
consistency_set_("Consistent", -1.0f, 0.0f, 0.02f, 0.1f),
|
consistency_set_("Consistent", -1.0f, 0.0f, 0.02f, 0.1f),
|
||||||
rank_set_("LowRank", -1.0f, 0.0f, 5.0f, 20.0f) {}
|
rank_set_("LowRank", -1.0f, 0.0f, 5.0f, 20.0f) {}
|
||||||
|
|
||||||
float FuzzyFitnessEvaluator::evaluate(const FitnessMetrics& metrics) const noexcept {
|
float FuzzyFitnessEvaluator::evaluate(
|
||||||
float m_stability = stability_set_.membership(metrics.grad_cv);
|
const FitnessMetrics &metrics) const noexcept {
|
||||||
float m_train = train_set_.membership(metrics.training_advantage);
|
float m_stability = stability_set_.membership(metrics.grad_cv);
|
||||||
float m_val = val_set_.membership(metrics.validation_advantage);
|
float m_train = train_set_.membership(metrics.training_advantage);
|
||||||
float m_sparsity = sparsity_set_.membership(metrics.sparsity_delta);
|
float m_val = val_set_.membership(metrics.validation_advantage);
|
||||||
float m_consistency = consistency_set_.membership(metrics.consistency_gap);
|
float m_sparsity = sparsity_set_.membership(metrics.sparsity_delta);
|
||||||
float m_rank = rank_set_.membership(metrics.stable_rank);
|
float m_consistency = consistency_set_.membership(metrics.consistency_gap);
|
||||||
|
float m_rank = rank_set_.membership(metrics.stable_rank);
|
||||||
|
|
||||||
float weighted_score =
|
float weighted_score = m_stability * w_stability_ + m_train * w_train_ +
|
||||||
m_stability * w_stability_ +
|
m_val * w_val_ + m_sparsity * w_sparsity_ +
|
||||||
m_train * w_train_ +
|
m_consistency * w_consistency_ + m_rank * w_rank_;
|
||||||
m_val * w_val_ +
|
|
||||||
m_sparsity * w_sparsity_ +
|
|
||||||
m_consistency * w_consistency_ +
|
|
||||||
m_rank * w_rank_;
|
|
||||||
|
|
||||||
float total_weight = w_stability_ + w_train_ + w_val_ + w_sparsity_ + w_consistency_ + w_rank_;
|
float total_weight =
|
||||||
if (total_weight > 0.0f) {
|
w_stability_ + w_train_ + w_val_ + w_sparsity_ + w_consistency_ + w_rank_;
|
||||||
weighted_score /= total_weight;
|
if (total_weight > 0.0f) {
|
||||||
}
|
weighted_score /= total_weight;
|
||||||
|
}
|
||||||
|
|
||||||
// V153: Generalization-Aware Gate (Non-Linear)
|
// V153: Generalization-Aware Gate (Non-Linear)
|
||||||
float gate_efficiency = 0.5f + 0.5f * m_consistency;
|
float gate_efficiency = 0.5f + 0.5f * m_consistency;
|
||||||
return weighted_score * gate_efficiency;
|
return weighted_score * gate_efficiency;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -1,475 +1,496 @@
|
|||||||
#include "fces/optimizer.hpp"
|
#include "fces/optimizer.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
int classify_layer_by_shape(const torch::Tensor& p) {
|
int classify_layer_by_shape(const torch::Tensor &p) {
|
||||||
auto dims = p.sizes();
|
auto dims = p.sizes();
|
||||||
if (dims.size() == 2) {
|
if (dims.size() == 2) {
|
||||||
int64_t d0 = dims[0];
|
int64_t d0 = dims[0];
|
||||||
int64_t d1 = dims[1];
|
int64_t d1 = dims[1];
|
||||||
if (d0 > 10000 || d1 > 10000) {
|
if (d0 > 10000 || d1 > 10000) {
|
||||||
return 0; // Embedding
|
return 0; // Embedding
|
||||||
} else if (d0 * 3 == d1 || d0 == d1 * 3) {
|
} else if (d0 * 3 == d1 || d0 == d1 * 3) {
|
||||||
return 1; // Attention QKV
|
return 1; // Attention QKV
|
||||||
} else if (d0 == d1) {
|
} else if (d0 == d1) {
|
||||||
return 3; // MLP/FFN
|
return 3; // MLP/FFN
|
||||||
} else {
|
} else {
|
||||||
return 2; // Attention Proj
|
return 2; // Attention Proj
|
||||||
}
|
|
||||||
} else if (dims.size() == 1) {
|
|
||||||
if (dims[0] < 128) {
|
|
||||||
return 4; // LayerNorm
|
|
||||||
} else {
|
|
||||||
return 5; // Other / bias
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 5; // Other
|
} else if (dims.size() == 1) {
|
||||||
|
if (dims[0] < 128) {
|
||||||
|
return 4; // LayerNorm
|
||||||
|
} else {
|
||||||
|
return 5; // Other / bias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 5; // Other
|
||||||
}
|
}
|
||||||
|
|
||||||
torch::Tensor apply_trust_clipping(const torch::Tensor& p, torch::Tensor update, float trust_region_clip) {
|
torch::Tensor apply_trust_clipping(const torch::Tensor &p, torch::Tensor update,
|
||||||
if (torch::isnan(update).any().item<bool>() || torch::isinf(update).any().item<bool>()) {
|
float trust_region_clip) {
|
||||||
return torch::zeros_like(update);
|
if (torch::isnan(update).any().item<bool>() ||
|
||||||
|
torch::isinf(update).any().item<bool>()) {
|
||||||
|
return torch::zeros_like(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
float p_norm = p.norm().item<float>();
|
||||||
|
if (p_norm > 1e-6f) {
|
||||||
|
float update_mag = update.norm().item<float>();
|
||||||
|
if (!std::isfinite(update_mag)) {
|
||||||
|
return torch::zeros_like(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
float p_norm = p.norm().item<float>();
|
float max_update = trust_region_clip * p_norm;
|
||||||
if (p_norm > 1e-6f) {
|
if (update_mag > max_update) {
|
||||||
float update_mag = update.norm().item<float>();
|
float correction = max_update / (update_mag + 1e-8f);
|
||||||
if (!std::isfinite(update_mag)) {
|
update.mul_(correction);
|
||||||
return torch::zeros_like(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
float max_update = trust_region_clip * p_norm;
|
|
||||||
if (update_mag > max_update) {
|
|
||||||
float correction = max_update / (update_mag + 1e-8f);
|
|
||||||
update.mul_(correction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (torch::isnan(update).any().item<bool>() || torch::isinf(update).any().item<bool>()) {
|
if (torch::isnan(update).any().item<bool>() ||
|
||||||
return torch::zeros_like(update);
|
torch::isinf(update).any().item<bool>()) {
|
||||||
}
|
return torch::zeros_like(update);
|
||||||
|
}
|
||||||
|
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
float calculate_parasitic_reward(const torch::Tensor& p, float mult, const RunningStats& grad_norm_tracker) {
|
float calculate_parasitic_reward(const torch::Tensor &p, float mult,
|
||||||
if (!p.grad().defined()) {
|
const RunningStats &grad_norm_tracker) {
|
||||||
return 0.0f;
|
if (!p.grad().defined()) {
|
||||||
}
|
return 0.0f;
|
||||||
float g_norm = p.grad().abs().mean().item<float>();
|
}
|
||||||
float z_g = grad_norm_tracker.z_score(g_norm);
|
float g_norm = p.grad().abs().mean().item<float>();
|
||||||
return z_g * (mult - 1.0f);
|
float z_g = grad_norm_tracker.z_score(g_norm);
|
||||||
|
return z_g * (mult - 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<torch::optim::OptimizerOptions> make_optimizer_options(double lr) {
|
std::unique_ptr<torch::optim::OptimizerOptions>
|
||||||
return std::make_unique<FCESOptimizerOptions>(lr);
|
make_optimizer_options(double lr) {
|
||||||
|
return std::make_unique<FCESOptimizerOptions>(lr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FCESOptimizer::FCESOptimizer(
|
FCESOptimizer::FCESOptimizer(std::vector<torch::Tensor> params,
|
||||||
std::vector<torch::Tensor> params,
|
FCESConfig config)
|
||||||
FCESConfig config
|
|
||||||
)
|
|
||||||
: torch::optim::Optimizer(
|
: torch::optim::Optimizer(
|
||||||
{torch::optim::OptimizerParamGroup(std::move(params))},
|
{torch::optim::OptimizerParamGroup(std::move(params))},
|
||||||
make_optimizer_options(config.lr)
|
make_optimizer_options(config.lr)),
|
||||||
),
|
|
||||||
config_(std::move(config)),
|
config_(std::move(config)),
|
||||||
population_(config_.population_size, 10000,
|
population_(config_.population_size, 10000, EliteStrategy::Cumulative,
|
||||||
EliteStrategy::Cumulative,
|
|
||||||
false, false, false, false, false,
|
false, false, false, false, false,
|
||||||
config_.direct_construction,
|
config_.direct_construction, config_.use_banach_fission),
|
||||||
config_.use_banach_fission),
|
|
||||||
fitness_engine_(config_.grokking_coefficient) {
|
fitness_engine_(config_.grokking_coefficient) {
|
||||||
|
|
||||||
evolution_manager_ = std::make_unique<EvolutionManager>(
|
evolution_manager_ = std::make_unique<EvolutionManager>(
|
||||||
population_, 50, config_.auto_population, config_.direct_construction
|
population_, 50, config_.auto_population, config_.direct_construction);
|
||||||
);
|
|
||||||
|
|
||||||
spectral_sensor_ = std::make_unique<SpectralSensor>();
|
spectral_sensor_ = std::make_unique<SpectralSensor>();
|
||||||
|
|
||||||
// Initial RAM backup
|
// Initial RAM backup
|
||||||
backup_to_ram();
|
backup_to_ram();
|
||||||
|
|
||||||
Telemetry::get().info("optimizer_initialized",
|
Telemetry::get().info("optimizer_initialized",
|
||||||
"version=0.1.0 pop_size=" + std::to_string(config_.population_size));
|
"version=0.1.0 pop_size=" +
|
||||||
|
std::to_string(config_.population_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
torch::Tensor FCESOptimizer::step(LossClosure closure) {
|
torch::Tensor FCESOptimizer::step(LossClosure closure) {
|
||||||
torch::NoGradGuard no_grad;
|
torch::NoGradGuard no_grad;
|
||||||
step_counter_++;
|
step_counter_++;
|
||||||
|
|
||||||
torch::Tensor loss = {};
|
torch::Tensor loss = {};
|
||||||
if (closure) {
|
if (closure) {
|
||||||
torch::AutoGradMode grad_mode(true);
|
torch::AutoGradMode grad_mode(true);
|
||||||
loss = closure();
|
loss = closure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Gather Statistics
|
// 1. Gather Statistics
|
||||||
gather_stats();
|
gather_stats();
|
||||||
|
|
||||||
// 2. Strategy: Population Selection & Dynamics
|
// 2. Strategy: Population Selection & Dynamics
|
||||||
auto& active_controller = evolution_manager_->get_active_controller();
|
auto &active_controller = evolution_manager_->get_active_controller();
|
||||||
|
|
||||||
// 3. Decision: Neural Decisions from Controllers
|
// 3. Decision: Neural Decisions from Controllers
|
||||||
float current_loss_val = (loss.defined()) ? loss.item<float>() : last_step_loss_;
|
float current_loss_val =
|
||||||
|
(loss.defined()) ? loss.item<float>() : last_step_loss_;
|
||||||
// Emergency Brake - NaN/Inf Detection
|
|
||||||
if (std::isnan(current_loss_val) || !std::isfinite(current_loss_val)) {
|
|
||||||
Telemetry::get().error("emergency_brake_nan", "NaN/Inf loss detected in step " + std::to_string(step_counter_));
|
|
||||||
handle_rollback();
|
|
||||||
return loss;
|
|
||||||
}
|
|
||||||
|
|
||||||
float loss_velocity = fitness_engine_.calculate_loss_signal(current_loss_val, ema_loss_, config_.signal_mode);
|
|
||||||
last_loss_velocity_ = loss_velocity;
|
|
||||||
|
|
||||||
float progress = std::min(1.0f, static_cast<float>(step_counter_) / std::max(1, config_.total_steps));
|
|
||||||
float grad_cv = grad_norm_tracker_.get_std() / (grad_norm_tracker_.get_mean() + 1e-8f);
|
|
||||||
|
|
||||||
float csr_factor = 1.0f;
|
|
||||||
if (config_.csr_enabled) {
|
|
||||||
if (step_counter_ < config_.csr_warmup_steps) {
|
|
||||||
csr_factor = 0.0f;
|
|
||||||
} else {
|
|
||||||
float steps_since_warmup = static_cast<float>(step_counter_ - config_.csr_warmup_steps);
|
|
||||||
csr_factor = std::min(1.0f, steps_since_warmup / std::max(1.0f, static_cast<float>(config_.csr_ramp_steps)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update spectral sensing rank
|
|
||||||
float spectral_alpha = 0.0f;
|
|
||||||
if (config_.grokking_coefficient > 0.0f && spectral_sensor_) {
|
|
||||||
if (step_counter_ % config_.spectral_frequency == 0 || last_spectral_rank_ == 0.0f) {
|
|
||||||
int param_idx = 0;
|
|
||||||
for (auto& group : param_groups()) {
|
|
||||||
for (auto& p : group.params()) {
|
|
||||||
if (p.dim() >= 2) {
|
|
||||||
std::string name = "layer_" + std::to_string(param_idx);
|
|
||||||
spectral_sensor_->track_layer(name, p);
|
|
||||||
}
|
|
||||||
param_idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_spectral_rank_ = spectral_sensor_->get_global_rank();
|
|
||||||
}
|
|
||||||
spectral_alpha = last_spectral_rank_;
|
|
||||||
}
|
|
||||||
|
|
||||||
float effective_alpha = spectral_alpha * csr_factor;
|
|
||||||
float kzm_damping = fitness_engine_.compute_kzm_damping(effective_alpha);
|
|
||||||
float stagnation_intensity = std::min(1.0f, static_cast<float>(stagnation_counter_) / 500.0f);
|
|
||||||
float log_spectral_alpha = std::log(effective_alpha + 1e-6f);
|
|
||||||
|
|
||||||
// Call decide_update
|
|
||||||
auto actions = active_controller.decide_update(
|
|
||||||
layer_stats_,
|
|
||||||
loss_velocity,
|
|
||||||
progress,
|
|
||||||
rollback_ema_,
|
|
||||||
grad_cv,
|
|
||||||
log_spectral_alpha,
|
|
||||||
stagnation_intensity,
|
|
||||||
kzm_damping,
|
|
||||||
loss_velocity
|
|
||||||
);
|
|
||||||
|
|
||||||
// Bandit-style Early Stopping
|
|
||||||
if (step_counter_ % 5 == 0 && loss_velocity > 0.05f) {
|
|
||||||
Telemetry::get().warning("early_stopping_poor_controller",
|
|
||||||
"controller_id=" + std::to_string(active_controller.id) + " velocity=" + std::to_string(loss_velocity));
|
|
||||||
evolution_manager_->steps_active = evolution_manager_->selection_interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (torch::isnan(actions).any().item<bool>()) {
|
|
||||||
Telemetry::get().error("controller_nan_actions", "NaN actions returned by controller ID " + std::to_string(active_controller.id));
|
|
||||||
population_.kill(active_controller);
|
|
||||||
auto& new_controller = evolution_manager_->get_active_controller();
|
|
||||||
actions = torch::zeros_like(actions);
|
|
||||||
for (int i = 0; i < actions.size(0); ++i) {
|
|
||||||
actions[i][0] = 0.5f; // log_mult default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Action: Apply Updates
|
|
||||||
apply_parameter_updates(actions);
|
|
||||||
|
|
||||||
// 5. Evolution & Maintenance
|
|
||||||
if (current_loss_val > 0.0f) {
|
|
||||||
evolution_manager_->update_population_dynamics(
|
|
||||||
loss_velocity,
|
|
||||||
ema_loss_,
|
|
||||||
step_counter_,
|
|
||||||
config_.total_steps
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (step_counter_ % 50 == 0) {
|
|
||||||
backup_to_ram();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Emergency Brake - NaN/Inf Detection
|
||||||
|
if (std::isnan(current_loss_val) || !std::isfinite(current_loss_val)) {
|
||||||
|
Telemetry::get().error("emergency_brake_nan",
|
||||||
|
"NaN/Inf loss detected in step " +
|
||||||
|
std::to_string(step_counter_));
|
||||||
|
handle_rollback();
|
||||||
return loss;
|
return loss;
|
||||||
|
}
|
||||||
|
|
||||||
|
float loss_velocity = fitness_engine_.calculate_loss_signal(
|
||||||
|
current_loss_val, ema_loss_, config_.signal_mode);
|
||||||
|
last_loss_velocity_ = loss_velocity;
|
||||||
|
|
||||||
|
float progress = std::min(1.0f, static_cast<float>(step_counter_) /
|
||||||
|
std::max(1, config_.total_steps));
|
||||||
|
float grad_cv =
|
||||||
|
grad_norm_tracker_.get_std() / (grad_norm_tracker_.get_mean() + 1e-8f);
|
||||||
|
|
||||||
|
float csr_factor = 1.0f;
|
||||||
|
if (config_.csr_enabled) {
|
||||||
|
if (step_counter_ < config_.csr_warmup_steps) {
|
||||||
|
csr_factor = 0.0f;
|
||||||
|
} else {
|
||||||
|
float steps_since_warmup =
|
||||||
|
static_cast<float>(step_counter_ - config_.csr_warmup_steps);
|
||||||
|
csr_factor = std::min(
|
||||||
|
1.0f, steps_since_warmup /
|
||||||
|
std::max(1.0f, static_cast<float>(config_.csr_ramp_steps)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update spectral sensing rank
|
||||||
|
float spectral_alpha = 0.0f;
|
||||||
|
if (config_.grokking_coefficient > 0.0f && spectral_sensor_) {
|
||||||
|
if (step_counter_ % config_.spectral_frequency == 0 ||
|
||||||
|
last_spectral_rank_ == 0.0f) {
|
||||||
|
int param_idx = 0;
|
||||||
|
for (auto &group : param_groups()) {
|
||||||
|
for (auto &p : group.params()) {
|
||||||
|
if (p.dim() >= 2) {
|
||||||
|
std::string name = "layer_" + std::to_string(param_idx);
|
||||||
|
spectral_sensor_->track_layer(name, p);
|
||||||
|
}
|
||||||
|
param_idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_spectral_rank_ = spectral_sensor_->get_global_rank();
|
||||||
|
}
|
||||||
|
spectral_alpha = last_spectral_rank_;
|
||||||
|
}
|
||||||
|
|
||||||
|
float effective_alpha = spectral_alpha * csr_factor;
|
||||||
|
float kzm_damping = fitness_engine_.compute_kzm_damping(effective_alpha);
|
||||||
|
float stagnation_intensity =
|
||||||
|
std::min(1.0f, static_cast<float>(stagnation_counter_) / 500.0f);
|
||||||
|
float log_spectral_alpha = std::log(effective_alpha + 1e-6f);
|
||||||
|
|
||||||
|
// Call decide_update
|
||||||
|
auto actions = active_controller.decide_update(
|
||||||
|
layer_stats_, loss_velocity, progress, rollback_ema_, grad_cv,
|
||||||
|
log_spectral_alpha, stagnation_intensity, kzm_damping, loss_velocity);
|
||||||
|
|
||||||
|
// Bandit-style Early Stopping
|
||||||
|
if (step_counter_ % 5 == 0 && loss_velocity > 0.05f) {
|
||||||
|
Telemetry::get().warning(
|
||||||
|
"early_stopping_poor_controller",
|
||||||
|
"controller_id=" + std::to_string(active_controller.id) +
|
||||||
|
" velocity=" + std::to_string(loss_velocity));
|
||||||
|
evolution_manager_->steps_active = evolution_manager_->selection_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (torch::isnan(actions).any().item<bool>()) {
|
||||||
|
Telemetry::get().error("controller_nan_actions",
|
||||||
|
"NaN actions returned by controller ID " +
|
||||||
|
std::to_string(active_controller.id));
|
||||||
|
population_.kill(active_controller);
|
||||||
|
auto &new_controller = evolution_manager_->get_active_controller();
|
||||||
|
actions = torch::zeros_like(actions);
|
||||||
|
for (int i = 0; i < actions.size(0); ++i) {
|
||||||
|
actions[i][0] = 0.5f; // log_mult default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Action: Apply Updates
|
||||||
|
apply_parameter_updates(actions);
|
||||||
|
|
||||||
|
// 5. Evolution & Maintenance
|
||||||
|
if (current_loss_val > 0.0f) {
|
||||||
|
evolution_manager_->update_population_dynamics(
|
||||||
|
loss_velocity, ema_loss_, step_counter_, config_.total_steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step_counter_ % 50 == 0) {
|
||||||
|
backup_to_ram();
|
||||||
|
}
|
||||||
|
|
||||||
|
return loss;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::update_fitness(float loss) {
|
void FCESOptimizer::update_fitness(float loss) {
|
||||||
// 1. Divergence Safety
|
// 1. Divergence Safety
|
||||||
bool is_nan = std::isnan(loss) || !std::isfinite(loss);
|
bool is_nan = std::isnan(loss) || !std::isfinite(loss);
|
||||||
bool is_spike = (step_counter_ > 1) && (ema_loss_ > 0.0f) && (loss > config_.rollback_threshold * ema_loss_) && (ema_loss_ > 0.1f);
|
bool is_spike = (step_counter_ > 1) && (ema_loss_ > 0.0f) &&
|
||||||
if (is_nan || is_spike) {
|
(loss > config_.rollback_threshold * ema_loss_) &&
|
||||||
Telemetry::get().warning("divergence_detected", "loss=" + std::to_string(loss) + " ema=" + std::to_string(ema_loss_));
|
(ema_loss_ > 0.1f);
|
||||||
handle_rollback();
|
if (is_nan || is_spike) {
|
||||||
return;
|
Telemetry::get().warning("divergence_detected",
|
||||||
}
|
"loss=" + std::to_string(loss) +
|
||||||
|
" ema=" + std::to_string(ema_loss_));
|
||||||
|
handle_rollback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (step_counter_ == 1 || ema_loss_ == 0.0f) {
|
if (step_counter_ == 1 || ema_loss_ == 0.0f) {
|
||||||
ema_loss_ = loss;
|
ema_loss_ = loss;
|
||||||
last_step_loss_ = loss;
|
|
||||||
last_sparsity_ = calculate_sparsity();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Metric Calculation
|
|
||||||
float train_adv = ema_loss_ - loss;
|
|
||||||
float val_adv = 0.0f;
|
|
||||||
float current_sparsity = calculate_sparsity();
|
|
||||||
float sparsity_delta = current_sparsity - last_sparsity_;
|
|
||||||
float consistency_gap = std::max(0.0f, train_adv - val_adv);
|
|
||||||
|
|
||||||
float grad_std = grad_norm_tracker_.get_std();
|
|
||||||
float grad_mean = grad_norm_tracker_.get_mean();
|
|
||||||
float grad_cv = grad_std / (grad_mean + 1e-8f);
|
|
||||||
|
|
||||||
float raw_rank = (spectral_sensor_) ? spectral_sensor_->get_global_rank() : 0.0f;
|
|
||||||
float csr_factor = 1.0f;
|
|
||||||
if (config_.csr_enabled) {
|
|
||||||
if (step_counter_ < config_.csr_warmup_steps) {
|
|
||||||
csr_factor = 0.0f;
|
|
||||||
} else {
|
|
||||||
float steps_since_warmup = static_cast<float>(step_counter_ - config_.csr_warmup_steps);
|
|
||||||
csr_factor = std::min(1.0f, steps_since_warmup / std::max(1.0f, static_cast<float>(config_.csr_ramp_steps)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float effective_rank = config_.csr_enabled ? raw_rank * csr_factor : raw_rank;
|
|
||||||
|
|
||||||
FitnessMetrics metrics;
|
|
||||||
metrics.training_advantage = train_adv;
|
|
||||||
metrics.validation_advantage = val_adv;
|
|
||||||
metrics.grad_cv = grad_cv;
|
|
||||||
metrics.sparsity_delta = sparsity_delta;
|
|
||||||
metrics.consistency_gap = consistency_gap;
|
|
||||||
metrics.stable_rank = effective_rank;
|
|
||||||
|
|
||||||
// 3. Fuzzy Evaluation
|
|
||||||
float final_fitness = fitness_evaluator_.evaluate(metrics);
|
|
||||||
|
|
||||||
// 4. State Update
|
|
||||||
ema_loss_ = 0.95f * ema_loss_ + 0.05f * loss;
|
|
||||||
last_step_loss_ = loss;
|
last_step_loss_ = loss;
|
||||||
last_sparsity_ = current_sparsity;
|
last_sparsity_ = calculate_sparsity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Stagnation logic
|
// 2. Metric Calculation
|
||||||
if (loss < best_loss_window_ * 0.995f) {
|
float train_adv = ema_loss_ - loss;
|
||||||
best_loss_window_ = loss;
|
float val_adv = 0.0f;
|
||||||
stagnation_counter_ = 0;
|
float current_sparsity = calculate_sparsity();
|
||||||
|
float sparsity_delta = current_sparsity - last_sparsity_;
|
||||||
|
float consistency_gap = std::max(0.0f, train_adv - val_adv);
|
||||||
|
|
||||||
|
float grad_std = grad_norm_tracker_.get_std();
|
||||||
|
float grad_mean = grad_norm_tracker_.get_mean();
|
||||||
|
float grad_cv = grad_std / (grad_mean + 1e-8f);
|
||||||
|
|
||||||
|
float raw_rank =
|
||||||
|
(spectral_sensor_) ? spectral_sensor_->get_global_rank() : 0.0f;
|
||||||
|
float csr_factor = 1.0f;
|
||||||
|
if (config_.csr_enabled) {
|
||||||
|
if (step_counter_ < config_.csr_warmup_steps) {
|
||||||
|
csr_factor = 0.0f;
|
||||||
} else {
|
} else {
|
||||||
stagnation_counter_++;
|
float steps_since_warmup =
|
||||||
|
static_cast<float>(step_counter_ - config_.csr_warmup_steps);
|
||||||
|
csr_factor = std::min(
|
||||||
|
1.0f, steps_since_warmup /
|
||||||
|
std::max(1.0f, static_cast<float>(config_.csr_ramp_steps)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
float effective_rank = config_.csr_enabled ? raw_rank * csr_factor : raw_rank;
|
||||||
|
|
||||||
// 5. Apply to Population
|
FitnessMetrics metrics;
|
||||||
auto& active_controller = evolution_manager_->get_active_controller();
|
metrics.training_advantage = train_adv;
|
||||||
population_.update_controller_fitness(active_controller, final_fitness);
|
metrics.validation_advantage = val_adv;
|
||||||
|
metrics.grad_cv = grad_cv;
|
||||||
|
metrics.sparsity_delta = sparsity_delta;
|
||||||
|
metrics.consistency_gap = consistency_gap;
|
||||||
|
metrics.stable_rank = effective_rank;
|
||||||
|
|
||||||
Telemetry::get().info("fitness_calculated",
|
// 3. Fuzzy Evaluation
|
||||||
"loss=" + std::to_string(loss) +
|
float final_fitness = fitness_evaluator_.evaluate(metrics);
|
||||||
" ema_loss=" + std::to_string(ema_loss_) +
|
|
||||||
" fitness=" + std::to_string(final_fitness));
|
// 4. State Update
|
||||||
|
ema_loss_ = 0.95f * ema_loss_ + 0.05f * loss;
|
||||||
|
last_step_loss_ = loss;
|
||||||
|
last_sparsity_ = current_sparsity;
|
||||||
|
|
||||||
|
// Stagnation logic
|
||||||
|
if (loss < best_loss_window_ * 0.995f) {
|
||||||
|
best_loss_window_ = loss;
|
||||||
|
stagnation_counter_ = 0;
|
||||||
|
} else {
|
||||||
|
stagnation_counter_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Apply to Population
|
||||||
|
auto &active_controller = evolution_manager_->get_active_controller();
|
||||||
|
population_.update_controller_fitness(active_controller, final_fitness);
|
||||||
|
|
||||||
|
Telemetry::get().info("fitness_calculated",
|
||||||
|
"loss=" + std::to_string(loss) +
|
||||||
|
" ema_loss=" + std::to_string(ema_loss_) +
|
||||||
|
" fitness=" + std::to_string(final_fitness));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::backup_to_ram() {
|
void FCESOptimizer::backup_to_ram() {
|
||||||
ram_backup_.clear();
|
ram_backup_.clear();
|
||||||
for (auto& group : param_groups()) {
|
for (auto &group : param_groups()) {
|
||||||
for (auto& p : group.params()) {
|
for (auto &p : group.params()) {
|
||||||
ram_backup_.push_back(p.data().clone().cpu());
|
ram_backup_.push_back(p.data().clone().cpu());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::restore_from_ram() {
|
void FCESOptimizer::restore_from_ram() {
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (auto& group : param_groups()) {
|
for (auto &group : param_groups()) {
|
||||||
for (auto& p : group.params()) {
|
for (auto &p : group.params()) {
|
||||||
if (idx < static_cast<int>(ram_backup_.size())) {
|
if (idx < static_cast<int>(ram_backup_.size())) {
|
||||||
p.data().copy_(ram_backup_[idx].to(p.device()));
|
p.data().copy_(ram_backup_[idx].to(p.device()));
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float FCESOptimizer::calculate_sparsity() const {
|
float FCESOptimizer::calculate_sparsity() const {
|
||||||
int64_t total = 0, zeros = 0;
|
int64_t total = 0, zeros = 0;
|
||||||
for (const auto& group : param_groups()) {
|
for (const auto &group : param_groups()) {
|
||||||
for (const auto& p : group.params()) {
|
for (const auto &p : group.params()) {
|
||||||
total += p.numel();
|
total += p.numel();
|
||||||
zeros += (p.data().abs() < 1e-5f).sum().item<int64_t>();
|
zeros += (p.data().abs() < 1e-5f).sum().item<int64_t>();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return (total > 0) ? static_cast<float>(zeros) / total : 0.0f;
|
}
|
||||||
|
return (total > 0) ? static_cast<float>(zeros) / total : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::gather_stats() {
|
void FCESOptimizer::gather_stats() {
|
||||||
layer_stats_.clear();
|
layer_stats_.clear();
|
||||||
param_group_mapping_.clear();
|
param_group_mapping_.clear();
|
||||||
|
|
||||||
int param_idx = 0;
|
int param_idx = 0;
|
||||||
bool has_nan_or_inf = false;
|
bool has_nan_or_inf = false;
|
||||||
float max_grad_norm = 0.0f;
|
float max_grad_norm = 0.0f;
|
||||||
|
|
||||||
for (auto& group : param_groups()) {
|
for (auto &group : param_groups()) {
|
||||||
for (auto& p : group.params()) {
|
for (auto &p : group.params()) {
|
||||||
if (!p.grad().defined()) {
|
if (!p.grad().defined()) {
|
||||||
param_group_mapping_.push_back(-1);
|
param_group_mapping_.push_back(-1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto grad = p.grad();
|
auto grad = p.grad();
|
||||||
if (torch::isnan(grad).any().item<bool>() || torch::isinf(grad).any().item<bool>()) {
|
if (torch::isnan(grad).any().item<bool>() ||
|
||||||
has_nan_or_inf = true;
|
torch::isinf(grad).any().item<bool>()) {
|
||||||
}
|
has_nan_or_inf = true;
|
||||||
|
}
|
||||||
|
|
||||||
float grad_norm = grad.norm().item<float>();
|
float grad_norm = grad.norm().item<float>();
|
||||||
if (std::isnan(grad_norm) || !std::isfinite(grad_norm)) {
|
if (std::isnan(grad_norm) || !std::isfinite(grad_norm)) {
|
||||||
has_nan_or_inf = true;
|
has_nan_or_inf = true;
|
||||||
grad_norm = 0.0f;
|
grad_norm = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grad_norm > max_grad_norm) {
|
if (grad_norm > max_grad_norm) {
|
||||||
max_grad_norm = grad_norm;
|
max_grad_norm = grad_norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t total_elements = grad.numel();
|
int64_t total_elements = grad.numel();
|
||||||
int64_t zeros = (grad.abs() < 1e-5f).sum().item<int64_t>();
|
int64_t zeros = (grad.abs() < 1e-5f).sum().item<int64_t>();
|
||||||
float sparsity = (total_elements > 0) ? static_cast<float>(zeros) / total_elements : 0.0f;
|
float sparsity = (total_elements > 0)
|
||||||
|
? static_cast<float>(zeros) / total_elements
|
||||||
|
: 0.0f;
|
||||||
|
|
||||||
int layer_type = classify_layer_by_shape(p);
|
int layer_type = classify_layer_by_shape(p);
|
||||||
int group_idx = static_cast<int>(layer_stats_.size());
|
int group_idx = static_cast<int>(layer_stats_.size());
|
||||||
layer_stats_.push_back({grad_norm, sparsity, static_cast<float>(layer_type)});
|
layer_stats_.push_back(
|
||||||
param_group_mapping_.push_back(group_idx);
|
{grad_norm, sparsity, static_cast<float>(layer_type)});
|
||||||
|
param_group_mapping_.push_back(group_idx);
|
||||||
|
|
||||||
if (spectral_sensor_ && p.dim() >= 2) {
|
if (spectral_sensor_ && p.dim() >= 2) {
|
||||||
std::string name = "layer_" + std::to_string(param_idx);
|
std::string name = "layer_" + std::to_string(param_idx);
|
||||||
spectral_sensor_->track_layer(name, p);
|
spectral_sensor_->track_layer(name, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
param_idx++;
|
param_idx++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (has_nan_or_inf) {
|
if (has_nan_or_inf) {
|
||||||
Telemetry::get().error("poisoned_gradients_detected",
|
Telemetry::get().error("poisoned_gradients_detected",
|
||||||
"NaN/Inf detected in gradients during step " + std::to_string(step_counter_));
|
"NaN/Inf detected in gradients during step " +
|
||||||
handle_rollback();
|
std::to_string(step_counter_));
|
||||||
return;
|
handle_rollback();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (step_counter_ == 1 && max_grad_norm > 1.0f) {
|
if (step_counter_ == 1 && max_grad_norm > 1.0f) {
|
||||||
float safe_lr = 0.01f / (max_grad_norm + 1e-8f);
|
float safe_lr = 0.01f / (max_grad_norm + 1e-8f);
|
||||||
for (auto& group : param_groups()) {
|
for (auto &group : param_groups()) {
|
||||||
if (group.options().get_lr() > safe_lr) {
|
if (group.options().get_lr() > safe_lr) {
|
||||||
Telemetry::get().info("auto_calibration_throttled_lr",
|
Telemetry::get().info(
|
||||||
"old=" + std::to_string(group.options().get_lr()) + " new=" + std::to_string(safe_lr));
|
"auto_calibration_throttled_lr",
|
||||||
group.options().set_lr(safe_lr);
|
"old=" + std::to_string(group.options().get_lr()) +
|
||||||
config_.lr = safe_lr;
|
" new=" + std::to_string(safe_lr));
|
||||||
}
|
group.options().set_lr(safe_lr);
|
||||||
}
|
config_.lr = safe_lr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!layer_stats_.empty()) {
|
if (!layer_stats_.empty()) {
|
||||||
float first_grad_norm = layer_stats_[0][0];
|
float first_grad_norm = layer_stats_[0][0];
|
||||||
grad_norm_tracker_.update(first_grad_norm);
|
grad_norm_tracker_.update(first_grad_norm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::apply_parameter_updates(const torch::Tensor& actions) {
|
void FCESOptimizer::apply_parameter_updates(const torch::Tensor &actions) {
|
||||||
int param_idx = 0;
|
int param_idx = 0;
|
||||||
float parasitic_accum = 0.0f;
|
float parasitic_accum = 0.0f;
|
||||||
int count_updated = 0;
|
int count_updated = 0;
|
||||||
|
|
||||||
auto& active_controller = evolution_manager_->get_active_controller();
|
auto &active_controller = evolution_manager_->get_active_controller();
|
||||||
|
|
||||||
for (auto& group : param_groups()) {
|
for (auto &group : param_groups()) {
|
||||||
float lr = static_cast<float>(group.options().get_lr());
|
float lr = static_cast<float>(group.options().get_lr());
|
||||||
float wd = config_.weight_decay;
|
float wd = config_.weight_decay;
|
||||||
|
|
||||||
for (auto& p : group.params()) {
|
for (auto &p : group.params()) {
|
||||||
if (!p.grad().defined()) {
|
if (!p.grad().defined()) {
|
||||||
param_idx++;
|
param_idx++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int g_idx = param_group_mapping_[param_idx];
|
int g_idx = param_group_mapping_[param_idx];
|
||||||
if (g_idx < 0 || g_idx >= actions.size(0)) {
|
if (g_idx < 0 || g_idx >= actions.size(0)) {
|
||||||
param_idx++;
|
param_idx++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float mult = actions[g_idx][0].item<float>();
|
float mult = actions[g_idx][0].item<float>();
|
||||||
float sign_gate = actions[g_idx][1].item<float>();
|
float sign_gate = actions[g_idx][1].item<float>();
|
||||||
float wd_mult = (actions.size(1) > 2) ? actions[g_idx][2].item<float>() : 1.0f;
|
float wd_mult =
|
||||||
|
(actions.size(1) > 2) ? actions[g_idx][2].item<float>() : 1.0f;
|
||||||
|
|
||||||
bool use_sign = sign_gate > 0.0f;
|
bool use_sign = sign_gate > 0.0f;
|
||||||
if (config_.ablation_mode == "force_sign") {
|
if (config_.ablation_mode == "force_sign") {
|
||||||
use_sign = true;
|
use_sign = true;
|
||||||
} else if (config_.ablation_mode == "force_grad") {
|
} else if (config_.ablation_mode == "force_grad") {
|
||||||
use_sign = false;
|
use_sign = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wd > 0.0f) {
|
if (wd > 0.0f) {
|
||||||
float effective_wd = wd;
|
float effective_wd = wd;
|
||||||
if (config_.adaptive_wd) {
|
if (config_.adaptive_wd) {
|
||||||
effective_wd *= wd_mult;
|
effective_wd *= wd_mult;
|
||||||
}
|
|
||||||
p.data().mul_(1.0f - lr * effective_wd);
|
|
||||||
}
|
|
||||||
|
|
||||||
torch::Tensor update_vec = use_sign ? torch::sign(p.grad()) : p.grad();
|
|
||||||
torch::Tensor update = -lr * mult * update_vec;
|
|
||||||
|
|
||||||
update = apply_trust_clipping(p, update, config_.trust_region_clip);
|
|
||||||
p.data().add_(update);
|
|
||||||
|
|
||||||
if (config_.parasitic_mode) {
|
|
||||||
parasitic_accum += calculate_parasitic_reward(p, mult, grad_norm_tracker_);
|
|
||||||
}
|
|
||||||
|
|
||||||
param_idx++;
|
|
||||||
count_updated++;
|
|
||||||
}
|
}
|
||||||
}
|
p.data().mul_(1.0f - lr * effective_wd);
|
||||||
|
}
|
||||||
|
|
||||||
if (config_.parasitic_mode && count_updated > 0) {
|
torch::Tensor update_vec = use_sign ? torch::sign(p.grad()) : p.grad();
|
||||||
float reward = parasitic_accum / static_cast<float>(count_updated);
|
torch::Tensor update = -lr * mult * update_vec;
|
||||||
population_.update_controller_fitness(active_controller, reward * 10.0f, false);
|
|
||||||
|
update = apply_trust_clipping(p, update, config_.trust_region_clip);
|
||||||
|
p.data().add_(update);
|
||||||
|
|
||||||
|
if (config_.parasitic_mode) {
|
||||||
|
parasitic_accum +=
|
||||||
|
calculate_parasitic_reward(p, mult, grad_norm_tracker_);
|
||||||
|
}
|
||||||
|
|
||||||
|
param_idx++;
|
||||||
|
count_updated++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_.parasitic_mode && count_updated > 0) {
|
||||||
|
float reward = parasitic_accum / static_cast<float>(count_updated);
|
||||||
|
population_.update_controller_fitness(active_controller, reward * 10.0f,
|
||||||
|
false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FCESOptimizer::handle_rollback() {
|
void FCESOptimizer::handle_rollback() {
|
||||||
restore_from_ram();
|
restore_from_ram();
|
||||||
population_.calm_down();
|
population_.calm_down();
|
||||||
rollback_ema_ = 0.9f * rollback_ema_ + 0.1f;
|
rollback_ema_ = 0.9f * rollback_ema_ + 0.1f;
|
||||||
|
|
||||||
ema_loss_ = 0.0f;
|
ema_loss_ = 0.0f;
|
||||||
last_step_loss_ = 0.0f;
|
last_step_loss_ = 0.0f;
|
||||||
grad_norm_tracker_.reset();
|
grad_norm_tracker_.reset();
|
||||||
zero_grad();
|
zero_grad();
|
||||||
|
|
||||||
Telemetry::get().warning("hard_reset_executed", "rollback_sanitization");
|
Telemetry::get().warning("hard_reset_executed", "rollback_sanitization");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -1,97 +1,103 @@
|
|||||||
#include "fces/oscillation.hpp"
|
#include "fces/oscillation.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
void OscillationDetector::update(float loss) {
|
void OscillationDetector::update(float loss) {
|
||||||
loss_history_.push_back(loss);
|
loss_history_.push_back(loss);
|
||||||
if (static_cast<int>(loss_history_.size()) > WINDOW_SIZE) {
|
if (static_cast<int>(loss_history_.size()) > WINDOW_SIZE) {
|
||||||
loss_history_.erase(loss_history_.begin());
|
loss_history_.erase(loss_history_.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OscillationDetector::detect() const {
|
bool OscillationDetector::detect() const {
|
||||||
return get_score() > POWER_THRESHOLD;
|
return get_score() > POWER_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
float OscillationDetector::get_score() const {
|
float OscillationDetector::get_score() const {
|
||||||
if (static_cast<int>(loss_history_.size()) < WINDOW_SIZE) return 0.0f;
|
if (static_cast<int>(loss_history_.size()) < WINDOW_SIZE)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
auto detrended = detrend(loss_history_);
|
auto detrended = detrend(loss_history_);
|
||||||
auto power = compute_power_spectrum(detrended);
|
auto power = compute_power_spectrum(detrended);
|
||||||
|
|
||||||
// Sum power in oscillation bands (periods 4-16)
|
// Sum power in oscillation bands (periods 4-16)
|
||||||
float osc_power = 0.0f;
|
float osc_power = 0.0f;
|
||||||
float total_power = 0.0f;
|
float total_power = 0.0f;
|
||||||
int n = static_cast<int>(power.size());
|
int n = static_cast<int>(power.size());
|
||||||
for (int i = 1; i < n; ++i) {
|
for (int i = 1; i < n; ++i) {
|
||||||
total_power += power[i];
|
total_power += power[i];
|
||||||
int period = n / i;
|
int period = n / i;
|
||||||
if (period >= MIN_PERIOD && period <= MAX_PERIOD) {
|
if (period >= MIN_PERIOD && period <= MAX_PERIOD) {
|
||||||
osc_power += power[i];
|
osc_power += power[i];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (total_power < 1e-8f) return 0.0f;
|
if (total_power < 1e-8f)
|
||||||
return osc_power / total_power;
|
return 0.0f;
|
||||||
|
return osc_power / total_power;
|
||||||
}
|
}
|
||||||
|
|
||||||
float OscillationDetector::get_variance_50() const {
|
float OscillationDetector::get_variance_50() const {
|
||||||
if (loss_history_.size() < 50) return 0.0f;
|
if (loss_history_.size() < 50)
|
||||||
auto start = loss_history_.end() - 50;
|
return 0.0f;
|
||||||
float mean = std::accumulate(start, loss_history_.end(), 0.0f) / 50.0f;
|
auto start = loss_history_.end() - 50;
|
||||||
float var = 0.0f;
|
float mean = std::accumulate(start, loss_history_.end(), 0.0f) / 50.0f;
|
||||||
for (auto it = start; it != loss_history_.end(); ++it) {
|
float var = 0.0f;
|
||||||
float d = *it - mean;
|
for (auto it = start; it != loss_history_.end(); ++it) {
|
||||||
var += d * d;
|
float d = *it - mean;
|
||||||
}
|
var += d * d;
|
||||||
return var / 50.0f;
|
}
|
||||||
|
return var / 50.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OscillationDetector::reset() {
|
void OscillationDetector::reset() { loss_history_.clear(); }
|
||||||
loss_history_.clear();
|
|
||||||
|
std::vector<float>
|
||||||
|
OscillationDetector::detrend(const std::vector<float> &signal) {
|
||||||
|
int n = static_cast<int>(signal.size());
|
||||||
|
if (n < 2)
|
||||||
|
return signal;
|
||||||
|
|
||||||
|
// Remove linear trend via least squares
|
||||||
|
float sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
sum_x += i;
|
||||||
|
sum_y += signal[i];
|
||||||
|
sum_xy += i * signal[i];
|
||||||
|
sum_xx += i * i;
|
||||||
|
}
|
||||||
|
float slope =
|
||||||
|
(n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x + 1e-8f);
|
||||||
|
float intercept = (sum_y - slope * sum_x) / n;
|
||||||
|
|
||||||
|
std::vector<float> result(n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
result[i] = signal[i] - (slope * i + intercept);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> OscillationDetector::detrend(const std::vector<float>& signal) {
|
std::vector<float>
|
||||||
int n = static_cast<int>(signal.size());
|
OscillationDetector::compute_power_spectrum(const std::vector<float> &signal) {
|
||||||
if (n < 2) return signal;
|
// Simple DFT (for WINDOW_SIZE=64, this is fast enough; upgrade to FFT if
|
||||||
|
// needed)
|
||||||
|
int n = static_cast<int>(signal.size());
|
||||||
|
int half = n / 2;
|
||||||
|
std::vector<float> power(half);
|
||||||
|
|
||||||
// Remove linear trend via least squares
|
for (int k = 0; k < half; ++k) {
|
||||||
float sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
|
float re = 0.0f, im = 0.0f;
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int t = 0; t < n; ++t) {
|
||||||
sum_x += i;
|
float angle = 2.0f * 3.14159265358979f * k * t / n;
|
||||||
sum_y += signal[i];
|
re += signal[t] * std::cos(angle);
|
||||||
sum_xy += i * signal[i];
|
im -= signal[t] * std::sin(angle);
|
||||||
sum_xx += i * i;
|
|
||||||
}
|
}
|
||||||
float slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x + 1e-8f);
|
power[k] = re * re + im * im;
|
||||||
float intercept = (sum_y - slope * sum_x) / n;
|
}
|
||||||
|
return power;
|
||||||
std::vector<float> result(n);
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
result[i] = signal[i] - (slope * i + intercept);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> OscillationDetector::compute_power_spectrum(const std::vector<float>& signal) {
|
} // namespace fces
|
||||||
// Simple DFT (for WINDOW_SIZE=64, this is fast enough; upgrade to FFT if needed)
|
|
||||||
int n = static_cast<int>(signal.size());
|
|
||||||
int half = n / 2;
|
|
||||||
std::vector<float> power(half);
|
|
||||||
|
|
||||||
for (int k = 0; k < half; ++k) {
|
|
||||||
float re = 0.0f, im = 0.0f;
|
|
||||||
for (int t = 0; t < n; ++t) {
|
|
||||||
float angle = 2.0f * 3.14159265358979f * k * t / n;
|
|
||||||
re += signal[t] * std::cos(angle);
|
|
||||||
im -= signal[t] * std::sin(angle);
|
|
||||||
}
|
|
||||||
power[k] = re * re + im * im;
|
|
||||||
}
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fces
|
|
||||||
|
|||||||
1059
src/population.cpp
1059
src/population.cpp
File diff suppressed because it is too large
Load Diff
@@ -3,39 +3,40 @@
|
|||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
SpectralSensor::SpectralSensor(torch::nn::Module& /*model*/) {}
|
SpectralSensor::SpectralSensor(torch::nn::Module & /*model*/) {}
|
||||||
|
|
||||||
void SpectralSensor::track_layer(const std::string& name, const torch::Tensor& weight) {
|
void SpectralSensor::track_layer(const std::string &name,
|
||||||
if (weight.dim() >= 2) {
|
const torch::Tensor &weight) {
|
||||||
layer_ranks_[name] = compute_effective_rank(weight);
|
if (weight.dim() >= 2) {
|
||||||
}
|
layer_ranks_[name] = compute_effective_rank(weight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float SpectralSensor::get_global_rank() const {
|
float SpectralSensor::get_global_rank() const {
|
||||||
if (layer_ranks_.empty()) return 0.0f;
|
if (layer_ranks_.empty())
|
||||||
float sum = 0.0f;
|
return 0.0f;
|
||||||
for (const auto& [_, rank] : layer_ranks_) {
|
float sum = 0.0f;
|
||||||
sum += rank;
|
for (const auto &[_, rank] : layer_ranks_) {
|
||||||
}
|
sum += rank;
|
||||||
return sum / static_cast<float>(layer_ranks_.size());
|
}
|
||||||
|
return sum / static_cast<float>(layer_ranks_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectralSensor::reset() {
|
void SpectralSensor::reset() { layer_ranks_.clear(); }
|
||||||
layer_ranks_.clear();
|
|
||||||
|
float SpectralSensor::compute_effective_rank(const torch::Tensor &weight) {
|
||||||
|
// SVD-based effective rank (Shannon entropy of normalized singular values)
|
||||||
|
auto svd_result = torch::svd(weight.to(torch::kFloat32));
|
||||||
|
auto svd = std::get<1>(svd_result);
|
||||||
|
auto s = svd / svd.sum();
|
||||||
|
auto log_s = (s + 1e-10f).log();
|
||||||
|
float entropy = -s.mul(log_s).sum().item<float>();
|
||||||
|
return std::exp(entropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
float SpectralSensor::compute_effective_rank(const torch::Tensor& weight) {
|
float SpectralController::compute_alpha(float global_rank,
|
||||||
// SVD-based effective rank (Shannon entropy of normalized singular values)
|
float grokking_coefficient) const {
|
||||||
auto svd_result = torch::svd(weight.to(torch::kFloat32));
|
return global_rank * grokking_coefficient;
|
||||||
auto svd = std::get<1>(svd_result);
|
|
||||||
auto s = svd / svd.sum();
|
|
||||||
auto log_s = (s + 1e-10f).log();
|
|
||||||
float entropy = -s.mul(log_s).sum().item<float>();
|
|
||||||
return std::exp(entropy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float SpectralController::compute_alpha(float global_rank, float grokking_coefficient) const {
|
} // namespace fces
|
||||||
return global_rank * grokking_coefficient;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fces
|
|
||||||
|
|||||||
@@ -1,34 +1,37 @@
|
|||||||
#include "fces/telemetry.hpp"
|
#include "fces/telemetry.hpp"
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace fces {
|
namespace fces {
|
||||||
|
|
||||||
Telemetry& Telemetry::get() {
|
Telemetry &Telemetry::get() {
|
||||||
static Telemetry instance;
|
static Telemetry instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Telemetry::info(const std::string& event, const std::string& detail) {
|
void Telemetry::info(const std::string &event, const std::string &detail) {
|
||||||
std::cout << "[INFO] " << event;
|
std::cout << "[INFO] " << event;
|
||||||
if (!detail.empty()) std::cout << " | " << detail;
|
if (!detail.empty())
|
||||||
std::cout << std::endl;
|
std::cout << " | " << detail;
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Telemetry::warning(const std::string& event, const std::string& detail) {
|
void Telemetry::warning(const std::string &event, const std::string &detail) {
|
||||||
std::cerr << "[WARN] " << event;
|
std::cerr << "[WARN] " << event;
|
||||||
if (!detail.empty()) std::cerr << " | " << detail;
|
if (!detail.empty())
|
||||||
std::cerr << std::endl;
|
std::cerr << " | " << detail;
|
||||||
|
std::cerr << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Telemetry::error(const std::string& event, const std::string& detail) {
|
void Telemetry::error(const std::string &event, const std::string &detail) {
|
||||||
std::cerr << "[ERROR] " << event;
|
std::cerr << "[ERROR] " << event;
|
||||||
if (!detail.empty()) std::cerr << " | " << detail;
|
if (!detail.empty())
|
||||||
std::cerr << std::endl;
|
std::cerr << " | " << detail;
|
||||||
|
std::cerr << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Telemetry::push_to_remote() {
|
void Telemetry::push_to_remote() {
|
||||||
// TODO: Implement telemetry push (Git sync, file export, etc.)
|
// TODO: Implement telemetry push (Git sync, file export, etc.)
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fces
|
} // namespace fces
|
||||||
|
|||||||
@@ -1,58 +1,59 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
#include "fces/controller.hpp"
|
#include "fces/controller.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
TEST(ControllerTest, Construction) {
|
TEST(ControllerTest, Construction) {
|
||||||
FuzzyController ctrl;
|
FuzzyController ctrl;
|
||||||
EXPECT_GT(ctrl.id, 0u);
|
EXPECT_GT(ctrl.id, 0u);
|
||||||
EXPECT_EQ(ctrl.fitness, 0.0f);
|
EXPECT_EQ(ctrl.fitness, 0.0f);
|
||||||
EXPECT_EQ(ctrl.origin, "random");
|
EXPECT_EQ(ctrl.origin, "random");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, GenomeSize) {
|
TEST(ControllerTest, GenomeSize) {
|
||||||
FuzzyController ctrl;
|
FuzzyController ctrl;
|
||||||
EXPECT_EQ(ctrl.genome.weights.size(), static_cast<size_t>(GENOME_SIZE));
|
EXPECT_EQ(ctrl.genome.weights.size(), static_cast<size_t>(GENOME_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, Mutation) {
|
TEST(ControllerTest, Mutation) {
|
||||||
FuzzyController parent;
|
FuzzyController parent;
|
||||||
auto child = parent.mutate(1.0f);
|
auto child = parent.mutate(1.0f);
|
||||||
EXPECT_NE(child.id, parent.id);
|
EXPECT_NE(child.id, parent.id);
|
||||||
EXPECT_EQ(child.origin, "mutation");
|
EXPECT_EQ(child.origin, "mutation");
|
||||||
// Child should differ from parent
|
// Child should differ from parent
|
||||||
bool differs = false;
|
bool differs = false;
|
||||||
for (size_t i = 0; i < parent.genome.weights.size(); ++i) {
|
for (size_t i = 0; i < parent.genome.weights.size(); ++i) {
|
||||||
if (parent.genome.weights[i] != child.genome.weights[i]) {
|
if (parent.genome.weights[i] != child.genome.weights[i]) {
|
||||||
differs = true;
|
differs = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(differs);
|
}
|
||||||
|
EXPECT_TRUE(differs);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, Crossover) {
|
TEST(ControllerTest, Crossover) {
|
||||||
FuzzyController a, b;
|
FuzzyController a, b;
|
||||||
auto child = a.crossover(b);
|
auto child = a.crossover(b);
|
||||||
EXPECT_EQ(child.origin, "crossover");
|
EXPECT_EQ(child.origin, "crossover");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, DecideUpdate) {
|
TEST(ControllerTest, DecideUpdate) {
|
||||||
FuzzyController ctrl;
|
FuzzyController ctrl;
|
||||||
std::vector<std::vector<float>> stats = {{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}};
|
std::vector<std::vector<float>> stats = {{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}};
|
||||||
auto actions = ctrl.decide_update(stats, 0.0f, 0.5f, 0.0f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f);
|
auto actions =
|
||||||
EXPECT_EQ(actions.size(0), 1);
|
ctrl.decide_update(stats, 0.0f, 0.5f, 0.0f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||||
EXPECT_EQ(actions.size(1), GENOME_OUTPUT_DIM);
|
EXPECT_EQ(actions.size(0), 1);
|
||||||
|
EXPECT_EQ(actions.size(1), GENOME_OUTPUT_DIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, OrthogonalChild) {
|
TEST(ControllerTest, OrthogonalChild) {
|
||||||
FuzzyController parent;
|
FuzzyController parent;
|
||||||
auto child = parent.create_orthogonal_child(1.0f);
|
auto child = parent.create_orthogonal_child(1.0f);
|
||||||
EXPECT_EQ(child.origin, "phoenix_rebirth");
|
EXPECT_EQ(child.origin, "phoenix_rebirth");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ControllerTest, BanachFission) {
|
TEST(ControllerTest, BanachFission) {
|
||||||
FuzzyController parent;
|
FuzzyController parent;
|
||||||
auto [plus, minus] = parent.banach_tarski_fission(1.0f);
|
auto [plus, minus] = parent.banach_tarski_fission(1.0f);
|
||||||
EXPECT_NE(plus.id, minus.id);
|
EXPECT_NE(plus.id, minus.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,34 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
#include "fces/fitness.hpp"
|
#include "fces/fitness.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
TEST(RunningStatsTest, BasicUpdate) {
|
TEST(RunningStatsTest, BasicUpdate) {
|
||||||
RunningStats stats;
|
RunningStats stats;
|
||||||
stats.update(1.0f);
|
stats.update(1.0f);
|
||||||
stats.update(2.0f);
|
stats.update(2.0f);
|
||||||
stats.update(3.0f);
|
stats.update(3.0f);
|
||||||
EXPECT_NEAR(stats.get_mean(), 2.0f, 1e-5f);
|
EXPECT_NEAR(stats.get_mean(), 2.0f, 1e-5f);
|
||||||
EXPECT_GT(stats.get_std(), 0.0f);
|
EXPECT_GT(stats.get_std(), 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RunningStatsTest, ZScore) {
|
TEST(RunningStatsTest, ZScore) {
|
||||||
RunningStats stats;
|
RunningStats stats;
|
||||||
for (int i = 0; i < 100; ++i) stats.update(static_cast<float>(i));
|
for (int i = 0; i < 100; ++i)
|
||||||
float z = stats.z_score(50.0f);
|
stats.update(static_cast<float>(i));
|
||||||
EXPECT_NEAR(z, 0.0f, 0.1f);
|
float z = stats.z_score(50.0f);
|
||||||
|
EXPECT_NEAR(z, 0.0f, 0.1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FitnessEngineTest, LossSignal) {
|
TEST(FitnessEngineTest, LossSignal) {
|
||||||
FitnessEngine engine;
|
FitnessEngine engine;
|
||||||
float sig = engine.calculate_loss_signal(1.0f, 2.0f, "relative");
|
float sig = engine.calculate_loss_signal(1.0f, 2.0f, "relative");
|
||||||
EXPECT_LT(sig, 0.0f); // Improving
|
EXPECT_LT(sig, 0.0f); // Improving
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FitnessEngineTest, KZMDamping) {
|
TEST(FitnessEngineTest, KZMDamping) {
|
||||||
FitnessEngine engine(0.1f);
|
FitnessEngine engine(0.1f);
|
||||||
float d = engine.compute_kzm_damping(5.0f);
|
float d = engine.compute_kzm_damping(5.0f);
|
||||||
EXPECT_GT(d, 0.0f);
|
EXPECT_GT(d, 0.0f);
|
||||||
EXPECT_LT(d, 1.0f);
|
EXPECT_LT(d, 1.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,45 @@
|
|||||||
|
#include "fces/optimizer.hpp"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <torch/torch.h>
|
#include <torch/torch.h>
|
||||||
#include "fces/optimizer.hpp"
|
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
TEST(OptimizerTest, Construction) {
|
TEST(OptimizerTest, Construction) {
|
||||||
auto model = torch::nn::Linear(10, 5);
|
auto model = torch::nn::Linear(10, 5);
|
||||||
std::vector<torch::Tensor> params;
|
std::vector<torch::Tensor> params;
|
||||||
for (auto& p : model->parameters()) params.push_back(p);
|
for (auto &p : model->parameters())
|
||||||
|
params.push_back(p);
|
||||||
|
|
||||||
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
||||||
EXPECT_EQ(opt.step_count(), 0);
|
EXPECT_EQ(opt.step_count(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OptimizerTest, StepUpdatesCounter) {
|
TEST(OptimizerTest, StepUpdatesCounter) {
|
||||||
auto model = torch::nn::Linear(10, 5);
|
auto model = torch::nn::Linear(10, 5);
|
||||||
std::vector<torch::Tensor> params;
|
std::vector<torch::Tensor> params;
|
||||||
for (auto& p : model->parameters()) params.push_back(p);
|
for (auto &p : model->parameters())
|
||||||
|
params.push_back(p);
|
||||||
|
|
||||||
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
FCESOptimizer opt(params, FCESConfig{}.set_lr(1e-3f));
|
||||||
|
|
||||||
// Simulate a training step
|
// Simulate a training step
|
||||||
auto x = torch::randn({2, 10});
|
auto x = torch::randn({2, 10});
|
||||||
auto y = model->forward(x);
|
auto y = model->forward(x);
|
||||||
auto loss = y.sum();
|
auto loss = y.sum();
|
||||||
loss.backward();
|
loss.backward();
|
||||||
opt.step();
|
opt.step();
|
||||||
|
|
||||||
EXPECT_EQ(opt.step_count(), 1);
|
EXPECT_EQ(opt.step_count(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OptimizerTest, UpdateFitness) {
|
TEST(OptimizerTest, UpdateFitness) {
|
||||||
auto model = torch::nn::Linear(10, 5);
|
auto model = torch::nn::Linear(10, 5);
|
||||||
std::vector<torch::Tensor> params;
|
std::vector<torch::Tensor> params;
|
||||||
for (auto& p : model->parameters()) params.push_back(p);
|
for (auto &p : model->parameters())
|
||||||
|
params.push_back(p);
|
||||||
|
|
||||||
FCESOptimizer opt(params);
|
FCESOptimizer opt(params);
|
||||||
opt.update_fitness(3.0f);
|
opt.update_fitness(3.0f);
|
||||||
opt.update_fitness(2.5f);
|
opt.update_fitness(2.5f);
|
||||||
// Should not crash
|
// Should not crash
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
#include "fces/population.hpp"
|
#include "fces/population.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
using namespace fces;
|
using namespace fces;
|
||||||
|
|
||||||
TEST(PopulationTest, Construction) {
|
TEST(PopulationTest, Construction) {
|
||||||
Population pop(50);
|
Population pop(50);
|
||||||
EXPECT_EQ(pop.size(), 50);
|
EXPECT_EQ(pop.size(), 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PopulationTest, DirectConstruction) {
|
TEST(PopulationTest, DirectConstruction) {
|
||||||
Population pop(200, 10000, EliteStrategy::Cumulative,
|
Population pop(200, 10000, EliteStrategy::Cumulative, false, false, false,
|
||||||
false, false, false, false, false, true);
|
false, false, true);
|
||||||
EXPECT_EQ(pop.size(), 1);
|
EXPECT_EQ(pop.size(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PopulationTest, GetBestActive) {
|
TEST(PopulationTest, GetBestActive) {
|
||||||
Population pop(10);
|
Population pop(10);
|
||||||
auto& best = pop.get_best_active();
|
auto &best = pop.get_best_active();
|
||||||
// Should not crash
|
// Should not crash
|
||||||
EXPECT_GE(best.id, 0u);
|
EXPECT_GE(best.id, 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PopulationTest, CalmDown) {
|
TEST(PopulationTest, CalmDown) {
|
||||||
Population pop(10);
|
Population pop(10);
|
||||||
pop.calm_down();
|
pop.calm_down();
|
||||||
EXPECT_LT(pop.global_sigma_modifier(), 1.0f);
|
EXPECT_LT(pop.global_sigma_modifier(), 1.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user