Program Listing for File neldermead.cc¶
↰ Return to documentation for file (environment/plasma/tec/neldermead.cc)
#include "lupnt/environment/plasma/tec/neldermead.h"
namespace pecsim {
NelderMead::NelderMead(std::function<double(Vec2d)> func) { f = func; }
NelderMead::NelderMead(std::function<double(Vec2d)> func, Vec2d x0, double step, bool debug) {
f = func;
set_initial_simplex(x0, step, debug);
}
void NelderMead::set_initial_simplex(Vec2d x0, double step, bool debug) {
// Initialize 2D simplex (3 vertices)
Vec2d x1 = {0, 0};
Vec2d x2 = {1, 0};
Vec2d x3 = {0.5, std::sqrt(3) / 2.0};
// Compute centroid
Vec2d centroid = {(x1[0] + x2[0] + x3[0]) / 3.0, (x1[1] + x2[1] + x3[1]) / 3.0};
// Offset points so that centroid is at x0
for (int i = 0; i < 2; ++i) {
x1[i] = x0[i] + step * (x1[i] - centroid[i]);
x2[i] = x0[i] + step * (x2[i] - centroid[i]);
x3[i] = x0[i] + step * (x3[i] - centroid[i]);
}
if (debug) {
std::cout << "Initial simplex vertices:\n"
<< " x1: (" << x1[0] << ", " << x1[1] << ")\n"
<< " x2: (" << x2[0] << ", " << x2[1] << ")\n"
<< " x3: (" << x3[0] << ", " << x3[1] << ")\n";
}
simplex[0] = {x1, f(x1)};
simplex[1] = {x2, f(x2)};
simplex[2] = {x3, f(x3)};
}
void NelderMead::set_initial_simplex(Vec2d x0, double f0, double step, bool debug) {
// Initialize 2D simplex (3 vertices)
Vec2d x1 = x0; // Start at x0
Vec2d x2 = x0 + Vec2d{step, 0}; // Step right from x0
Vec2d x3 = x0 + Vec2d{0, step}; // Step up from x0
if (debug) {
std::cout << "Initial simplex vertices:\n"
<< " x1: (" << x1[0] << ", " << x1[1] << ")\n"
<< " x2: (" << x2[0] << ", " << x2[1] << ")\n"
<< " x3: (" << x3[0] << ", " << x3[1] << ")\n";
}
simplex[0] = {x1, f0}; // use provided function value for x0
simplex[1] = {x2, f(x2)};
simplex[2] = {x3, f(x3)};
}
Vec2d NelderMead::minimize(int max_iters, double tol, double fval_tol, bool debug) {
if (debug) {
std::cout << "Starting Nelder-Mead optimization with initial points:\n";
for (const auto& vertex : simplex) {
std::cout << " Point: (" << vertex.x[0] << ", " << vertex.x[1] << ") fval: " << vertex.fval
<< "\n";
}
}
for (int iter = 0; iter < max_iters; ++iter) {
iter_ = iter;
// Sort simplex: best -> worst
std::sort(simplex.begin(), simplex.end(),
[](const SimplexVertex& a, const SimplexVertex& b) { return a.fval < b.fval; });
min_val_ = simplex[0].fval; // Update minimum value found
if (debug) {
std::cout << "Iteration " << iter << ": "
<< "Best: (" << simplex[0].x[0] << ", " << simplex[0].x[1] << ") "
<< "fval: " << simplex[0].fval << " | "
<< "Second: (" << simplex[1].x[0] << ", " << simplex[1].x[1] << ") "
<< "fval: " << simplex[1].fval << " | "
<< "Worst: (" << simplex[2].x[0] << ", " << simplex[2].x[1] << ") "
<< "fval: " << simplex[2].fval << "\n";
}
// Check convergence
double spread = std::abs(simplex[2].fval - simplex[0].fval);
if ((spread < tol) || (simplex[0].fval <= fval_tol)) return simplex[0].x;
Vec2d centroid = mean_point({simplex[0].x, simplex[1].x});
// Reflection
Vec2d xr = reflect(centroid, simplex[2].x);
double fr = f(xr);
if (fr < simplex[0].fval) {
// Expansion
Vec2d xe = expand(centroid, xr);
double fe = f(xe);
if (fe < fr)
simplex[2] = {xe, fe};
else
simplex[2] = {xr, fr};
} else if (fr < simplex[1].fval) {
simplex[2] = {xr, fr};
} else {
// Contraction
Vec2d xc = contract(centroid, simplex[2].x);
double fc = f(xc);
if (fc < simplex[2].fval)
simplex[2] = {xc, fc};
else
shrink();
}
}
return simplex[0].x;
}
Vec2d NelderMead::mean_point(const std::array<Vec2d, 2>& points) {
return {(points[0][0] + points[1][0]) / 2.0, (points[0][1] + points[1][1]) / 2.0};
}
Vec2d NelderMead::reflect(const Vec2d& c, const Vec2d& worst, double alpha) {
return {c[0] + alpha * (c[0] - worst[0]), c[1] + alpha * (c[1] - worst[1])};
}
Vec2d NelderMead::expand(const Vec2d& c, const Vec2d& reflected, double gamma) {
return {c[0] + gamma * (reflected[0] - c[0]), c[1] + gamma * (reflected[1] - c[1])};
}
Vec2d NelderMead::contract(const Vec2d& c, const Vec2d& worst, double rho) {
return {c[0] + rho * (worst[0] - c[0]), c[1] + rho * (worst[1] - c[1])};
}
void NelderMead::shrink(double sigma) {
for (int i = 1; i < 3; ++i) {
simplex[i].x[0] = simplex[0].x[0] + sigma * (simplex[i].x[0] - simplex[0].x[0]);
simplex[i].x[1] = simplex[0].x[1] + sigma * (simplex[i].x[1] - simplex[0].x[1]);
simplex[i].fval = f(simplex[i].x);
}
}
} // namespace pecsim