сейчас я пришлю тебе отрывок разговора с chat01ai ...

Criado em: 7 de dezembro de 2025

Respondido usando GPT-5.1 Thinking por Chat01

Pergunta

сейчас я пришлю тебе отрывок разговора с chat01ai где он сказал как мне исправить своё приложение так чтобы можно было отрисовать графики для научного руководителя, сейчас я просто пришлю тебе это для понимания контекста а потом в следующем сообщении скажу что от тебя требуется, итак контекст: ""И нужны какие-то графики с временем решения и точности от числа итераций" - требования научника касаемо реализации моего алгоритма глобального поиска, в моём управляемом приложении есть возможность выбора алгоритма для решения задачи нахождения оптимального положения захвата робота-манипулятора, мой алгоритм записан в основном .cpp файле моей dll библиотеки, код алгоритма: "
/// НАЧАЛО НЕ ИМЕЕТ СУЩЕСТВЕННОГО ЗНАЧЕНИЯ В ЭТОМ КОНТЕКСТЕ
static __declspec(noalias) void agp_run_branch_mpi(
const MortonND& map, const ManipCost& cost, const int maxIter, const float r, const bool adaptive, const float eps, const unsigned seed,
std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>>& H,
std::vector<float, boost::alignment::aligned_allocator<float, 16u>>& bestQ,
float& bestF, float& bestX, float& bestY, size_t& out_iterations, float& out_achieved_epsilon, const float M_prior = 1e-3f)
noexcept {
const int n = cost.n;
const int dim = n + (cost.variableLen ? n : 0);
const float dim_f = static_cast<float>(dim);
unsigned exchange_counter_500 = 0;
unsigned exchange_counter_T = 0;

text

alignas(16) float M_by_span[12];
int msi = 0;
while (msi < 12) {
M_by_span[msi++] = M_prior;
}
float Mmax = M_prior;

alignas(16) float q_local[32], phi[32], s_arr[32], c_arr[32], sum_s[32], sum_c[32], q_try[32];
bestQ.reserve(static_cast<size_t>(dim));
float x = 0.0f, y = 0.0f;
int no_improve = 0;

auto t_to_idx = [&](const float t) -> unsigned long long {
unsigned long long idx = static_cast<unsigned long long>(fmaf(t, static_cast<float>(map.scale), 0.0f));
return idx;
};

auto update_pockets_and_Mmax = [&](IntervalND* const I) {
const int k = I->span_level;
if (I->M > M_by_span[k]) M_by_span[k] = I->M;
if (M_by_span[k] > Mmax) Mmax = M_by_span[k];
};

const float a = 0.0f, b = 1.0f;

auto evalAt = [&](const float t) -> float {
map.map01ToPoint(t, q_local);
float f = cost(q_local, x, y);

text
if (f < bestF * 1.25f) { float acc = 0.0f; int ii = 0; while (ii < n) { acc = fmaf(q_local[ii], 1.0f, acc); phi[ii] = acc; ++ii; } FABE13_SINCOS(phi, s_arr, c_arr, n); float as = 0.0f, ac = 0.0f; int k = n - 1; while (k >= 0) { const float Lk = cost.variableLen ? q_local[n + k] : 1.0f; as = fmaf(Lk, s_arr[k], as); ac = fmaf(Lk, c_arr[k], ac); sum_s[k] = as; sum_c[k] = ac; --k; } const float dx = fmaf(x, 1.0f, -cost.targetX); const float dy = fmaf(y, 1.0f, -cost.targetY); const float dist = sqrtf(fmaf(dx, dx, dy * dy)) + 1.0e-8f; float eta = 0.125f; int stepI = 0; while (stepI < 3) { int i = 0;

#pragma loop ivdep
while (i < n) {
float gpen = 0.0f;
{
const float ai = fabsf(q_local[i]);
const float v = ai - cost.minTheta;
if (v > 0.0f) {
const float scale = fmaf(cost.minTheta, 1.0f, 1.0e-6f);
const float e = exp2f(fmaf(2.0f / scale, v, 0.0f));
const float dpen_dtheta = cost.sharpW * fmaf(e, fmaf(0.69314718055994530941723212145818f, 2.0f / scale, 0.0f), 0.0f) * (copysignf(1.0f, q_local[i]));
gpen = fmaf(dpen_dtheta, 1.0f, gpen);
}
}
{
const float tsg = fmaf(-q_local[i], cost.archBiasK, 0.0f);
const float sig = 1.0f / fmaf(expf(-tsg), 1.0f, 1.0f);
gpen = fmaf(-(cost.archBiasW * cost.archBiasK), sig, gpen);
}

text

text
const float g = fmaf(fmaf(dx, -sum_s[i], fmaf(dy, sum_c[i], 0.0f)), 1.0f / dist, gpen); q_try[i] = fmaf(-eta, g, q_local[i]); const float lo0 = -1.0471975511965977461542144610932f; const float hi0 = 2.6179938779914943653855361527329f; const float lo = -2.6179938779914943653855361527329f; const float hi = 2.6179938779914943653855361527329f; const float Lb = (i == 0) ? lo0 : lo; const float Hb = (i == 0) ? hi0 : hi; if (q_try[i] < Lb) q_try[i] = Lb; else if (q_try[i] > Hb) q_try[i] = Hb; ++i; } if (cost.variableLen) { int j = 0;

#pragma loop ivdep
while (j < n) {
const float g = fmaf(fmaf(dx, c_arr[j], fmaf(dy, s_arr[j], 0.0f)), 1.0f / dist, 0.0f);
float v = fmaf(-eta, g, q_local[n + j]);
if (v < 0.5f) v = 0.5f;
else if (v > 2.0f) v = 2.0f;
q_try[n + j] = v;
++j;
}
}
float x2, y2;
const float f2 = cost(q_try, x2, y2);
if (f2 < f) {
memcpy(q_local, q_try, static_cast<size_t>(dim) * sizeof(float));
f = f2;
x = x2;
y = y2;
break;
}
eta = fmaf(eta, 0.5f, 0.0f);
++stepI;
}

text

text
const int last = n - 1; const float lo = (last == 0) ? -1.0471975511965977461542144610932f : -2.6179938779914943653855361527329f; const float hi = 2.6179938779914943653855361527329f; float bestLocF = f; float saved = q_local[last]; float delta = 0.05f; while (delta >= 0.00625f) { int sgn = -1; while (sgn <= 1) { float cand = fmaf(static_cast<float>(sgn), delta, saved); if (cand < lo) cand = lo; else if (cand > hi) cand = hi; const float backup = q_local[last]; q_local[last] = cand; float x2, y2; const float f2 = cost(q_local, x2, y2); if (f2 < bestLocF) { bestLocF = f2; x = x2; y = y2; saved = cand; } q_local[last] = backup; sgn += 2; } delta = fmaf(delta, 0.5f, 0.0f); } if (bestLocF < f) { q_local[last] = saved; f = bestLocF; } } if (f < bestF) { bestF = f; bestQ.assign(q_local, q_local + dim); bestX = x; bestY = y; no_improve = 0; } else { ++no_improve; } return f; };

const float f_a = evalAt(a), f_b = evalAt(b);
const float Kf = fminf(fmaxf(fmaf(2.0f, dim_f, 0.0f), 8.0f), 128.0f);
const int K = static_cast<int>(Kf);

H.reserve(static_cast<size_t>(maxIter) + static_cast<size_t>(K) + 16u);
const int rank = g_world->rank();
const int world = g_world->size();

alignas(16) float seeds[256 * 32];
const int seedCnt = generate_heuristic_seeds(cost, map, dim, seeds, 32, fmaf(static_cast<float>(rank), 7919.0f, static_cast<float>(seed)));

int i = 0;
while (i < seedCnt) {
const float* const s = seeds + static_cast<size_t>(fmaf(static_cast<float>(i), 32.0f, 0.0f));
const float t_seed = map.pointToT(s);
const float interval_size = (i < 3) ? fmaf(0.0004f, static_cast<float>(dim), 0.0f)
: fmaf(fmaf(0.00031f, static_cast<float>(dim), 0.0f),
exp2f(fmaf((1.0f / static_cast<float>(seedCnt - 4)) * log2f(fmaf(0.00025f, 1.0f / 0.00031f, 0.0f)),
static_cast<float>(i - 3), 0.0f)),
0.0f);
const float t1 = fmaxf(a, fmaf(-interval_size, 1.0f, t_seed));
const float t2 = fminf(b, fmaf(interval_size, 1.0f, t_seed));
if (t2 > t1) {
alignas(16) float q1[32], q2[32];
float x1, y1, x2, y2;
map.map01ToPoint(t1, q1);
const float f1 = cost(q1, x1, y1);
map.map01ToPoint(t2, q2);
const float f2 = cost(q2, x2, y2);
IntervalND* const I = new IntervalND(t1, t2, f1, f2);
I->i1 = t_to_idx(t1);
I->i2 = t_to_idx(t2);
I->diam = map.block_diameter(I->i1, I->i2);
I->compute_span_level(map);
I->set_metric(I->diam);
update_pockets_and_Mmax(I);
I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f));
if (i < 3) {
I->R = fmaf(I->R, fmaf(0.01f, static_cast<float>(dim), 0.85f), 0.0f);
}
else {
const float start_mult = fmaf(0.214f, static_cast<float>(dim), 0.0f);
const float end_mult = fmaf(0.174f, static_cast<float>(dim), 0.0f);
const float mult = fmaf(start_mult,
exp2f(fmaf((1.0f / static_cast<float>(seedCnt - 4)) * log2f(fmaf(end_mult, 1.0f / start_mult, 0.0f)),
static_cast<float>(i - 3), 0.0f)),
0.0f);
I->R = fmaf(I->R, mult, 0.0f);
}
H.emplace_back(I);
std::push_heap(H.begin(), H.end(), ComparePtrND);
if (f1 < bestF) {
bestF = f1;
bestQ.assign(q1, q1 + dim);
bestX = x1;
bestY = y1;
}
if (f2 < bestF) {
bestF = f2;
bestQ.assign(q2, q2 + dim);
bestX = x2;
bestY = y2;
}
}
++i;
}

float prev_t = a, prev_f = f_a;
int k = 1;
while (k <= K) {
const float t = fmaf(fmaf((b - a), static_cast<float>(k) / static_cast<float>(K + 1), a),
1.0f,
static_cast<float>(rank) / static_cast<float>(world * (K + 1)));
const float f = evalAt(t);
IntervalND* const I = new IntervalND(prev_t, t, prev_f, f);
I->i1 = t_to_idx(prev_t);
I->i2 = t_to_idx(t);
I->diam = map.block_diameter(I->i1, I->i2);
I->compute_span_level(map);
I->set_metric(I->diam);
update_pockets_and_Mmax(I);
I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f));
H.emplace_back(I);
std::push_heap(H.begin(), H.end(), ComparePtrND);
prev_t = t;
prev_f = f;
++k;
}
IntervalND* const tail = new IntervalND(prev_t, b, prev_f, f_b);
tail->i1 = t_to_idx(prev_t);
tail->i2 = t_to_idx(b);
tail->diam = map.block_diameter(tail->i1, tail->i2);
tail->compute_span_level(map);
tail->set_metric(tail->diam);
update_pockets_and_Mmax(tail);
tail->ChangeCharacteristic(fmaf(r, Mmax, 0.0f));
H.emplace_back(tail);
std::push_heap(H.begin(), H.end(), ComparePtrND);

float dmax = fmaf(b, 1.0f, -a);
const float initial_len = dmax;
const float thr03 = fmaf(0.3f, initial_len, 0.0f);
const float inv_thr03 = 1.0f / thr03;
int it = 0;

float kickEveryDimf = fmaf(120.0f, exp2f(fmaf(-0.05f, dim_f, 0.0f)), 0.0f);
if (kickEveryDimf < 60.0f) kickEveryDimf = 60.0f;
const int kickEveryDim = static_cast<int>(kickEveryDimf);

float noImproveThrDimf = fmaf(80.0f, exp2f(fmaf(-0.08f, dim_f, 0.0f)), 0.0f);
if (noImproveThrDimf < 30.0f) noImproveThrDimf = 30.0f;
const int noImproveThrDim = static_cast<int>(noImproveThrDimf);

auto kickEveryByDim = [&](const int d) -> int {
float z = fmaf(120.0f, exp2f(fmaf(-0.05f, static_cast<float>(d), 0.0f)), 0.0f);
if (z < 60.0f) z = 60.0f;
return static_cast<int>(z);
};

auto noImproveThrByDim = [&](const int d) -> int {
float z = fmaf(80.0f, exp2f(fmaf(-0.08f, static_cast<float>(d), 0.0f)), 0.0f);
if (z < 30.0f) z = 30.0f;
return static_cast<int>(z);
};

while (it < maxIter) {
if ((it % kickEveryDim) == 0 && no_improve > noImproveThrDim) {
const float t_best = map.pointToT(bestQ.data());
int ii = 0;
while (ii < 2) {
const float off = (ii == 0) ? 0.01f : -0.01f;
const float t_seed = fminf(b, fmaxf(a, fmaf(off, 1.0f, t_best)));
const float f_seed = evalAt(t_seed);
IntervalND* const J = new IntervalND(fmaf(-0.005f, 1.0f, t_seed), fmaf(0.005f, 1.0f, t_seed), f_seed, f_seed);
J->i1 = t_to_idx(fmaf(-0.005f, 1.0f, t_seed));
J->i2 = t_to_idx(fmaf(0.005f, 1.0f, t_seed));
J->diam = map.block_diameter(J->i1, J->i2);
J->compute_span_level(map);
J->set_metric(J->diam);
update_pockets_and_Mmax(J);
J->ChangeCharacteristic(fmaf(r, Mmax, 0.0f));
J->R = fmaf(J->R, 0.9f, 0.0f);
H.emplace_back(J);
std::push_heap(H.begin(), H.end(), ComparePtrND);
++ii;
}
no_improve = 0;
}

text
const float p = fmaf(-1.0f / initial_len, dmax, 1.0f); const bool stagnation = (no_improve > 100) && (it > 270); const float exp_arg = fmaf(-0.06f, dim_f, 0.0f); const float exp2_exp_arg = exp2f(exp_arg); const float A = fmaf(64.0f, exp2_exp_arg, 200.0f); const float B = fmaf(67.0f, exp2_exp_arg, 210.0f); const int T = static_cast<int>(fmaf(-expm1f(p), A, B)); const float p_arg = fmaf(p, 2.3f, -3.0f); const float r_eff = fmaf(-fmaf(p_arg, fmaf(p_arg, fmaf(p_arg, fmaf(p_arg, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f), 1.0f, 1.05f); std::pop_heap(H.begin(), H.end(), ComparePtrND); IntervalND* const cur = H.back(); H.pop_back(); const float x1 = cur->x1, x2 = cur->x2, y1 = cur->y1, y2 = cur->y2; float m = fmaf(r_eff, Mmax, 0.0f); float tNew = step(m, x1, x2, y1, y2, dim_f, r); tNew = fminf(fmaxf(tNew, a), b); const float fNew = evalAt(tNew); IntervalND* const L = new IntervalND(x1, tNew, y1, fNew); IntervalND* const Rv = new IntervalND(tNew, x2, fNew, y2); L->i1 = t_to_idx(x1); L->i2 = t_to_idx(tNew); Rv->i1 = t_to_idx(tNew); Rv->i2 = t_to_idx(x2); L->diam = map.block_diameter(L->i1, L->i2); Rv->diam = map.block_diameter(Rv->i1, Rv->i2); L->compute_span_level(map); Rv->compute_span_level(map); L->set_metric(L->diam); Rv->set_metric(Rv->diam); const float Mloc = fmaxf(L->M, Rv->M); update_pockets_and_Mmax(L); update_pockets_and_Mmax(Rv); const float prevMmax = Mmax; if (Mloc > Mmax) Mmax = Mloc; m = fmaf(r_eff, Mmax, 0.0f); if (adaptive) { const float len1 = fmaf(tNew, 1.0f, -x1); const float len2 = fmaf(x2, 1.0f, -tNew); if (fmaf(len1, 1.0f, len2) == dmax) { dmax = fmaxf(len1, len2); for (auto pI : H) { const float Ls = fmaf(pI->x2, 1.0f, -pI->x1); if (Ls > dmax) dmax = Ls; } } if ((thr03 > dmax && !(it % 3)) || (fmaf(10.0f, dmax, 0.0f) < initial_len)) { const float progress = fmaf(-inv_thr03, dmax, 1.0f); const float alpha = fmaf(progress, progress, 0.0f); const float beta = fmaf(-alpha, 1.0f, 2.0f); const float MULT = (1.0f / dmax) * Mmax; const float global_coeff = fmaf(MULT, r_eff, -MULT); const float GF = fmaf(beta, global_coeff, 0.0f); L->ChangeCharacteristic(fmaf(GF, len1, fmaf(L->M, alpha, 0.0f))); Rv->ChangeCharacteristic(fmaf(GF, len2, fmaf(Rv->M, alpha, 0.0f))); const size_t sz = H.size(); RecomputeR_AffineM_AVX2_ND(H.data(), sz, GF, alpha); std::make_heap(H.begin(), H.end(), ComparePtrND); } else { if (Mloc > prevMmax) { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); if (Mloc > fmaf(1.15f, prevMmax, 0.0f)) { const size_t sz = H.size(); RecomputeR_ConstM_AVX2_ND(H.data(), sz, m); std::make_heap(H.begin(), H.end(), ComparePtrND); } } else { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); } } } else { if (Mloc > prevMmax) { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); if (Mloc > fmaf(1.15f, prevMmax, 0.0f)) { const size_t sz = H.size(); RecomputeR_ConstM_AVX2_ND(H.data(), sz, m); std::make_heap(H.begin(), H.end(), ComparePtrND); } } else { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); } } H.emplace_back(L); std::push_heap(H.begin(), H.end(), ComparePtrND); H.emplace_back(Rv); std::push_heap(H.begin(), H.end(), ComparePtrND); _mm_prefetch(reinterpret_cast<const char*>(H[0]), _MM_HINT_T0); _mm_prefetch(reinterpret_cast<const char*>(H[1]), _MM_HINT_T0); IntervalND* const top = H.front(); const float interval_len = top->x2 - top->x1; if ((exp2f((1.0f / dim_f) * log2f(interval_len)) < eps) || (it == maxIter)) { out_iterations = static_cast<size_t>(it); out_achieved_epsilon = interval_len; return; } if (!(it % T)) { MultiCrossMsg out; out.count = 3; float* dest = out.intervals; IntervalND* const t1 = H[0]; IntervalND* const t2 = H[1]; IntervalND* const t3 = H[2]; IntervalND* const tops[3] = { t1, t2, t3 }; unsigned i2 = 0; while (i2 < 3) { IntervalND* const Tt = tops[i2]; dest[0] = Tt->x1; dest[1] = 0.0f; dest[2] = Tt->x2; dest[3] = 0.0f; dest[4] = Tt->R; dest += 5; ++i2; } const size_t iterations = std::bit_width(static_cast<size_t>(world - 1)); bool active = true; const bool invert_T = static_cast<int>(fmaf(static_cast<float>(exchange_counter_T), 1.0f, 1.0f)) & 1; size_t ii = 0; while (ii < iterations && active) { const size_t step = 1ULL << ii; const int partner = rank ^ static_cast<int>(step); if (partner < world) { const bool am_sender = (!!(rank & static_cast<int>(step))) ^ invert_T; if (am_sender) { g_world->isend(partner, 0, out); active = false; } } ++ii; } ++exchange_counter_T; } if (!(it % 500)) { BestSolutionMsg out; out.bestF = bestF; out.bestX = bestX; out.bestY = bestY; out.dim = static_cast<unsigned>(bestQ.size()); memcpy(out.bestQ, bestQ.data(), bestQ.size() * sizeof(float)); const size_t iterations = std::bit_width(static_cast<size_t>(world - 1)); bool active = true; const bool invert_T = static_cast<int>(fmaf(static_cast<float>(exchange_counter_500), 1.0f, 1.0f)) & 1; size_t ii = 0; while (ii < iterations && active) { const size_t step = 1ULL << ii; const int partner = rank ^ static_cast<int>(step); if (partner < world) { const bool am_sender = (!!(rank & static_cast<int>(step))) ^ invert_T; if (am_sender) { g_world->isend(partner, 2, out); active = false; } } ++ii; } ++exchange_counter_500; } while (g_world->iprobe(boost::mpi::any_source, 0)) { MultiCrossMsg in; g_world->recv(boost::mpi::any_source, 0, in); const MultiCrossMsg& mX = in; unsigned ii = 0; while (ii < mX.count) { const float* const d = &mX.intervals[ii * 5]; float sx = d[0], ex = d[2]; if (ex > sx) { alignas(16) float tmp[32]; float tx, ty; map.map01ToPoint(sx, tmp); const float y1i = cost(tmp, tx, ty); map.map01ToPoint(ex, tmp); const float y2i = cost(tmp, tx, ty); IntervalND* const inj = new IntervalND(sx, ex, y1i, y2i); inj->i1 = t_to_idx(sx); inj->i2 = t_to_idx(ex); inj->diam = map.block_diameter(inj->i1, inj->i2); inj->compute_span_level(map); inj->set_metric(inj->diam); update_pockets_and_Mmax(inj); inj->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); _mm_prefetch(reinterpret_cast<const char*>(H[0]), _MM_HINT_T0); _mm_prefetch(reinterpret_cast<const char*>(H[1]), _MM_HINT_T0); IntervalND* const topH = H.front(); if (inj->R > fmaf(1.15f, topH->R, 0.0f)) { const float p2 = fmaf(-1.0f / initial_len, dmax, 1.0f); const float kf = (no_improve > 100 && it > 270) ? fmaf(0.5819767068693265f, expm1f(p2), 0.3f) : fmaf(0.3491860241215959f, expm1f(p2), 0.6f); inj->R = fmaf(d[4], kf, 0.0f); H.emplace_back(inj); std::push_heap(H.begin(), H.end(), ComparePtrND); } } ++ii; } } while (g_world->iprobe(boost::mpi::any_source, 2)) { BestSolutionMsg bm; g_world->recv(boost::mpi::any_source, 2, bm); if (bm.bestF < fmaf(bestF, 1.15f, 0.0f)) { alignas(16) float tmp_q[32]; memcpy(tmp_q, bm.bestQ, bm.dim * sizeof(float)); const float t_best = map.pointToT(tmp_q); const float t1 = fmaxf(a, fmaf(-0.001f, 1.0f, t_best)); const float t2 = fminf(b, fmaf(0.001f, 1.0f, t_best)); if (t2 > t1) { alignas(16) float tq1[32], tq2[32]; float xx1, yy1, xx2, yy2; map.map01ToPoint(t1, tq1); const float f1 = cost(tq1, xx1, yy1); map.map01ToPoint(t2, tq2); const float f2 = cost(tq2, xx2, yy2); IntervalND* const I = new IntervalND(t1, t2, f1, f2); I->i1 = t_to_idx(t1); I->i2 = t_to_idx(t2); I->diam = map.block_diameter(I->i1, I->i2); I->compute_span_level(map); I->set_metric(I->diam); update_pockets_and_Mmax(I); I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); I->R = fmaf(I->R, 0.90f, 0.0f); H.emplace_back(I); std::push_heap(H.begin(), H.end(), ComparePtrND); } if (bm.bestF < bestF) { bestF = bm.bestF; bestX = bm.bestX; bestY = bm.bestY; bestQ.assign(bm.bestQ, bm.bestQ + bm.dim); } } } ++it;

}
}
/// КАКОЙ-ТО КОД
extern "C" __declspec(dllexport) __declspec(noalias)
void AGP_Manip2D(const int nSegments, const bool variableLengths, const float minTheta, const float targetX, const float targetY,
const int peanoLevels, const int maxIterPerBranch, const float r, const bool adaptiveMode, const float epsilon,
const unsigned int seed, float** const out_bestQ, size_t* const out_bestQLen, float* const out_bestX,
float* const out_bestY, float* const out_bestF, size_t* const out_iterations, float* const out_achieved_epsilon)
noexcept {
Slab* const __restrict slab = tls.local();
slab->current = slab->base;
while (g_world->iprobe(boost::mpi::any_source, 0)) {
MultiCrossMsg dummy;
g_world->recv(boost::mpi::any_source, 0, dummy);
}
while (g_world->iprobe(boost::mpi::any_source, 2)) {
BestSolutionMsg dummy;
g_world->recv(boost::mpi::any_source, 2, dummy);
}
const int dim = nSegments + (variableLengths ? nSegments : 0);

text

g_mc.permCache.resize(static_cast<size_t>(dim));
int i = 0;
while (i < dim) {
g_mc.permCache[i] = i;
++i;
}
unsigned s = g_mc.baseSeed;
i = dim - 1;
while (i > 0) {
s ^= s << 13;
s ^= s >> 17;
s ^= s << 5;
const unsigned j = s % static_cast<unsigned>(i + 1);
std::swap(g_mc.permCache[i], g_mc.permCache[j]);
--i;
}
g_mc.invMaskCache.resize(static_cast<size_t>(dim));
int k = 0;
while (k < dim) {
s ^= s << 13;
s ^= s >> 17;
s ^= s << 5;
g_mc.invMaskCache[k] = static_cast<unsigned long long>(s);
++k;
}

std::vector<float, boost::alignment::aligned_allocator<float, 16u>> low;
std::vector<float, boost::alignment::aligned_allocator<float, 16u>> high;
low.reserve(static_cast<size_t>(dim));
high.reserve(static_cast<size_t>(dim));
i = 0;
while (i < nSegments) {
low.emplace_back(i == 0 ? -1.0471975511965977461542144610932f : -2.6179938779914943653855361527329f);
high.emplace_back(2.6179938779914943653855361527329f);
++i;
}
if (variableLengths) {
i = 0;
while (i < nSegments) {
low.emplace_back(0.5f);
high.emplace_back(2.0f);
++i;
}
}

const ManipCost cost(nSegments, variableLengths, targetX, targetY, minTheta);

const int rank = g_world->rank(), world = g_world->size();
std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ;
float bestF = FLT_MAX, bx = 0.0f, by = 0.0f;

const int levels0 = static_cast<int>(fminf(static_cast<float>(peanoLevels), 8.0f));
const int maxIter0 = static_cast<int>(fmaf(static_cast<float>(maxIterPerBranch), 0.2f, 0.0f));
const MortonND map0(dim, levels0, low.data(), high.data(), g_mc);

std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>> H_coarse;
std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ_coarse;
float bestF_coarse = FLT_MAX, bx_coarse = 0.0f, by_coarse = 0.0f;
size_t total_oi = 0u;
float total_oe = 0.0f;
size_t oi = 0u;
float oe = 0.0f;

const float base_M_prior_factor = fmaf(2.0f, static_cast<float>(nSegments), variableLengths ? 1.41421356f : 0.0f);

float M_prior = fmaf(base_M_prior_factor,
ldexpf(1.0f, -levels0),
0.0f);

agp_run_branch_mpi(map0, cost, maxIter0, r, adaptiveMode, epsilon, seed,
H_coarse, bestQ_coarse, bestF_coarse, bx_coarse, by_coarse, oi, oe, M_prior);

total_oi += oi;
total_oe = oe;

if (bestF_coarse < bestF) {
bestF = bestF_coarse;
bestQ = std::move(bestQ_coarse);
bx = bx_coarse;
by = by_coarse;
}

if (levels0 < peanoLevels) {
while (g_world->iprobe(boost::mpi::any_source, 0)) {
MultiCrossMsg dummy;
g_world->recv(boost::mpi::any_source, 0, dummy);
}
while (g_world->iprobe(boost::mpi::any_source, 2)) {
BestSolutionMsg dummy;
g_world->recv(boost::mpi::any_source, 2, dummy);
}
const MortonND map1(dim, peanoLevels, low.data(), high.data(), g_mc);
std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>> H_fine;
std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ_fine = bestQ;
float bestF_fine = bestF, bx_fine = bx, by_fine = by;
size_t oi_fine = 0u;
float oe_fine = 0.0f;

text
float M_prior_fine = fmaf(base_M_prior_factor, ldexpf(1.0f, -peanoLevels), 0.0f); HoaraSort(H_coarse.begin(), H_coarse.end() - 1); const float inv_dim = 1.0f / static_cast<float>(dim + 1); size_t ci = static_cast<size_t>(fmaf(static_cast<float>(H_coarse.size()), fmaf(fmaf(inv_dim, fmaf(inv_dim, fmaf(inv_dim, fmaf(inv_dim, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f), 1.0f, -0.7f), 0.0f)); while (ci < H_coarse.size()) { const IntervalND* const C = H_coarse[ci]; alignas(16) float q1[32], q2[32]; float x1, y1, x2, y2; map1.map01ToPoint(C->x1, q1); const float f1 = cost(q1, x1, y1); map1.map01ToPoint(C->x2, q2); const float f2 = cost(q2, x2, y2); IntervalND* const I = new IntervalND(C->x1, C->x2, f1, f2); I->i1 = static_cast<unsigned long long>(fmaf(C->x1, static_cast<float>(map1.scale), 0.0f)); I->i2 = static_cast<unsigned long long>(fmaf(C->x2, static_cast<float>(map1.scale), 0.0f)); I->diam = map1.block_diameter(I->i1, I->i2); I->set_metric(I->diam); H_fine.emplace_back(I); if (f1 < bestF_fine) { bestF_fine = f1; bestQ_fine.assign(q1, q1 + dim); bx_fine = x1; by_fine = y1; } if (f2 < bestF_fine) { bestF_fine = f2; bestQ_fine.assign(q2, q2 + dim); bx_fine = x2; by_fine = y2; } ++ci; } std::make_heap(H_fine.begin(), H_fine.end(), ComparePtrND); agp_run_branch_mpi(map1, cost, fmaf(static_cast<float>(maxIterPerBranch), 1.0f, -static_cast<float>(maxIter0)), r, adaptiveMode, epsilon, seed, H_fine, bestQ_fine, bestF_fine, bx_fine, by_fine, oi_fine, oe_fine, M_prior_fine); total_oi += oi_fine; total_oe = oe_fine; if (bestF_fine < bestF) { bestF = bestF_fine; bestQ = std::move(bestQ_fine); bx = bx_fine; by = by_fine; }

}

BestSolutionMsg best;
best.bestF = bestF;
best.bestX = bx;
best.bestY = by;
best.dim = static_cast<unsigned>(bestQ.size());
memcpy(best.bestQ, bestQ.data(), static_cast<size_t>(best.dim) * sizeof(float));

const size_t iterations = std::bit_width(static_cast<size_t>(world - 1));
bool active = true;

size_t itx = 0;
while (itx < iterations && active) {
const size_t step = 1ULL << itx;
const int partner = rank ^ static_cast<int>(step);

text
if (partner < world) { const bool am_sender = (rank & static_cast<int>(step)) != 0; if (am_sender) { g_world->isend(partner, 3, best); active = false; } else { BestSolutionMsg in; g_world->recv(partner, 3, in); if (in.bestF < best.bestF) best = in; } } ++itx;

}

if (rank == 0) {
*out_bestQLen = static_cast<size_t>(best.dim);
out_bestQ = static_cast<float>(CoTaskMemAlloc(sizeof(float) * (*out_bestQLen)));
memcpy(*out_bestQ, best.bestQ, sizeof(float) * (*out_bestQLen));
*out_bestX = best.bestX;
*out_bestY = best.bestY;
*out_bestF = best.bestF;
*out_iterations = total_oi;
*out_achieved_epsilon = total_oe;
}
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline int AgpInit(const int peanoLevel, const float a, const float b, const float c, const float d) noexcept {
g_env = new boost::mpi::environment();
g_world = new boost::mpi::communicator();
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
const int rank = g_world->rank();
const int world_size = g_world->size();
if (world_size == 4) {
new (&gActiveMap) Peano2DMap(peanoLevel, a, b, c, d, rank & 3);
}
g_mc.baseSeed = fmaf(0x9E3779B9u, static_cast<float>(rank), 0x9E3779B9u);
return rank;
}
/// КАКОЙ-ТО КОД
__declspec(align(16)) struct RunParams final {
int nSegments;
unsigned varLen;
float minTheta;
float tx, ty;
int levels;
int maxIter;
float r;
unsigned adaptive;
float eps;
unsigned seed;

text

template<typename Archive>
void serialize(Archive& ar, const unsigned int) {
ar& nSegments& varLen& minTheta& tx& ty
& levels& maxIter& r& adaptive& eps& seed;
}
};

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline
void AgpStartManipND(const int nSegments, const bool variableLengths, const float minTheta, const float targetX, const float targetY,
const int peanoLevels, const int maxIterPerBranch, const float r, const bool adaptiveMode, const float epsilon, const unsigned int seed) noexcept {
RunParams p;
p.nSegments = nSegments;
p.varLen = static_cast<unsigned>(variableLengths);
p.minTheta = minTheta;
p.tx = targetX;
p.ty = targetY;
p.levels = peanoLevels;
p.maxIter = maxIterPerBranch;
p.r = r;
p.adaptive = static_cast<unsigned>(adaptiveMode);
p.eps = epsilon;
p.seed = seed;
int i = 1;
const int world = g_world->size();
while (i < world) {
g_world->isend(i, 1, p);
++i;
}
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpWaitStartAndRun() noexcept {
RunParams p;
float* __restrict q;
size_t qlen;
float bx, by, bf;
size_t oi;
float oa;
while (true) {
if (g_world->iprobe(0, 1)) {
g_world->recv(0, 1, p);
AGP_Manip2D(p.nSegments, static_cast<bool>(p.varLen), p.minTheta, p.tx, p.ty, p.levels, p.maxIter, p.r, static_cast<bool>(p.adaptive), p.eps, p.seed, &q, &qlen, &bx, &by, &bf, &oi, &oa);
}
Sleep(0);
}
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AgpStartWorkers() noexcept {
int i = 1;
const int world = g_world->size();
while (i < world) {
g_world->isend(i, 1, 0);
++i;
}
}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline void AGP_Free(float* const p) noexcept {
CoTaskMemFree(p);
}", код интерфейса c++/clr, код MyForm.h: "#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::Collections::Generic;
using namespace System::Drawing::Drawing2D;
using namespace System::Diagnostics;
using namespace System::Globalization;
using namespace System::Runtime::CompilerServices;

typedef void(__cdecl* P_MANIP)(int, bool, float, float, float, int, int, float, bool, float, unsigned int, float**, size_t*, float*, float*, float*, size_t*, float*);
typedef void(__cdecl* P_FREE)(float*);
typedef void(__cdecl* P_START)(int, bool, float, float, float, int, int, float, bool, float, unsigned int);

namespace TESTAGP {

text

public enum class OptimizerBackend {
AgpCpp = 0,
Optuna = 1,
IOpt = 2
};

public ref class MyForm sealed : public Form {
public:
MyForm(HMODULE hLib)
: hLib(hLib) {
this->SetStyle(ControlStyles::AllPaintingInWmPaint |
ControlStyles::UserPaint |
ControlStyles::OptimizedDoubleBuffer, true);
this->Text = L"AGP Manipulator 2D";
this->ClientSize = System::Drawing::Size(1200, 800);
this->Resize += gcnew EventHandler(this, &MyForm::OnResize);

text
fManip = reinterpret_cast<P_MANIP>(GetProcAddress(hLib, "AGP_Manip2D")); pFree = reinterpret_cast<P_FREE>(GetProcAddress(hLib, "AGP_Free")); pStart = reinterpret_cast<P_START>(GetProcAddress(hLib, "AgpStartManipND")); angles = gcnew List<float>(8); lengths = gcnew List<float>(8); backend = OptimizerBackend::AgpCpp; InitGraphicsResources(); InitUI(); ResetRandomConfig(); }

private:
initonly HMODULE hLib;
initonly P_MANIP fManip;
initonly P_FREE pFree;
initonly P_START pStart;

text
OptimizerBackend backend; ComboBox^ cbBackend; int nSegments; bool variableLengths; List<float>^ angles; List<float>^ lengths; CheckBox^ cbVarLen; NumericUpDown^ nudMinTheta; NumericUpDown^ nudBaseLength; NumericUpDown^ nudStretchFactor; NumericUpDown^ nudTargetX; NumericUpDown^ nudTargetY; NumericUpDown^ nudLevels; NumericUpDown^ nudMaxIter; CheckBox^ cbAdaptive; NumericUpDown^ nudR; NumericUpDown^ nudEps; Button^ btnAdd; Button^ btnRem; Button^ btnOptimize; Label^ lblInfo; UInt32 rngState = 0xA5C39E0Du; System::Drawing::Font^ uiFontBold11; System::Drawing::Font^ uiFontBold10; Pen^ wallPen; Pen^ dashedPen; Pen^ targetPen; Pen^ penRod; SolidBrush^ jointBrush; HatchBrush^ wallHatchBrush; void InitGraphicsResources() { uiFontBold11 = gcnew System::Drawing::Font("Yu Gothic UI", 11, FontStyle::Bold); uiFontBold10 = gcnew System::Drawing::Font("Yu Gothic UI", 10, FontStyle::Bold); wallPen = gcnew Pen(Color::Black, 2.0f); dashedPen = gcnew Pen(Color::Black, 2.0f); dashedPen->DashStyle = DashStyle::Dash; targetPen = gcnew Pen(Color::Green, 3.0f); targetPen->DashStyle = DashStyle::Dot; penRod = gcnew Pen(Color::Red, 6.0f); jointBrush = gcnew SolidBrush(Color::Blue); wallHatchBrush = gcnew HatchBrush(HatchStyle::BackwardDiagonal, Color::LightGray, Color::White); } void InitUI() { cbBackend = gcnew ComboBox(); cbBackend->Location = Point(920, 52); cbBackend->Width = 260; cbBackend->Height = 28; cbBackend->DropDownStyle = ComboBoxStyle::DropDownList; cbBackend->Font = uiFontBold11; cbBackend->BackColor = SystemColors::Info; cbBackend->FlatStyle = FlatStyle::Flat; cbBackend->Items->Add(L"AGP"); cbBackend->Items->Add(L"Optuna"); cbBackend->Items->Add(L"iOpt"); cbBackend->SelectedIndex = 0; cbBackend->SelectedIndexChanged += gcnew EventHandler(this, &MyForm::OnBackendChanged); this->Controls->Add(cbBackend); Label^ L; L = gcnew Label(); L->Text = L"Мин. угол (рад)"; L->Location = Point(20, 20); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudMinTheta = gcnew NumericUpDown(); nudMinTheta->Location = Point(20, 52); nudMinTheta->Width = 200; nudMinTheta->DecimalPlaces = 3; nudMinTheta->Minimum = Decimal(1) / Decimal(100); nudMinTheta->Maximum = Decimal(314159) / Decimal(100000); nudMinTheta->Value = Decimal(150) / Decimal(100); nudMinTheta->Font = uiFontBold10; nudMinTheta->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudMinTheta); L = gcnew Label(); L->Text = L"Базовая длина"; L->Location = Point(245, 20); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudBaseLength = gcnew NumericUpDown(); nudBaseLength->Location = Point(245, 52); nudBaseLength->Width = 200; nudBaseLength->DecimalPlaces = 2; nudBaseLength->Minimum = Decimal(1) / Decimal(2); nudBaseLength->Maximum = Decimal(200) / Decimal(100); nudBaseLength->Value = Decimal(100) / Decimal(100); nudBaseLength->Font = uiFontBold10; nudBaseLength->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudBaseLength); L = gcnew Label(); L->Text = L"Коэф. растяжения"; L->Location = Point(470, 20); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudStretchFactor = gcnew NumericUpDown(); nudStretchFactor->Location = Point(470, 52); nudStretchFactor->Width = 200; nudStretchFactor->DecimalPlaces = 2; nudStretchFactor->Minimum = Decimal(100) / Decimal(100); nudStretchFactor->Maximum = Decimal(150) / Decimal(100); nudStretchFactor->Increment = Decimal(1) / Decimal(100); nudStretchFactor->Value = Decimal(150) / Decimal(100); nudStretchFactor->Font = uiFontBold10; nudStretchFactor->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudStretchFactor); cbVarLen = gcnew CheckBox(); cbVarLen->Text = L"Переменные длины"; cbVarLen->Location = Point(695, 52); cbVarLen->Width = 200; cbVarLen->Checked = false; cbVarLen->Font = uiFontBold11; cbVarLen->CheckedChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(cbVarLen); L = gcnew Label(); L->Text = L"Цель X"; L->Location = Point(20, 107); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudTargetX = gcnew NumericUpDown(); nudTargetX->Location = Point(20, 139); nudTargetX->Width = 200; nudTargetX->DecimalPlaces = 2; nudTargetX->Minimum = Decimal(-100) / Decimal(10); nudTargetX->Maximum = Decimal(100) / Decimal(10); nudTargetX->Value = Decimal(25) / Decimal(10); nudTargetX->Font = uiFontBold10; nudTargetX->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudTargetX); L = gcnew Label(); L->Text = L"Цель Y"; L->Location = Point(245, 107); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudTargetY = gcnew NumericUpDown(); nudTargetY->Location = Point(245, 139); nudTargetY->Width = 200; nudTargetY->DecimalPlaces = 2; nudTargetY->Minimum = Decimal(-100) / Decimal(10); nudTargetY->Maximum = Decimal(100) / Decimal(10); nudTargetY->Value = Decimal(-10) / Decimal(10); nudTargetY->Font = uiFontBold10; nudTargetY->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudTargetY); L = gcnew Label(); L->Text = L"Глубина развёрток"; L->Location = Point(470, 107); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudLevels = gcnew NumericUpDown(); nudLevels->Location = Point(470, 139); nudLevels->Width = 200; nudLevels->Minimum = 7; nudLevels->Maximum = 20; nudLevels->Value = 12; nudLevels->Font = uiFontBold10; nudLevels->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudLevels); L = gcnew Label(); L->Text = L"Надежность (r)"; L->Location = Point(695, 107); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudR = gcnew NumericUpDown(); nudR->Location = Point(695, 139); nudR->Width = 200; nudR->DecimalPlaces = 2; nudR->Minimum = Decimal(100) / Decimal(100); nudR->Maximum = Decimal(2000) / Decimal(100); nudR->Value = Decimal(250) / Decimal(100); nudR->Font = uiFontBold10; nudR->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudR); cbAdaptive = gcnew CheckBox(); cbAdaptive->Text = L"Адаптивная схема"; cbAdaptive->Location = Point(920, 139); cbAdaptive->Width = 200; cbAdaptive->Checked = true; cbAdaptive->Font = uiFontBold11; cbAdaptive->CheckedChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(cbAdaptive); L = gcnew Label(); L->Text = L"Точность"; L->Location = Point(20, 194); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudEps = gcnew NumericUpDown(); nudEps->Location = Point(20, 226); nudEps->Width = 200; nudEps->DecimalPlaces = 9; nudEps->Minimum = Decimal(1) / Decimal(1000000000); nudEps->Maximum = Decimal(1) / Decimal(10); nudEps->Value = Decimal(1) / Decimal(100000); nudEps->Font = uiFontBold10; nudEps->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudEps); L = gcnew Label(); L->Text = L"Макс. итераций"; L->Location = Point(245, 194); L->Width = 200; L->Font = uiFontBold11; this->Controls->Add(L); nudMaxIter = gcnew NumericUpDown(); nudMaxIter->Location = Point(245, 226); nudMaxIter->Width = 200; nudMaxIter->Minimum = 10; nudMaxIter->Maximum = 500000; nudMaxIter->Value = 1000; nudMaxIter->Font = uiFontBold10; nudMaxIter->Increment = 100; nudMaxIter->ValueChanged += gcnew EventHandler(this, &MyForm::OnAnyChanged); this->Controls->Add(nudMaxIter); btnAdd = gcnew Button(); btnAdd->Text = L"+ Звено"; btnAdd->Location = Point(465, 226); btnAdd->Width = 90; btnAdd->Height = 35; btnAdd->BackColor = SystemColors::Info; btnAdd->Cursor = Cursors::Hand; btnAdd->FlatAppearance->BorderColor = Color::FromArgb(64, 64, 64); btnAdd->FlatAppearance->BorderSize = 3; btnAdd->FlatAppearance->MouseDownBackColor = Color::FromArgb(128, 128, 255); btnAdd->FlatAppearance->MouseOverBackColor = Color::FromArgb(192, 192, 255); btnAdd->FlatStyle = FlatStyle::Flat; btnAdd->Font = uiFontBold11; btnAdd->ForeColor = SystemColors::ControlDarkDark; btnAdd->Click += gcnew EventHandler(this, &MyForm::OnAddClick); this->Controls->Add(btnAdd); btnRem = gcnew Button(); btnRem->Text = L"- Звено"; btnRem->Location = Point(560, 226); btnRem->Width = 90; btnRem->Height = 35; btnRem->BackColor = SystemColors::Info; btnRem->Cursor = Cursors::Hand; btnRem->FlatAppearance->BorderColor = Color::FromArgb(64, 64, 64); btnRem->FlatAppearance->BorderSize = 3; btnRem->FlatAppearance->MouseDownBackColor = Color::FromArgb(128, 128, 255); btnRem->FlatAppearance->MouseOverBackColor = Color::FromArgb(192, 192, 255); btnRem->FlatStyle = FlatStyle::Flat; btnRem->Font = uiFontBold11; btnRem->ForeColor = SystemColors::ControlDarkDark; btnRem->Click += gcnew EventHandler(this, &MyForm::OnRemClick); this->Controls->Add(btnRem); btnOptimize = gcnew Button(); btnOptimize->Text = L"Оптимизировать"; btnOptimize->Location = Point(680, 226); btnOptimize->Width = 150; btnOptimize->Height = 35; btnOptimize->BackColor = SystemColors::Info; btnOptimize->Cursor = Cursors::Hand; btnOptimize->FlatAppearance->BorderColor = Color::FromArgb(64, 64, 64); btnOptimize->FlatAppearance->BorderSize = 3; btnOptimize->FlatAppearance->MouseDownBackColor = Color::FromArgb(128, 128, 255); btnOptimize->FlatAppearance->MouseOverBackColor = Color::FromArgb(192, 192, 255); btnOptimize->FlatStyle = FlatStyle::Flat; btnOptimize->Font = uiFontBold11; btnOptimize->ForeColor = SystemColors::ControlDarkDark; btnOptimize->Click += gcnew EventHandler(this, &MyForm::OnOptimizeClick); this->Controls->Add(btnOptimize); lblInfo = gcnew Label(); lblInfo->Location = Point(835, 194); lblInfo->Size = System::Drawing::Size(335, 140); lblInfo->BorderStyle = BorderStyle::FixedSingle; lblInfo->Text = L"Готов"; lblInfo->Font = uiFontBold10; this->Controls->Add(lblInfo); } void ResetRandomConfig() { nSegments = 1; angles->Clear(); lengths->Clear(); angles->Add(1.57079637f); lengths->Add(static_cast<float>(nudBaseLength->Value)); variableLengths = false; this->Invalidate(); } [MethodImpl(MethodImplOptions::AggressiveInlining)] float Rand01() { rngState ^= rngState << 13; rngState ^= rngState >> 17; rngState ^= rngState << 5; const float inv = 1.0f / 4294967296.0f; return static_cast<float>(static_cast<unsigned int>(rngState)) * inv; } [MethodImpl(MethodImplOptions::AggressiveInlining)] float RandAngle() { return Rand01() * 6.28318548f - 3.14159274f; } void RunAgpCpp() { variableLengths = cbVarLen->Checked; const float minTheta = static_cast<float>(nudMinTheta->Value); const float tx = static_cast<float>(nudTargetX->Value); const float ty = static_cast<float>(nudTargetY->Value); const int levels = static_cast<int>(nudLevels->Value); const int maxIter = static_cast<int>(nudMaxIter->Value); const bool adaptive = cbAdaptive->Checked; const float r_param = static_cast<float>(nudR->Value); const float eps = static_cast<float>(nudEps->Value); const unsigned int seed = static_cast<unsigned int>(GetTickCount()); pStart(nSegments, variableLengths, minTheta, tx, ty, levels, maxIter, r_param, adaptive, eps, seed); LARGE_INTEGER t0; LARGE_INTEGER t1; LARGE_INTEGER fq; QueryPerformanceCounter(&t0); float* bestQ = nullptr; size_t bestQLen = 0; float bestX = 0.0f; float bestY = 0.0f; float bestF = 0.0f; size_t actualIterations = 0u; float achievedEps = 0.0f; fManip(nSegments, variableLengths, minTheta, tx, ty, levels, maxIter, r_param, adaptive, eps, seed, &bestQ, &bestQLen, &bestX, &bestY, &bestF, &actualIterations, &achievedEps); QueryPerformanceCounter(&t1); QueryPerformanceFrequency(&fq); const double micros = 1e6 * static_cast<double>(t1.QuadPart - t0.QuadPart) / static_cast<double>(fq.QuadPart); angles->Clear(); for (int i = 0; i < nSegments; ++i) { angles->Add(bestQ[i]); } lengths->Clear(); if (variableLengths) { for (int i = 0; i < nSegments; ++i) { lengths->Add(bestQ[nSegments + i]); } } else { const float baseLen = static_cast<float>(nudBaseLength->Value); for (int i = 0; i < nSegments; ++i) { lengths->Add(baseLen); } } pFree(bestQ); const float dx = bestX - tx; const float dy = bestY - ty; lblInfo->Text = String::Format( L"Результат:\n" L"Близость захвата: {0:F5}\n" L"Функционал: {1:F5}\n" L"Точка: ({2:F3}, {3:F3})\n" L"Время: {4:F0} мкс\n" L"Число шагов: {5}\n" L"Достигнутая точность: {6:E3}", Math::Sqrt(dx * dx + dy * dy), bestF, bestX, bestY, micros, actualIterations, achievedEps); this->Invalidate(); } [MethodImpl(MethodImplOptions::AggressiveInlining)] bool ParseFloat(String^ s, float% out) { return Single::TryParse( s, NumberStyles::Float, CultureInfo::InvariantCulture, out); } void RunPythonBackend(String^ which) { const int nSeg = nSegments; const bool varLen = cbVarLen->Checked; const float minTheta = static_cast<float>(nudMinTheta->Value); const float tx = static_cast<float>(nudTargetX->Value); const float ty = static_cast<float>(nudTargetY->Value); const int levels = static_cast<int>(nudLevels->Value); const int maxIter = static_cast<int>(nudMaxIter->Value); const float r_param = static_cast<float>(nudR->Value); const float eps = static_cast<float>(nudEps->Value); const bool adaptive = cbAdaptive->Checked; String^ pythonExe = System::IO::Path::Combine( Application::StartupPath, "env", "Scripts", "python.exe"); String^ scriptPath = System::IO::Path::Combine( Application::StartupPath, "optimizer_bridge.py"); ProcessStartInfo^ psi = gcnew ProcessStartInfo(); psi->FileName = pythonExe; psi->Arguments = String::Format( CultureInfo::InvariantCulture, "\"{0}\" {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11}", scriptPath, which, nSeg, varLen ? 1 : 0, minTheta, tx, ty, levels, maxIter, r_param, eps, adaptive ? 1 : 0); psi->UseShellExecute = false; psi->CreateNoWindow = true; psi->RedirectStandardOutput = true; psi->RedirectStandardError = false; psi->WorkingDirectory = Application::StartupPath; Process^ proc = Process::Start(psi); String^ stdoutAll = proc->StandardOutput->ReadToEnd(); proc->WaitForExit(); float bestF = 0.0f; float bestX = 0.0f; float bestY = 0.0f; float achievedEps = 0.0f; int iterations = 0; float micros = 0.0f; array<float>^ tempAngles = gcnew array<float>(nSeg); array<float>^ tempLengths = gcnew array<float>(nSeg); const float baseLen = static_cast<float>(nudBaseLength->Value); for (int i = 0; i < nSeg; ++i) { tempAngles[i] = 0.0f; tempLengths[i] = baseLen; } array<wchar_t>^ separators = gcnew array<wchar_t>{ '\r', '\n' }; array<String^>^ lines = stdoutAll->Split( separators, StringSplitOptions::RemoveEmptyEntries); int qIndex = 0; for each(String ^ line in lines) { array<wchar_t>^ partSeparators = gcnew array<wchar_t>{ ' ', '\t' }; array<String^>^ parts = line->Split( partSeparators, StringSplitOptions::RemoveEmptyEntries); if (parts->Length == 0) { continue; } String^ key = parts[0]; if (key == "Q") { for (int i = 1; i < parts->Length && qIndex < (varLen ? 2 * nSeg : nSeg); ++i) { float qVal; if (ParseFloat(parts[i], qVal)) { if (qIndex < nSeg) { tempAngles[qIndex] = qVal; } else if (varLen && qIndex < 2 * nSeg) { tempLengths[qIndex - nSeg] = qVal; } ++qIndex; } } } else if (parts->Length > 1) { String^ value = parts[1]; if (key == "BEST_F") { ParseFloat(value, bestF); } else if (key == "BEST_X") { ParseFloat(value, bestX); } else if (key == "BEST_Y") { ParseFloat(value, bestY); } else if (key == "ITERATIONS") { Int32::TryParse(value, iterations); } else if (key == "EPS") { ParseFloat(value, achievedEps); } else if (key == "TIME") { ParseFloat(value, micros); } } } angles->Clear(); lengths->Clear(); for (int i = 0; i < nSeg; ++i) { angles->Add(tempAngles[i]); lengths->Add(tempLengths[i]); } const float dx = bestX - tx; const float dy = bestY - ty; const double distance = Math::Sqrt(dx * dx + dy * dy); lblInfo->Text = String::Format( L"Результат:\n" L"Близость захвата: {0:F5}\n" L"Функционал: {1:F5}\n" L"Точка: ({2:F3}, {3:F3})\n" L"Время: {4:F0} мкс\n" L"Число шагов: {5}\n" L"Достигнутая точность: {6:E3}", distance, bestF, bestX, bestY, micros, iterations, achievedEps); this->Invalidate(); this->Refresh(); } System::Void OnBackendChanged(System::Object^ sender, System::EventArgs^ e) { switch (cbBackend->SelectedIndex) { case 0: backend = OptimizerBackend::AgpCpp; cbAdaptive->Enabled = true; break; case 1: backend = OptimizerBackend::Optuna; cbAdaptive->Enabled = false; break; case 2: backend = OptimizerBackend::IOpt; cbAdaptive->Enabled = true; break; default: backend = OptimizerBackend::AgpCpp; cbAdaptive->Enabled = true; break; } } System::Void OnResize(System::Object^ sender, System::EventArgs^ e) { this->Invalidate(); } System::Void OnAnyChanged(System::Object^ sender, System::EventArgs^ e) { this->Invalidate(); } System::Void OnAddClick(System::Object^ sender, System::EventArgs^ e) { ++nSegments; angles->Add(RandAngle()); lengths->Add(static_cast<float>(nudBaseLength->Value)); this->Invalidate(); } System::Void OnRemClick(System::Object^ sender, System::EventArgs^ e) { if (nSegments > 1) { --nSegments; angles->RemoveAt(angles->Count - 1); lengths->RemoveAt(lengths->Count - 1); this->Invalidate(); } } System::Void OnOptimizeClick(System::Object^ sender, System::EventArgs^ e) { switch (backend) { case OptimizerBackend::AgpCpp: RunAgpCpp(); break; case OptimizerBackend::Optuna: RunPythonBackend(L"optuna"); break; case OptimizerBackend::IOpt: RunPythonBackend(L"iopt"); break; } }

protected:
virtual void OnPaint(PaintEventArgs^ e) override {
Form::OnPaint(e);
Graphics^ g = e->Graphics;
g->SmoothingMode = SmoothingMode::HighQuality;
g->Clear(this->BackColor);

text
System::Drawing::Rectangle drawArea(0, 180, this->ClientSize.Width, this->ClientSize.Height - 180); g->FillRectangle(Brushes::White, drawArea); const int leftWallX = drawArea.Left + this->ClientSize.Width * 25 / 100; g->DrawLine(wallPen, leftWallX, drawArea.Top, leftWallX, this->ClientSize.Height); g->FillRectangle(wallHatchBrush, leftWallX - 100, drawArea.Top, 100, this->ClientSize.Height - drawArea.Top); const float targetX = static_cast<float>(nudTargetX->Value); const float targetY = static_cast<float>(nudTargetY->Value); const int baseX = leftWallX; const int baseY = drawArea.Top + drawArea.Height / 2; const float pixelTargetX = static_cast<float>(baseX) + targetX * 160.0f; const float pixelTargetY = static_cast<float>(baseY) - targetY * 160.0f; int rightWallX = static_cast<int>(pixelTargetX + 8.0f); if (rightWallX > drawArea.Right - 10) { rightWallX = drawArea.Right - 10; } g->DrawLine(dashedPen, rightWallX, drawArea.Top, rightWallX, this->ClientSize.Height); g->FillRectangle(wallHatchBrush, rightWallX, drawArea.Top, 100, this->ClientSize.Height - drawArea.Top); g->DrawEllipse(targetPen, pixelTargetX - 8.0f, pixelTargetY - 8.0f, 16.0f, 16.0f); cli::array<PointF>^ pts = gcnew cli::array<PointF>(nSegments + 1); pts[0] = PointF(static_cast<float>(baseX), static_cast<float>(baseY)); float x = 0.0f; float y = 0.0f; float phi = 0.0f; array<float>^ localAngles = angles->ToArray(); array<float>^ localLengths = lengths->ToArray(); for (int i = 0; i < nSegments; ++i) { const float theta = localAngles[i]; const float L = localLengths[i]; phi += theta; x += L * static_cast<float>(Math::Cos(static_cast<double>(phi))); y += L * static_cast<float>(Math::Sin(static_cast<double>(phi))); pts[i + 1] = PointF(static_cast<float>(baseX) + x * 160.0f, static_cast<float>(baseY) - y * 160.0f); } for (int i = 0; i < nSegments; ++i) { g->DrawLine(penRod, pts[i], pts[i + 1]); } for (int i = 0; i <= nSegments; ++i) { g->FillEllipse(jointBrush, pts[i].X - 8.0f, pts[i].Y - 8.0f, 16.0f, 16.0f); } }

};
}", MyForm.cpp: "#include "MyForm.h"

using namespace System;
using namespace System::Windows::Forms;

typedef int(__cdecl* PInit)(int, float, float, float, float);
typedef void(__cdecl* PStartWorkers)();

[STAThread]
int main() {
HMODULE h = LoadLibraryW(L"TEST_FUNC.dll");
auto AgpInit = (PInit)GetProcAddress(h, "AgpInit");
auto AgpWaitStartAndRun = (PStartWorkers)GetProcAddress(h, "AgpWaitStartAndRun");

text

const int rank = AgpInit(12, -2.2f, 1.8f, -2.2f, 1.8f);

if (!rank) {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew TESTAGP::MyForm(h));
}
else {
AgpWaitStartAndRun();
}

return 0;
}", код python скрипта: "import sys
import math
import time
import statistics
from io import StringIO

import optuna
import iOpt

def forward_kinematics(angles, lengths):
n = len(angles)
phi = 0.0
x = 0.0
y = 0.0
cos = math.cos
sin = math.sin
for i in range(n):
phi += angles[i]
L = lengths[i]
x += L * cos(phi)
y += L * sin(phi)
return x, y

def manipulator_cost(angles, lengths, target_x, target_y, min_theta):
x, y = forward_kinematics(angles, lengths)
dx = x - target_x
dy = y - target_y
sqrt = math.sqrt
pow_ = math.pow
log1p = math.log1p
exp = math.exp
abs_ = abs
dist = sqrt(dx * dx + dy * dy)
arch_bias_w = 0.02
arch_bias_k = 3.0
sharp_w = 0.05
scale = 2.0 / (min_theta + 1e-6)
pen_c = 0.0
arch_pen = 0.0
for theta in angles:
a = abs_(theta)
v = a - min_theta
if v > 0.0:
pen_c += sharp_w * (pow_(2.0, scale * v) - 1.0)
t = -theta * arch_bias_k
if t > 10.0:
sp = t
else:
sp = log1p(exp(t))
arch_pen += arch_bias_w * sp
return dist + pen_c + arch_pen

def build_angles_lengths(trial, n_seg, var_len):
theta0_min = -1.0471975511965977
theta0_max = 2.6179938779914944
theta_min = -2.6179938779914944
theta_max = 2.6179938779914944
suggest_float = trial.suggest_float
angles = []
if n_seg > 0:
angles.append(suggest_float("theta_0", theta0_min, theta0_max))
for i in range(1, n_seg):
name = "theta_" + str(i)
angles.append(suggest_float(name, theta_min, theta_max))
if var_len:
lengths = []
for i in range(n_seg):
name = "L_" + str(i)
lengths.append(suggest_float(name, 0.5, 2.0))
else:
lengths = [1.0] * n_seg
return angles, lengths

def run_optuna(n_seg, var_len, min_theta, tx, ty, max_iter, r_param, eps):
optuna.logging.set_verbosity(optuna.logging.ERROR)
build = build_angles_lengths
cost = manipulator_cost

text

def objective(trial):
angles, lengths = build(trial, n_seg, var_len)
return cost(angles, lengths, tx, ty, min_theta)

study = optuna.create_study(direction="minimize")
start_time = time.perf_counter()
study.optimize(objective, n_trials=max_iter, show_progress_bar=False)
elapsed_micros = (time.perf_counter() - start_time) * 1e6
best_trial = study.best_trial
best_f = best_trial.value
params = best_trial.params
angles = [params["theta_" + str(i)] for i in range(n_seg)]
if var_len:
lengths = [params["L_" + str(i)] for i in range(n_seg)]
else:
lengths = [1.0] * n_seg
best_x, best_y = forward_kinematics(angles, lengths)
trials = study.trials
iterations = len(trials)
recent_trials = max(1, iterations // 10)
recent_values = [t.value for t in trials[-recent_trials:] if t.value is not None]
achieved_eps = statistics.stdev(recent_values)
q_values = angles + lengths
print("BEST_F", best_f)
print("BEST_X", best_x)
print("BEST_Y", best_y)
print("ITERATIONS", iterations)
print("EPS", achieved_eps)
print("TIME", int(elapsed_micros))
print("Q", " ".join(str(q) for q in q_values))
def run_iopt(n_seg, var_len, min_theta, tx, ty, max_iter, r_param, eps, adaptive):
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
build = build_angles_lengths
cost = manipulator_cost

text

def objective(trial):
angles, lengths = build(trial, n_seg, var_len)
return cost(angles, lengths, tx, ty, min_theta)

study = iOpt.create_study()
params = iOpt.SolverParameters(r=r_param, eps=eps, iters_limit=max_iter, refine_solution=adaptive)
study.optimize(objective=objective, solver_parameters=params, type_of_painter="none", console_mode="off")
sys.stdout = old_stdout
sys.stderr = old_stderr
best_params = study.best_float_params_()
angles = best_params[:n_seg]
if var_len:
lengths = best_params[n_seg:n_seg * 2]
else:
lengths = [1.0] * n_seg
best_f = study.best_values_()
best_x, best_y = forward_kinematics(angles, lengths)
solution = getattr(study, "solution", None)
achieved_eps = solution.solution_accuracy
iterations = solution.number_of_global_trials
elapsed_micros = solution.solving_time * 1e6
q_values = angles + lengths
print("BEST_F", best_f)
print("BEST_X", best_x)
print("BEST_Y", best_y)
print("ITERATIONS", iterations)
print("EPS", achieved_eps)
print("TIME", int(elapsed_micros))
print("Q", " ".join(str(q) for q in q_values))
def main():
argv = sys.argv
backend = argv[1]
n_seg = int(argv[2])
var_len = bool(int(argv[3]))
min_theta = float(argv[4])
tx = float(argv[5])
ty = float(argv[6])
_levels = int(argv[7])
max_iter = int(argv[8])
r_param = float(argv[9])
eps = float(argv[10])
adaptive = bool(int(argv[11]))
if backend == "optuna":
run_optuna(n_seg, var_len, min_theta, tx, ty, max_iter, r_param, eps)
elif backend == "iopt":
run_iopt(n_seg, var_len, min_theta, tx, ty, max_iter, r_param, eps, adaptive)

if name == "main":
main()" - реализация верна, все алгоритмы считаются правильно - так что не трать время на проверку корректности реализаций - тут всё точно хорошо, сконцентрируйся на построении графиков, я думаю скопировать целиком это приложение и заменить визуальный интерфейс добавив там объект c++/clr chart для отрисовки графиков, то есть можно вообще убрать все кнопки и все поля и лейблы и т д из интерфейса а сделать так чтобы сразу при загрузке формы рисовались три графика на одном chart для зависимости времени выполнения от числа итераций - имеется ввиду что должно под капотом происходить много запусков каждого алгоритма при параметрах по умолчанию - при тех которые сейчас установлены, для iOpt например как для полностью детерменированного алгоритма можно делать по одному запуску при допустимом максимальном числе итераций начиная с 100 заканчивая 1000 с шагом 100, но на графиках по оси абсцисс откладывать всё равно реально затраченное число итераций, по оси ординат - затраченное время, для optuna - делать по 10 испытаний при каждом пороге итераций - 10 испытаний при 100 итерациях, 10 при 200 - и т д тоже до 1000, - и откладывать для каждого порога итераций среднее значение по результатам 10 испытаний для каждого порога, для AGP также как для optuna нужно проводить много испытаний - так как AGP тоже не детерменирован, тоже по 10 испытаний на каждый порог итераций и вывод среднего - также для алгоритма AGP нужно будет убрать полностью условие останова по точности - чтобы останов только по макс числу итераций, также в приложении должны выводиться результаты для всех трёх реализаций и статистика в отдельные окошке с учётом стандартного отклонения и корня из среднеквадратической ошибки как например в предыдущей моей работе: "Для корректного сравнения алгоритмов при фиксированных дискретных параметрах и варьируемых непрерывных значениях для Optuna аналогично предыдущему сравнению проведем серию из 20 независимых запусков.Как и ранее, будем усреднять полученные значения, рассчитывать стандартное отклонение (σ) и корень из среднеквадратической ошибки (√MSE) относительно детерминированного решения iOpt. Результаты представим в виде таблиц для наглядного сравнения производительности алгоритмов в новых условиях задачи, результаты сравнения наглядно представим в следующих таблицах:Таблица 1.Сравнение результатов оптимизации: Optuna vs iOptvs собственная реализацияOptuna (μ ± σ)iOptсобственная реализацияN435± 41257295t5.575± 0.9260.2044540.054429f -97.12516765± 5.647-98.83353268-98.699477x10.00137987± 0.0005-0.002734380.007035065x20.061889± 0.2930.012890620.030472564697
24Таблица 2.Отклонение результатов Optuna и собственной реализации от iOpt√MSE(для Optuna)|∆|(для собственной реализации)N (число шагов метода)22038t (время выполнения)5.6470.150025f (найденный оптимум)3.9870.13405568x10.0078680.004300685x20.3140.017581945Как видно из результатов, для непрерывного случая iOpt, так же,как и для смешанного, демонстрирует более высокую эффективность по сравнению с Optuna при оптимизации функции Растригина, что ожидаемо ввиду реализации в iOptдетерминированного метода липшицевой оптимизации. Алгоритм обеспечивает гарантированную сходимость к глобальному экстремуму с заданной точностью, что объясняет его стабильно высокие результаты в обоих типах задач.Несмотря на использование непрерывных параметров, Optuna вновь продемонстрировала значительный разброс результатов.Результаты подтверждают общее свойство байесовской оптимизации: при достаточном количестве испытаний вероятностный алгоритм может достигать высокой точности, однако:требует существенно больше вычислительных ресурсов;демонстрирует нестабильность результатов между запусками (что было показано в предыдущем эксперименте);сильно зависит от удачности выборки точек на начальных итерациях.При сравнении iOpt с предложенным алгоритмом на C++ наблюдается схожееповедение, что ожидаемо, поскольку обе реализации используют детерминированный подход:число итераций: 257 (iOpt) vs 295 (C++) -различие в пределах 15%;найденный оптимум: -98.8335 (iOpt) vs -98.6995 (C++) -расхождение менее 0.14%;" - графики зависимости времени выполнения от числа итераций должны начинаться из нуля, аналогично построй графики зависимости значения целевой функции от числа итераций (именно значения целевой функции не точности!), для времени подбери адекватный масштаб или полагайся на автоподбор масштаба chat c++/clr, для второго графика chart (опять же с трёма линиями для каждой реализации) - аналогично - мне верни полный исправленный код интерфейса и где у AGP отключить останов по достигнутой точности

Pensamento

Thought for 2m 42s

Resposta
Сделаю так:

Покажу, что именно надо поменять в AGP, чтобы останавливаться только по числу итераций.
Затем дам полный новый код MyForm.h и MyForm.cpp, где
нет кнопок/полей,
при показе формы автоматически гоняются серии запусков трёх алгоритмов,
строятся 2 графика (Chart):
время vs число итераций,
значение целевой функции vs число итераций,
справа выводится текстовая статистика с μ, σ и √MSE относительно iOpt.

  1. Где в AGP отключить останов по достигнутой точности
    1.1. agp_run_branch_mpi (многомерный AGP / AGP_Manip2D)
    Внутри функции agp_run_branch_mpi в конце основного цикла while (it < maxIter) у вас сейчас:

cpp

if ((exp2f((1.0f / dim_f) * log2f(interval_len)) < eps) || (it == maxIter)) {
out_iterations = static_cast<size_t>(it);
out_achieved_epsilon = interval_len;
return;
}
Нужно убрать остановку по точности, оставив только ограничение по итерациям:

cpp

if (it == maxIter) {
out_iterations = static_cast<size_t>(it);
// Можно оставить как есть для логирования «достигнутой точности»
out_achieved_epsilon = interval_len;
return;
}
То есть eps остаётся параметром, но больше не используется как критерий останова.

1.2. AGP_1D
Сейчас:

cpp

if (++counter == static_cast<int>(global_iterations) || interval_len < epsilon) {
Extr.emplace_back(static_cast<float>(counter));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = static_cast<float>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
return;
}
Должно стать:

cpp

if (++counter == static_cast<int>(global_iterations)) {
Extr.emplace_back(static_cast<float>(counter));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = static_cast<float>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
return;
}
1.3. AGP_2D
Сейчас:

cpp

const bool want_term = interval_len < epsilon || counter == static_cast<int>(global_iterations);
...
if (want_term) {
if (!rank) {
Extr.emplace_back(static_cast<float>(counter));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = reinterpret_cast<float __restrict>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
}
return;
}
Нужно:

cpp

const bool want_term = (counter == static_cast<int>(global_iterations));
...
if (want_term) {
if (!rank) {
Extr.emplace_back(static_cast<float>(counter));
Extr.emplace_back(interval_len);
*out_len = Extr.size();
out_data = reinterpret_cast<float __restrict>(CoTaskMemAlloc(sizeof(float) * (*out_len)));
memcpy(*out_data, Extr.data(), sizeof(float) * (*out_len));
}
return;
}
После этих трёх правок все AGP-варианты будут останавливаться только по лимиту итераций, что вам и нужно для «честного» сравнения.

  1. Новый интерфейс: только графики + статистика
    Ниже — полный новый код MyForm.h и актуальный MyForm.cpp.

Важные моменты, что делает новый интерфейс
При запуске (ранг 0):

форма создаётся,
без кнопок и NumericUpDown, только:
Chart с графиком время vs число итераций (3 линии: AGP, Optuna, iOpt),
Chart с графиком f vs число итераций (3 линии),
справа — TextBox со сводной статистикой.
Под капотом:

берутся жёстко заданные параметры (аналог текущих дефолтов):
cpp

nSegments = 1;
variableLengths = false;
minTheta = 1.5f;
targetX = 2.5f;
targetY = -1.0f;
peanoLevels = 12;
rParam = 2.5f;
epsParam = 1.0e-5f; // хоть eps и не влияет на остановку AGP
при желании вы их легко поправите в одном месте.
набор порогов максимального числа итераций:
cpp

{100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}
iOpt: по одному запуску для каждого порога; на графиках:
X = реальное число шагов iterations,
Y = время (мкс) или значение f.
Optuna: по 10 запусков для каждого порога:
X — среднее число итераций по 10 запускам,
Y — среднее время / среднее значение f,
также считается σ по этим 10 значениям.
AGP (C++): 10 запусков на каждый порог:
перед каждым запуском вызывается AgpStartManipND(...),
измеряется время вокруг AGP_Manip2D(...) через QueryPerformanceCounter,
берутся actualIterations и bestF из AGP,
считаются среднее и σ.
по завершении:
для каждого алгоритма собираются все сырые значения времени и f,
считается μ и σ,
для Optuna и AGP считается √MSE относительно iOpt по времени и по f (по всем точкам). Для iOpt √MSE = 0.
Ось X временного графика начинается с нуля (AxisX->Minimum = 0; AxisY->Minimum = 0 для времени, и AxisX->Minimum = 0 для графика f).

  1. Новый MyForm.h
    cpp

#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstddef>

using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
using namespace System::Collections::Generic;
using namespace System::Diagnostics;
using namespace System::Globalization;
using namespace System::Runtime::CompilerServices;
using namespace System::IO;
using namespace System::Windows::Forms::DataVisualization::Charting;

typedef void(__cdecl* P_MANIP)(
int, bool, float, float, float, int, int, float, bool, float, unsigned int,
float**, size_t*, float*, float*, float*, size_t*, float*
);

typedef void(__cdecl* P_FREE)(float*);
typedef void(__cdecl* P_START)(
int, bool, float, float, float, int, int, float, bool, float, unsigned int
);

namespace TESTAGP {

public enum class OptimizerKind {
AgpCpp = 0,
Optuna = 1,
IOpt = 2
};

public ref class MyForm sealed : public Form {
public:
MyForm(HMODULE hLib)
: hLib(hLib),
experimentsStarted(false)
{
this->Text = L"AGP vs Optuna vs iOpt – сравнение производительности";
this->ClientSize = System::Drawing::Size(1200, 800);
this->SetStyle(ControlStyles::AllPaintingInWmPaint |
ControlStyles::UserPaint |
ControlStyles::OptimizedDoubleBuffer, true);

text
fManip = reinterpret_cast<P_MANIP>(GetProcAddress(hLib, "AGP_Manip2D")); pFree = reinterpret_cast<P_FREE>(GetProcAddress(hLib, "AGP_Free")); pStart = reinterpret_cast<P_START>(GetProcAddress(hLib, "AgpStartManipND")); if (!fManip || !pFree || !pStart) { MessageBox::Show( L"Не удалось получить адреса функций из TEST_FUNC.dll", L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error); this->Close(); return; } // Параметры задачи – аналоги текущих дефолтов в интерфейсе nSegments = 1; // при желании поменяйте на 3, 4, ... variableLengths = false; minTheta = 1.5f; // рад targetX = 2.5f; targetY = -1.0f; peanoLevels = 12; rParam = 2.5f; epsParam = 1.0e-5f; // в AGP на остановку уже не влияет uiFontBold11 = gcnew System::Drawing::Font("Yu Gothic UI", 11, FontStyle::Bold); uiFontBold10 = gcnew System::Drawing::Font("Yu Gothic UI", 10, FontStyle::Regular); pythonExe = Path::Combine(Application::StartupPath, "env", "Scripts", "python.exe"); scriptPath = Path::Combine(Application::StartupPath, "optimizer_bridge.py"); InitUI(); this->Shown += gcnew EventHandler(this, &MyForm::OnFormShown); this->Resize += gcnew EventHandler(this, &MyForm::OnResizeInternal); }

private:
// --- поля ---

text
initonly HMODULE hLib; initonly P_MANIP fManip; initonly P_FREE pFree; initonly P_START pStart; System::Drawing::Font^ uiFontBold11; System::Drawing::Font^ uiFontBold10; Chart^ chartTime; Chart^ chartF; TextBox^ tbStats; bool experimentsStarted; // Параметры задачи для всех трёх алгоритмов int nSegments; bool variableLengths; float minTheta; float targetX; float targetY; int peanoLevels; float rParam; float epsParam; String^ pythonExe; String^ scriptPath; // --- UI --- void InitUI() { chartTime = gcnew Chart(); chartF = gcnew Chart(); tbStats = gcnew TextBox(); chartTime->Parent = this; chartF->Parent = this; tbStats->Parent = this; chartTime->Text = L"Время vs итерации"; chartF->Text = L"f vs итерации"; chartTime->BorderlineDashStyle = ChartDashStyle::Solid; chartTime->BorderlineWidth = 1; chartTime->BorderlineColor = Color::Black; chartF->BorderlineDashStyle = ChartDashStyle::Solid; chartF->BorderlineWidth = 1; chartF->BorderlineColor = Color::Black; ChartArea^ areaTime = gcnew ChartArea("TimeArea"); areaTime->AxisX->Title = "Число итераций"; areaTime->AxisY->Title = "Время, мкс"; areaTime->AxisX->Minimum = 0.0; // график начинается с нуля areaTime->AxisY->Minimum = 0.0; chartTime->ChartAreas->Add(areaTime); Legend^ legendTime = gcnew Legend("LegendTime"); legendTime->Docking = Docking::Top; chartTime->Legends->Add(legendTime); // три серии времени: AGP, Optuna, iOpt CreateSeries(chartTime, "AGP", "TimeArea"); CreateSeries(chartTime, "Optuna", "TimeArea"); CreateSeries(chartTime, "iOpt", "TimeArea"); ChartArea^ areaF = gcnew ChartArea("FArea"); areaF->AxisX->Title = "Число итераций"; areaF->AxisY->Title = "Значение целевой функции f"; areaF->AxisX->Minimum = 0.0; // по X тоже с нуля chartF->ChartAreas->Add(areaF); Legend^ legendF = gcnew Legend("LegendF"); legendF->Docking = Docking::Top; chartF->Legends->Add(legendF); // три серии f: AGP, Optuna, iOpt CreateSeries(chartF, "AGP", "FArea"); CreateSeries(chartF, "Optuna", "FArea"); CreateSeries(chartF, "iOpt", "FArea"); tbStats->Multiline = true; tbStats->ReadOnly = true; tbStats->ScrollBars = ScrollBars::Vertical; tbStats->Font = uiFontBold10; tbStats->Text = L"Подготовка..."; OnResizeInternal(nullptr, nullptr); } void CreateSeries(Chart^ chart, String^ name, String^ areaName) { Series^ s = gcnew Series(name); s->ChartType = SeriesChartType::Line; s->ChartArea = areaName; s->BorderWidth = 3; s->MarkerStyle = MarkerStyle::Circle; s->MarkerSize = 6; chart->Series->Add(s); } void OnResizeInternal(Object^, EventArgs^) { int margin = 10; int statsWidth = 280; int totalWidth = this->ClientSize.Width; int totalHeight = this->ClientSize.Height; if (totalWidth < 400) totalWidth = 400; if (totalHeight < 300) totalHeight = 300; int chartsWidth = totalWidth - statsWidth - 3 * margin; if (chartsWidth < 200) chartsWidth = 200; int chartHeight = (totalHeight - 3 * margin) / 2; chartTime->Location = Point(margin, margin); chartTime->Size = System::Drawing::Size(chartsWidth, chartHeight); chartF->Location = Point(margin, 2 * margin + chartHeight); chartF->Size = System::Drawing::Size(chartsWidth, chartHeight); tbStats->Location = Point(2 * margin + chartsWidth, margin); tbStats->Size = System::Drawing::Size(statsWidth, totalHeight - 2 * margin); } void OnFormShown(Object^, EventArgs^) { if (experimentsStarted) return; experimentsStarted = true; try { RunAllExperiments(); } catch (Exception^ ex) { tbStats->Text = L"Ошибка при выполнении экспериментов:\r\n" + ex->ToString(); } } // --- Вспомогательные статистические функции --- static void ComputeMeanStd(List<double>^ values, double% mean, double% stdDev) { int n = values->Count; if (n == 0) { mean = Double::NaN; stdDev = Double::NaN; return; } double sum = 0.0; for each (double v in values) { sum += v; } mean = sum / n; if (n == 1) { stdDev = 0.0; return; } double var = 0.0; for each (double v in values) { double d = v - mean; var += d * d; } stdDev = Math::Sqrt(var / (n - 1)); } static double ComputeMean(List<double>^ values) { double m, s; ComputeMeanStd(values, m, s); return m; } static bool ParseDouble(String^ s, double% outVal) { return Double::TryParse( s, NumberStyles::Float | NumberStyles::AllowThousands, CultureInfo::InvariantCulture, outVal ); } // --- Запуск Python-бэкенда (Optuna / iOpt) один раз --- bool RunPythonSingle( String^ backend, int maxIter, double% outBestF, double% outBestX, double% outBestY, int% outIterations, double% outEps, double% outMicros) { if (!File::Exists(pythonExe)) { throw gcnew InvalidOperationException( L"Не найден python.exe по пути: " + pythonExe); } if (!File::Exists(scriptPath)) { throw gcnew InvalidOperationException( L"Не найден optimizer_bridge.py по пути: " + scriptPath); } ProcessStartInfo^ psi = gcnew ProcessStartInfo(); psi->FileName = pythonExe; int varLenInt = variableLengths ? 1 : 0; int adaptiveInt = 1; // для Optuna игнорируется, для iOpt используется psi->Arguments = String::Format( CultureInfo::InvariantCulture, "\"{0}\" {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11}", scriptPath, backend, nSegments, varLenInt, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, epsParam, adaptiveInt ); psi->UseShellExecute = false; psi->CreateNoWindow = true; psi->RedirectStandardOutput = true; psi->RedirectStandardError = false; psi->WorkingDirectory = Application::StartupPath; Process^ proc = Process::Start(psi); if (proc == nullptr) { throw gcnew InvalidOperationException( L"Не удалось запустить Python-процесс."); } String^ stdoutAll = proc->StandardOutput->ReadToEnd(); proc->WaitForExit(); double bestF = Double::NaN; double bestX = Double::NaN; double bestY = Double::NaN; double eps = Double::NaN; double micros = Double::NaN; int iterations = 0; array<wchar_t>^ lineSep = gcnew array<wchar_t>{ '\r', '\n' }; array<String^>^ lines = stdoutAll->Split( lineSep, StringSplitOptions::RemoveEmptyEntries ); for each (String ^ line in lines) { array<wchar_t>^ partSep = gcnew array<wchar_t>{ ' ', '\t' }; array<String^>^ parts = line->Split( partSep, StringSplitOptions::RemoveEmptyEntries ); if (parts->Length == 0) continue; String^ key = parts[0]; if (key == "Q") { // Нам Q тут не нужен для графиков continue; } if (parts->Length < 2) continue; String^ valStr = parts[1]; if (key == "BEST_F") { ParseDouble(valStr, bestF); } else if (key == "BEST_X") { ParseDouble(valStr, bestX); } else if (key == "BEST_Y") { ParseDouble(valStr, bestY); } else if (key == "ITERATIONS") { Int32::TryParse(valStr, iterations); } else if (key == "EPS") { ParseDouble(valStr, eps); } else if (key == "TIME") { ParseDouble(valStr, micros); } } outBestF = bestF; outBestX = bestX; outBestY = bestY; outIterations = iterations; outEps = eps; outMicros = micros; return true; } // --- Запуск одного AGP (C++) на maxIter --- bool RunAgpSingle( int maxIter, double% outBestF, double% outBestX, double% outBestY, std::size_t% outIterations, double% outAchievedEps, double% outMicros) { float* bestQ = nullptr; std::size_t bestQLen = 0; float bestXf = 0.0f; float bestYf = 0.0f; float bestFf = 0.0f; std::size_t actualIterations = 0; float achievedEps = 0.0f; unsigned int seed = static_cast<unsigned int>(GetTickCount()); // сообщаем воркерам параметры pStart( nSegments, variableLengths, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, true, // adaptiveMode: по умолчанию включаем epsParam, seed ); LARGE_INTEGER t0, t1, fq; QueryPerformanceCounter(&t0); // сам запуск AGP fManip( nSegments, variableLengths, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, true, // adaptiveMode epsParam, seed, &bestQ, &bestQLen, &bestXf, &bestYf, &bestFf, &actualIterations, &achievedEps ); QueryPerformanceCounter(&t1); QueryPerformanceFrequency(&fq); double micros = 1.0e6 * static_cast<double>(t1.QuadPart - t0.QuadPart) / static_cast<double>(fq.QuadPart); if (bestQ != nullptr) { pFree(bestQ); } outBestF = static_cast<double>(bestFf); outBestX = static_cast<double>(bestXf); outBestY = static_cast<double>(bestYf); outIterations = actualIterations; outAchievedEps = static_cast<double>(achievedEps); outMicros = micros; return true; } // --- Основной эксперимент: многократные прогоны и построение графиков --- void RunAllExperiments() { // Пороги максимального числа итераций array<int>^ thresholds = gcnew array<int>{ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; const int steps = thresholds->Length; const int repeatsStochastic = 10; // для AGP и Optuna // Массивы средних значений и std по порогам array<double>^ agpIterMean = gcnew array<double>(steps); array<double>^ agpTimeMean = gcnew array<double>(steps); array<double>^ agpTimeStd = gcnew array<double>(steps); array<double>^ agpFMean = gcnew array<double>(steps); array<double>^ agpFStd = gcnew array<double>(steps); array<double>^ optunaIterMean = gcnew array<double>(steps); array<double>^ optunaTimeMean = gcnew array<double>(steps); array<double>^ optunaTimeStd = gcnew array<double>(steps); array<double>^ optunaFMean = gcnew array<double>(steps); array<double>^ optunaFStd = gcnew array<double>(steps); array<double>^ ioptIter = gcnew array<double>(steps); array<double>^ ioptTime = gcnew array<double>(steps); array<double>^ ioptF = gcnew array<double>(steps); // Инициализируем NaN'ами, чтобы не рисовать незаполненные точки for (int i = 0; i < steps; ++i) { agpIterMean[i] = Double::NaN; agpTimeMean[i] = Double::NaN; agpTimeStd[i] = Double::NaN; agpFMean[i] = Double::NaN; agpFStd[i] = Double::NaN; optunaIterMean[i] = Double::NaN; optunaTimeMean[i] = Double::NaN; optunaTimeStd[i] = Double::NaN; optunaFMean[i] = Double::NaN; optunaFStd[i] = Double::NaN; ioptIter[i] = Double::NaN; ioptTime[i] = Double::NaN; ioptF[i] = Double::NaN; } // Для глобальных μ, σ List<double>^ agpTimesAll = gcnew List<double>(); List<double>^ agpFAll = gcnew List<double>(); List<double>^ optunaTimesAll = gcnew List<double>(); List<double>^ optunaFAll = gcnew List<double>(); List<double>^ ioptTimesAll = gcnew List<double>(); List<double>^ ioptFAll = gcnew List<double>(); // Для MSE по порогам относительно iOpt (берём средние по порогу) double mseTimeAgp = 0.0; double mseTimeOptuna = 0.0; double mseFAgp = 0.0; double mseFOptuna = 0.0; int mseCount = 0; for (int idx = 0; idx < steps; ++idx) { int maxIter = thresholds[idx]; // --- iOpt: один запуск для порога --- double i_bestF, i_bestX, i_bestY, i_eps, i_micros; int i_iters; RunPythonSingle(L"iopt", maxIter, i_bestF, i_bestX, i_bestY, i_iters, i_eps, i_micros); ioptIter[idx] = static_cast<double>(i_iters); ioptTime[idx] = i_micros; ioptF[idx] = i_bestF; ioptTimesAll->Add(i_micros); ioptFAll->Add(i_bestF); // --- Optuna: 10 запусков --- List<double>^ optunaIterLoc = gcnew List<double>(); List<double>^ optunaTimeLoc = gcnew List<double>(); List<double>^ optunaFLoc = gcnew List<double>(); for (int r = 0; r < repeatsStochastic; ++r) { double o_bestF, o_bestX, o_bestY, o_eps, o_micros; int o_iters; RunPythonSingle(L"optuna", maxIter, o_bestF, o_bestX, o_bestY, o_iters, o_eps, o_micros); optunaIterLoc->Add(static_cast<double>(o_iters)); optunaTimeLoc->Add(o_micros); optunaFLoc->Add(o_bestF); optunaTimesAll->Add(o_micros); optunaFAll->Add(o_bestF); Application::DoEvents(); } optunaIterMean[idx] = ComputeMean(optunaIterLoc); ComputeMeanStd(optunaTimeLoc, optunaTimeMean[idx], optunaTimeStd[idx]); ComputeMeanStd(optunaFLoc, optunaFMean[idx], optunaFStd[idx]); // --- AGP C++: 10 запусков --- List<double>^ agpIterLoc = gcnew List<double>(); List<double>^ agpTimeLoc = gcnew List<double>(); List<double>^ agpFLoc = gcnew List<double>(); for (int r = 0; r < repeatsStochastic; ++r) { double a_bestF, a_bestX, a_bestY, a_eps, a_micros; std::size_t a_iters; RunAgpSingle(maxIter, a_bestF, a_bestX, a_bestY, a_iters, a_eps, a_micros); agpIterLoc->Add(static_cast<double>(a_iters)); agpTimeLoc->Add(a_micros); agpFLoc->Add(a_bestF); agpTimesAll->Add(a_micros); agpFAll->Add(a_bestF); Application::DoEvents(); } agpIterMean[idx] = ComputeMean(agpIterLoc); ComputeMeanStd(agpTimeLoc, agpTimeMean[idx], agpTimeStd[idx]); ComputeMeanStd(agpFLoc, agpFMean[idx], agpFStd[idx]); // --- обновляем графики по мере накопления данных --- UpdateCharts( steps, idx, agpIterMean, agpTimeMean, optunaIterMean, optunaTimeMean, ioptIter, ioptTime, agpFMean, optunaFMean, ioptF ); // --- вклад в MSE относительно iOpt (если данные есть) --- if (!Double::IsNaN(ioptTime[idx]) && !Double::IsNaN(agpTimeMean[idx]) && !Double::IsNaN(optunaTimeMean[idx]) && !Double::IsNaN(agpFMean[idx]) && !Double::IsNaN(optunaFMean[idx])) { double dtAgp = agpTimeMean[idx] - ioptTime[idx]; double dtOptuna = optunaTimeMean[idx] - ioptTime[idx]; double dfAgp = agpFMean[idx] - ioptF[idx]; double dfOptuna = optunaFMean[idx] - ioptF[idx]; mseTimeAgp += dtAgp * dtAgp; mseTimeOptuna += dtOptuna * dtOptuna; mseFAgp += dfAgp * dfAgp; mseFOptuna += dfOptuna * dfOptuna; ++mseCount; } } // --- Итоговая статистика по всем точкам --- double agpTimeMu, agpTimeSigma; double agpFMu, agpFSigma; double optTimeMu, optTimeSigma; double optFMu, optFSigma; double ioptTimeMu, ioptTimeSigma; double ioptFMu, ioptFSigma; ComputeMeanStd(agpTimesAll, agpTimeMu, agpTimeSigma); ComputeMeanStd(agpFAll, agpFMu, agpFSigma); ComputeMeanStd(optunaTimesAll, optTimeMu, optTimeSigma); ComputeMeanStd(optunaFAll, optFMu, optFSigma); ComputeMeanStd(ioptTimesAll, ioptTimeMu, ioptTimeSigma); ComputeMeanStd(ioptFAll, ioptFMu, ioptFSigma); double rmseTimeAgp = (mseCount > 0) ? Math::Sqrt(mseTimeAgp / mseCount) : 0.0; double rmseTimeOptuna = (mseCount > 0) ? Math::Sqrt(mseTimeOptuna / mseCount) : 0.0; double rmseFAgp = (mseCount > 0) ? Math::Sqrt(mseFAgp / mseCount) : 0.0; double rmseFOptuna = (mseCount > 0) ? Math::Sqrt(mseFOptuna / mseCount) : 0.0; // --- выводим сводную статистику в правое окошко --- System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder(); sb->AppendLine(L"Сводная статистика (по всем порогам 100–1000)"); sb->AppendLine(L"==========================================="); sb->AppendLine(); sb->AppendLine(L"iOpt (детерминированный, 1 запуск на порог):"); sb->AppendFormat(L" Время t, мкс: μ = {0:F1}, σ = {1:F1}\r\n", ioptTimeMu, ioptTimeSigma); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}\r\n", ioptFMu, ioptFSigma); sb->AppendLine(); sb->AppendLine(L"AGP (C++, 10 запусков на каждый порог):"); sb->AppendFormat(L" Время t, мкс: μ = {0:F1}, σ = {1:F1}, √MSE(t vs iOpt) = {2:F1}\r\n", agpTimeMu, agpTimeSigma, rmseTimeAgp); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}, √MSE(f vs iOpt) = {2:F5}\r\n", agpFMu, agpFSigma, rmseFAgp); sb->AppendLine(); sb->AppendLine(L"Optuna (10 запусков на каждый порог):"); sb->AppendFormat(L" Время t, мкс: μ = {0:F1}, σ = {1:F1}, √MSE(t vs iOpt) = {2:F1}\r\n", optTimeMu, optTimeSigma, rmseTimeOptuna); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}, √MSE(f vs iOpt) = {2:F5}\r\n", optFMu, optFSigma, rmseFOptuna); tbStats->Text = sb->ToString(); } // Обновление графиков после очередного порога void UpdateCharts( int steps, int filledUpTo, array<double>^ agpIterMean, array<double>^ agpTimeMean, array<double>^ optunaIterMean, array<double>^ optunaTimeMean, array<double>^ ioptIter, array<double>^ ioptTime, array<double>^ agpFMean, array<double>^ optunaFMean, array<double>^ ioptF) { Series^ sAgpTime = chartTime->Series["AGP"]; Series^ sOptTime = chartTime->Series["Optuna"]; Series^ sIoptTime = chartTime->Series["iOpt"]; Series^ sAgpF = chartF->Series["AGP"]; Series^ sOptF = chartF->Series["Optuna"]; Series^ sIoptF = chartF->Series["iOpt"]; sAgpTime->Points->Clear(); sOptTime->Points->Clear(); sIoptTime->Points->Clear(); sAgpF->Points->Clear(); sOptF->Points->Clear(); sIoptF->Points->Clear(); for (int i = 0; i <= filledUpTo && i < steps; ++i) { if (!Double::IsNaN(agpTimeMean[i])) { sAgpTime->Points->AddXY(agpIterMean[i], agpTimeMean[i]); } if (!Double::IsNaN(optunaTimeMean[i])) { sOptTime->Points->AddXY(optunaIterMean[i], optunaTimeMean[i]); } if (!Double::IsNaN(ioptTime[i])) { sIoptTime->Points->AddXY(ioptIter[i], ioptTime[i]); } if (!Double::IsNaN(agpFMean[i])) { sAgpF->Points->AddXY(agpIterMean[i], agpFMean[i]); } if (!Double::IsNaN(optunaFMean[i])) { sOptF->Points->AddXY(optunaIterMean[i], optunaFMean[i]); } if (!Double::IsNaN(ioptF[i])) { sIoptF->Points->AddXY(ioptIter[i], ioptF[i]); } } chartTime->Invalidate(); chartF->Invalidate(); this->Refresh(); }

};

} // namespace TESTAGP
4. Обновлённый MyForm.cpp
Тут изменений почти нет — только по смыслу: мы используем новый MyForm:

cpp

#include "MyForm.h"

using namespace System;
using namespace System::Windows::Forms;

typedef int(__cdecl* PInit)(int, float, float, float, float);
typedef void(__cdecl* PStartWorkers)();

[STAThread]
int main() {
HMODULE h = LoadLibraryW(L"TEST_FUNC.dll");
if (!h) {
MessageBox::Show(L"Не удалось загрузить TEST_FUNC.dll",
L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error);
return 1;
}

text
auto AgpInit = (PInit)GetProcAddress(h, "AgpInit"); auto AgpWaitStartAndRun = (PStartWorkers)GetProcAddress(h, "AgpWaitStartAndRun"); if (!AgpInit || !AgpWaitStartAndRun) { MessageBox::Show(L"Не удалось получить AgpInit/AgpWaitStartAndRun из TEST_FUNC.dll", L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error); return 1; } const int rank = AgpInit(12, -2.2f, 1.8f, -2.2f, 1.8f); if (!rank) { Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); Application::Run(gcnew TESTAGP::MyForm(h)); } else { // worker-процессы: ждут параметров и гоняют AGP_Manip2D AgpWaitStartAndRun(); } return 0;

}
5. Что ещё нужно не забыть в проекте
Ссылка на Chart
В проекте C++/CLI в References нужно добавить:

System.Windows.Forms.DataVisualization
Python окружение
Ожидается, что:

в $(OutDir)\env\Scripts\python.exe лежит ваш Python,
рядом с exe находится optimizer_bridge.py (тот, что вы привели).
MPI-запуск
Всё, как у вас уже было настроено: TEST_FUNC.dll с AGP, запуск рангов через MPI/MPICH и т.п. — внешний механизм не меняется." Эти графики и тот ответ который дал мне chat01ai - это всё правильно - я просто следовал указаниям chat01ai и копировал его код в своё приложение и всё получилось как надо, но загвоздка в том что это всё строилось для старой версии приложения а сейчас у меня приложение изменилось так что я скину тебе как выглядят мои новые файлы приложения а ты уже опираясь на ответ chat01ai в первую очередь скажешь мне как я должен изменить новое приложение чтобы оно построило графики - и сейчас пусть для недетрменированных методов оптимизации будет не 10 испытаний на порог а 20 - так будет надёжнее, и научный руководитель сказал увидев графики - что они у меня обрываются в начале а должны откуда-то начинаться - поэтому для графиков на первом чарте сделай чтобы они начинали идти из точки (0, 0), а для графиков на втором чарте сделай чтобы по оси абсцисс начало было не в нуле а в точке первого порога по итерациям - то есть начиная со 100 итераций, также научный руководитель упомянул что время в микросекундах на оси ординат выглядит странно - так что используй миллисекунды а не микросекунды для замера времени для более адекватных и более малых чисел по оси ординат, также для фреймворка optuna как для недетрменированного алгоритма нужно помимо усреднённого графика нарисовать также сверху него график лучших испытаний optuna - а под ним график худших испытаний optuna - это касается обоих чартов, вывод статистики справа как у chat01ai оставь - только измени там 10 испытаний на 20 где это встречается в справке справа от графиков, испытания пусть проводятся также для трёхзвенного робота-манипулятора, корень из среднеквадратической ошибки и другие вычисления и их вывод справа в справке оставь, итак файлы моего обновлённого приложения: MyForm.h: "ПО СУТИ ТОТ ЖЕ КОД ЧТО ИЗНАЧАЛЬНО БЫЛ ПО СУЩЕСТВУ ОН НЕ ОТЛИЧАЕТСЯ", код основного .cpp файла моей dll библиотеки: "
/// БЕЗ ИЗМЕНЕНИЙ
static const boost::mpi::environment* g_env;
static const boost::mpi::communicator* g_world;
static const pybind11::scoped_interpreter* g_pyInterpreter;
static const pybind11::module_* g_pyOptimizerBridge;
static std::string g_exeDirCache;
/// БЕЗ ИЗМЕНЕНИЙ
static __declspec(noalias) void agp_run_branch_mpi(
const MortonND& map, const ManipCost& cost, const int maxIter, const float r, const bool adaptive, const float eps, const unsigned seed,
std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>>& H,
std::vector<float, boost::alignment::aligned_allocator<float, 16u>>& bestQ,
float& bestF, float& bestX, float& bestY, size_t& out_iterations, float& out_achieved_epsilon, const float M_prior = 1e-3f)
noexcept {
const int n = cost.n;
const int dim = n + (cost.variableLen ? n : 0);
const float dim_f = static_cast<float>(dim);
unsigned exchange_counter_500 = 0;
unsigned exchange_counter_T = 0;

text
alignas(16) float M_by_span[12]; int msi = 0; while (msi < 12) { M_by_span[msi++] = M_prior; } float Mmax = M_prior; alignas(16) float q_local[32], phi[32], s_arr[32], c_arr[32], sum_s[32], sum_c[32], q_try[32]; bestQ.reserve(static_cast<size_t>(dim)); float x = 0.0f, y = 0.0f; int no_improve = 0; auto t_to_idx = [&](const float t) -> unsigned long long { unsigned long long idx = static_cast<unsigned long long>(fmaf(t, static_cast<float>(map.scale), 0.0f)); return idx; }; auto update_pockets_and_Mmax = [&](IntervalND* const I) { const int k = I->span_level; if (I->M > M_by_span[k]) M_by_span[k] = I->M; if (M_by_span[k] > Mmax) Mmax = M_by_span[k]; }; const float a = 0.0f, b = 1.0f; auto evalAt = [&](const float t) -> float { map.map01ToPoint(t, q_local); float f = cost(q_local, x, y); if (f < bestF * 1.25f) { float acc = 0.0f; int ii = 0; while (ii < n) { acc = fmaf(q_local[ii], 1.0f, acc); phi[ii] = acc; ++ii; } FABE13_SINCOS(phi, s_arr, c_arr, n); float as = 0.0f, ac = 0.0f; int k = n - 1; while (k >= 0) { const float Lk = cost.variableLen ? q_local[n + k] : 1.0f; as = fmaf(Lk, s_arr[k], as); ac = fmaf(Lk, c_arr[k], ac); sum_s[k] = as; sum_c[k] = ac; --k; } const float dx = fmaf(x, 1.0f, -cost.targetX); const float dy = fmaf(y, 1.0f, -cost.targetY); const float dist = sqrtf(fmaf(dx, dx, dy * dy)) + 1.0e-8f; float eta = 0.125f; int stepI = 0; while (stepI < 3) { int i = 0;

#pragma loop ivdep
while (i < n) {
float gpen = 0.0f;
{
const float ai = fabsf(q_local[i]);
const float v = ai - cost.minTheta;
if (v > 0.0f) {
const float scale_arg = (2.0f / fmaf(cost.minTheta, 1.0f, 1.0e-6f)) * v * 0.69314718055994530941723212145818f;
const float exp_val = fmaf(scale_arg, fmaf(scale_arg, fmaf(scale_arg, fmaf(scale_arg, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f);
const float dpen_dtheta = cost.sharpW * exp_val * (0.69314718055994530941723212145818f * (2.0f / (cost.minTheta + 1.0e-6f))) * copysignf(1.0f, q_local[i]);
gpen = fmaf(dpen_dtheta, 1.0f, gpen);
}
}
{
const float tsg = fmaf(-q_local[i], cost.archBiasK, 0.0f);
const float exp_arg = -tsg;
const float exp_val = fmaf(exp_arg, fmaf(exp_arg, fmaf(exp_arg, fmaf(exp_arg, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f);
const float sig = 1.0f / fmaf(exp_val, 1.0f, 1.0f);
gpen = fmaf(-(cost.archBiasW * cost.archBiasK), sig, gpen);
}

text
const float g = fmaf(fmaf(dx, -sum_s[i], fmaf(dy, sum_c[i], 0.0f)), 1.0f / dist, gpen); q_try[i] = fmaf(-eta, g, q_local[i]); const float lo0 = -1.0471975511965977461542144610932f; const float hi0 = 2.6179938779914943653855361527329f; const float lo = -2.6179938779914943653855361527329f; const float hi = 2.6179938779914943653855361527329f; const float Lb = (i == 0) ? lo0 : lo; const float Hb = (i == 0) ? hi0 : hi; if (q_try[i] < Lb) q_try[i] = Lb; else if (q_try[i] > Hb) q_try[i] = Hb; ++i; } if (cost.variableLen) { int j = 0;

#pragma loop ivdep
while (j < n) {
const float g = fmaf(fmaf(dx, c_arr[j], fmaf(dy, s_arr[j], 0.0f)), 1.0f / dist, 0.0f);
float v = fmaf(-eta, g, q_local[n + j]);
if (v < 0.5f) v = 0.5f;
else if (v > 2.0f) v = 2.0f;
q_try[n + j] = v;
++j;
}
}
float x2, y2;
const float f2 = cost(q_try, x2, y2);
if (f2 < f) {
memcpy(q_local, q_try, static_cast<size_t>(dim) * sizeof(float));
f = f2;
x = x2;
y = y2;
break;
}
eta = fmaf(eta, 0.5f, 0.0f);
++stepI;
}

text
const int last = n - 1; const float lo = (last == 0) ? -1.0471975511965977461542144610932f : -2.6179938779914943653855361527329f; const float hi = 2.6179938779914943653855361527329f; float bestLocF = f; float saved = q_local[last]; float delta = 0.05f; while (delta >= 0.00625f) { int sgn = -1; while (sgn <= 1) { float cand = fmaf(static_cast<float>(sgn), delta, saved); if (cand < lo) cand = lo; else if (cand > hi) cand = hi; const float backup = q_local[last]; q_local[last] = cand; float x2, y2; const float f2 = cost(q_local, x2, y2); if (f2 < bestLocF) { bestLocF = f2; x = x2; y = y2; saved = cand; } q_local[last] = backup; sgn += 2; } delta = fmaf(delta, 0.5f, 0.0f); } if (bestLocF < f) { q_local[last] = saved; f = bestLocF; } } if (f < bestF) { bestF = f; bestQ.assign(q_local, q_local + dim); bestX = x; bestY = y; no_improve = 0; } else { ++no_improve; } return f; }; const float f_a = evalAt(a), f_b = evalAt(b); const float Kf = fminf(fmaxf(fmaf(2.0f, dim_f, 0.0f), 8.0f), 128.0f); const int K = static_cast<int>(Kf); H.reserve(static_cast<size_t>(maxIter) + static_cast<size_t>(K) + 16u); const int rank = g_world->rank(); const int world = g_world->size(); alignas(16) float seeds[256 * 32]; const int seedCnt = generate_heuristic_seeds(cost, map, dim, seeds, 32, fmaf(static_cast<float>(rank), 7919.0f, static_cast<float>(seed))); int i = 0; while (i < seedCnt) { const float* const s = seeds + static_cast<size_t>(fmaf(static_cast<float>(i), 32.0f, 0.0f)); const float t_seed = map.pointToT(s); const float interval_size = (i < 3) ? fmaf(0.0004f, static_cast<float>(dim), 0.0f) : fmaf(fmaf(0.00031f, static_cast<float>(dim), 0.0f), exp2f(fmaf((1.0f / static_cast<float>(seedCnt - 4)) * log2f(fmaf(0.00025f, 1.0f / 0.00031f, 0.0f)), static_cast<float>(i - 3), 0.0f)), 0.0f); const float t1 = fmaxf(a, fmaf(-interval_size, 1.0f, t_seed)); const float t2 = fminf(b, fmaf(interval_size, 1.0f, t_seed)); if (t2 > t1) { alignas(16) float q1[32], q2[32]; float x1, y1, x2, y2; map.map01ToPoint(t1, q1); const float f1 = cost(q1, x1, y1); map.map01ToPoint(t2, q2); const float f2 = cost(q2, x2, y2); IntervalND* const I = new IntervalND(t1, t2, f1, f2); I->i1 = t_to_idx(t1); I->i2 = t_to_idx(t2); I->diam = map.block_diameter(I->i1, I->i2); I->compute_span_level(map); I->set_metric(I->diam); update_pockets_and_Mmax(I); I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); if (i < 3) { I->R = fmaf(I->R, fmaf(0.01f, static_cast<float>(dim), 0.85f), 0.0f); } else { const float start_mult = fmaf(0.214f, static_cast<float>(dim), 0.0f); const float end_mult = fmaf(0.174f, static_cast<float>(dim), 0.0f); const float mult = fmaf(start_mult, exp2f(fmaf((1.0f / static_cast<float>(seedCnt - 4)) * log2f(fmaf(end_mult, 1.0f / start_mult, 0.0f)), static_cast<float>(i - 3), 0.0f)), 0.0f); I->R = fmaf(I->R, mult, 0.0f); } H.emplace_back(I); std::push_heap(H.begin(), H.end(), ComparePtrND); if (f1 < bestF) { bestF = f1; bestQ.assign(q1, q1 + dim); bestX = x1; bestY = y1; } if (f2 < bestF) { bestF = f2; bestQ.assign(q2, q2 + dim); bestX = x2; bestY = y2; } } ++i; } float prev_t = a, prev_f = f_a; int k = 1; while (k <= K) { const float t = fmaf(fmaf((b - a), static_cast<float>(k) / static_cast<float>(K + 1), a), 1.0f, static_cast<float>(rank) / static_cast<float>(world * (K + 1))); const float f = evalAt(t); IntervalND* const I = new IntervalND(prev_t, t, prev_f, f); I->i1 = t_to_idx(prev_t); I->i2 = t_to_idx(t); I->diam = map.block_diameter(I->i1, I->i2); I->compute_span_level(map); I->set_metric(I->diam); update_pockets_and_Mmax(I); I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); H.emplace_back(I); std::push_heap(H.begin(), H.end(), ComparePtrND); prev_t = t; prev_f = f; ++k; } IntervalND* const tail = new IntervalND(prev_t, b, prev_f, f_b); tail->i1 = t_to_idx(prev_t); tail->i2 = t_to_idx(b); tail->diam = map.block_diameter(tail->i1, tail->i2); tail->compute_span_level(map); tail->set_metric(tail->diam); update_pockets_and_Mmax(tail); tail->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); H.emplace_back(tail); std::push_heap(H.begin(), H.end(), ComparePtrND); float dmax = fmaf(b, 1.0f, -a); const float initial_len = dmax; const float thr03 = fmaf(0.3f, initial_len, 0.0f); const float inv_thr03 = 1.0f / thr03; int it = 0; float kickEveryDimf = fmaf(120.0f, exp2f(fmaf(-0.05f, dim_f, 0.0f)), 0.0f); if (kickEveryDimf < 60.0f) kickEveryDimf = 60.0f; const int kickEveryDim = static_cast<int>(kickEveryDimf); float noImproveThrDimf = fmaf(80.0f, exp2f(fmaf(-0.08f, dim_f, 0.0f)), 0.0f); if (noImproveThrDimf < 30.0f) noImproveThrDimf = 30.0f; const int noImproveThrDim = static_cast<int>(noImproveThrDimf); auto kickEveryByDim = [&](const int d) -> int { float z = fmaf(120.0f, exp2f(fmaf(-0.05f, static_cast<float>(d), 0.0f)), 0.0f); if (z < 60.0f) z = 60.0f; return static_cast<int>(z); }; auto noImproveThrByDim = [&](const int d) -> int { float z = fmaf(80.0f, exp2f(fmaf(-0.08f, static_cast<float>(d), 0.0f)), 0.0f); if (z < 30.0f) z = 30.0f; return static_cast<int>(z); }; while (it < maxIter) { if ((it % kickEveryDim) == 0 && no_improve > noImproveThrDim) { const float t_best = map.pointToT(bestQ.data()); int ii = 0; while (ii < 2) { const float off = (ii == 0) ? 0.01f : -0.01f; const float t_seed = fminf(b, fmaxf(a, fmaf(off, 1.0f, t_best))); const float f_seed = evalAt(t_seed); IntervalND* const J = new IntervalND(fmaf(-0.005f, 1.0f, t_seed), fmaf(0.005f, 1.0f, t_seed), f_seed, f_seed); J->i1 = t_to_idx(fmaf(-0.005f, 1.0f, t_seed)); J->i2 = t_to_idx(fmaf(0.005f, 1.0f, t_seed)); J->diam = map.block_diameter(J->i1, J->i2); J->compute_span_level(map); J->set_metric(J->diam); update_pockets_and_Mmax(J); J->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); J->R = fmaf(J->R, 0.9f, 0.0f); H.emplace_back(J); std::push_heap(H.begin(), H.end(), ComparePtrND); ++ii; } no_improve = 0; } const float p = fmaf(-1.0f / initial_len, dmax, 1.0f); const bool stagnation = (no_improve > 100) && (it > 270); const float exp_arg = fmaf(-0.06f, dim_f, 0.0f); const float exp2_exp_arg = fmaf(exp_arg * 0.69314718055994530941723212145818f, fmaf(exp_arg * 0.69314718055994530941723212145818f, fmaf(exp_arg * 0.69314718055994530941723212145818f, fmaf(exp_arg * 0.69314718055994530941723212145818f, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f); const float A = fmaf(64.0f, exp2_exp_arg, 200.0f); const float B = fmaf(67.0f, exp2_exp_arg, 210.0f); const int T = static_cast<int>(fmaf(-(exp2_exp_arg - 1.0f), A, B)); const float p_arg = fmaf(p, 2.3f, -3.0f); const float r_eff = fmaf(-fmaf(p_arg, fmaf(p_arg, fmaf(p_arg, fmaf(p_arg, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f), 1.0f, 1.05f); std::pop_heap(H.begin(), H.end(), ComparePtrND); IntervalND* const cur = H.back(); H.pop_back(); const float x1 = cur->x1, x2 = cur->x2, y1 = cur->y1, y2 = cur->y2; float m = fmaf(r_eff, Mmax, 0.0f); float tNew = step(m, x1, x2, y1, y2, dim_f, r); tNew = fminf(fmaxf(tNew, a), b); const float fNew = evalAt(tNew); IntervalND* const L = new IntervalND(x1, tNew, y1, fNew); IntervalND* const Rv = new IntervalND(tNew, x2, fNew, y2); L->i1 = t_to_idx(x1); L->i2 = t_to_idx(tNew); Rv->i1 = t_to_idx(tNew); Rv->i2 = t_to_idx(x2); L->diam = map.block_diameter(L->i1, L->i2); Rv->diam = map.block_diameter(Rv->i1, Rv->i2); L->compute_span_level(map); Rv->compute_span_level(map); L->set_metric(L->diam); Rv->set_metric(Rv->diam); const float Mloc = fmaxf(L->M, Rv->M); update_pockets_and_Mmax(L); update_pockets_and_Mmax(Rv); const float prevMmax = Mmax; if (Mloc > Mmax) Mmax = Mloc; m = fmaf(r_eff, Mmax, 0.0f); if (adaptive) { const float len1 = fmaf(tNew, 1.0f, -x1); const float len2 = fmaf(x2, 1.0f, -tNew); if (fmaf(len1, 1.0f, len2) == dmax) { dmax = fmaxf(len1, len2); for (auto pI : H) { const float Ls = fmaf(pI->x2, 1.0f, -pI->x1); if (Ls > dmax) dmax = Ls; } } if ((thr03 > dmax && !(it % 3)) || (fmaf(10.0f, dmax, 0.0f) < initial_len)) { const float progress = fmaf(-inv_thr03, dmax, 1.0f); const float alpha = fmaf(progress, progress, 0.0f); const float beta = fmaf(-alpha, 1.0f, 2.0f); const float MULT = (1.0f / dmax) * Mmax; const float global_coeff = fmaf(MULT, r_eff, -MULT); const float GF = fmaf(beta, global_coeff, 0.0f); L->ChangeCharacteristic(fmaf(GF, len1, fmaf(L->M, alpha, 0.0f))); Rv->ChangeCharacteristic(fmaf(GF, len2, fmaf(Rv->M, alpha, 0.0f))); const size_t sz = H.size(); RecomputeR_AffineM_AVX2_ND(H.data(), sz, GF, alpha); std::make_heap(H.begin(), H.end(), ComparePtrND); } else { if (Mloc > prevMmax) { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); if (Mloc > fmaf(1.15f, prevMmax, 0.0f)) { const size_t sz = H.size(); RecomputeR_ConstM_AVX2_ND(H.data(), sz, m); std::make_heap(H.begin(), H.end(), ComparePtrND); } } else { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); } } } else { if (Mloc > prevMmax) { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); if (Mloc > fmaf(1.15f, prevMmax, 0.0f)) { const size_t sz = H.size(); RecomputeR_ConstM_AVX2_ND(H.data(), sz, m); std::make_heap(H.begin(), H.end(), ComparePtrND); } } else { L->ChangeCharacteristic(m); Rv->ChangeCharacteristic(m); } } H.emplace_back(L); std::push_heap(H.begin(), H.end(), ComparePtrND); H.emplace_back(Rv); std::push_heap(H.begin(), H.end(), ComparePtrND); _mm_prefetch(reinterpret_cast<const char*>(H[0]), _MM_HINT_T0); _mm_prefetch(reinterpret_cast<const char*>(H[1]), _MM_HINT_T0); IntervalND* const top = H.front(); const float interval_len = top->x2 - top->x1; if ((exp2f((1.0f / dim_f) * log2f(interval_len)) < eps) || (it == maxIter)) { out_iterations = static_cast<size_t>(it); out_achieved_epsilon = interval_len; return; } if (!(it % T)) { MultiCrossMsg out; out.count = 3; float* dest = out.intervals; IntervalND* const t1 = H[0]; IntervalND* const t2 = H[1]; IntervalND* const t3 = H[2]; IntervalND* const tops[3] = { t1, t2, t3 }; unsigned i2 = 0; while (i2 < 3) { IntervalND* const Tt = tops[i2]; dest[0] = Tt->x1; dest[1] = 0.0f; dest[2] = Tt->x2; dest[3] = 0.0f; dest[4] = Tt->R; dest += 5; ++i2; } const size_t iterations = std::bit_width(static_cast<size_t>(world - 1)); bool active = true; const bool invert_T = static_cast<int>(fmaf(static_cast<float>(exchange_counter_T), 1.0f, 1.0f)) & 1; size_t ii = 0; while (ii < iterations && active) { const size_t step = 1ULL << ii; const int partner = rank ^ static_cast<int>(step); if (partner < world) { const bool am_sender = (!!(rank & static_cast<int>(step))) ^ invert_T; if (am_sender) { g_world->isend(partner, 0, out); active = false; } } ++ii; } ++exchange_counter_T; } if (!(it % 500)) { BestSolutionMsg out; out.bestF = bestF; out.bestX = bestX; out.bestY = bestY; out.dim = static_cast<unsigned>(bestQ.size()); memcpy(out.bestQ, bestQ.data(), bestQ.size() * sizeof(float)); const size_t iterations = std::bit_width(static_cast<size_t>(world - 1)); bool active = true; const bool invert_T = static_cast<int>(fmaf(static_cast<float>(exchange_counter_500), 1.0f, 1.0f)) & 1; size_t ii = 0; while (ii < iterations && active) { const size_t step = 1ULL << ii; const int partner = rank ^ static_cast<int>(step); if (partner < world) { const bool am_sender = (!!(rank & static_cast<int>(step))) ^ invert_T; if (am_sender) { g_world->isend(partner, 2, out); active = false; } } ++ii; } ++exchange_counter_500; } while (g_world->iprobe(boost::mpi::any_source, 0)) { MultiCrossMsg in; g_world->recv(boost::mpi::any_source, 0, in); const MultiCrossMsg& mX = in; unsigned ii = 0; while (ii < mX.count) { const float* const d = &mX.intervals[ii * 5]; float sx = d[0], ex = d[2]; if (ex > sx) { alignas(16) float tmp[32]; float tx, ty; map.map01ToPoint(sx, tmp); const float y1i = cost(tmp, tx, ty); map.map01ToPoint(ex, tmp); const float y2i = cost(tmp, tx, ty); IntervalND* const inj = new IntervalND(sx, ex, y1i, y2i); inj->i1 = t_to_idx(sx); inj->i2 = t_to_idx(ex); inj->diam = map.block_diameter(inj->i1, inj->i2); inj->compute_span_level(map); inj->set_metric(inj->diam); update_pockets_and_Mmax(inj); inj->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); _mm_prefetch(reinterpret_cast<const char*>(H[0]), _MM_HINT_T0); _mm_prefetch(reinterpret_cast<const char*>(H[1]), _MM_HINT_T0); IntervalND* const topH = H.front(); if (inj->R > fmaf(1.15f, topH->R, 0.0f)) { const float p2 = fmaf(-1.0f / initial_len, dmax, 1.0f); const float kf = (no_improve > 100 && it > 270) ? fmaf(0.5819767068693265f, (fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f) - 1.0f), 0.3f) : fmaf(0.3491860241215959f, (fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, fmaf(p2 * 0.69314718055994530941723212145818f, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f) - 1.0f), 0.6f); inj->R = fmaf(d[4], kf, 0.0f); H.emplace_back(inj); std::push_heap(H.begin(), H.end(), ComparePtrND); } } ++ii; } } while (g_world->iprobe(boost::mpi::any_source, 2)) { BestSolutionMsg bm; g_world->recv(boost::mpi::any_source, 2, bm); if (bm.bestF < fmaf(bestF, 1.15f, 0.0f)) { alignas(16) float tmp_q[32]; memcpy(tmp_q, bm.bestQ, bm.dim * sizeof(float)); const float t_best = map.pointToT(tmp_q); const float t1 = fmaxf(a, fmaf(-0.001f, 1.0f, t_best)); const float t2 = fminf(b, fmaf(0.001f, 1.0f, t_best)); if (t2 > t1) { alignas(16) float tq1[32], tq2[32]; float xx1, yy1, xx2, yy2; map.map01ToPoint(t1, tq1); const float f1 = cost(tq1, xx1, yy1); map.map01ToPoint(t2, tq2); const float f2 = cost(tq2, xx2, yy2); IntervalND* const I = new IntervalND(t1, t2, f1, f2); I->i1 = t_to_idx(t1); I->i2 = t_to_idx(t2); I->diam = map.block_diameter(I->i1, I->i2); I->compute_span_level(map); I->set_metric(I->diam); update_pockets_and_Mmax(I); I->ChangeCharacteristic(fmaf(r, Mmax, 0.0f)); I->R = fmaf(I->R, 0.90f, 0.0f); H.emplace_back(I); std::push_heap(H.begin(), H.end(), ComparePtrND); } if (bm.bestF < bestF) { bestF = bm.bestF; bestX = bm.bestX; bestY = bm.bestY; bestQ.assign(bm.bestQ, bm.bestQ + bm.dim); } } } ++it; }

}

/// БЕЗ ИЗМЕНЕНИЙ

extern "C" __declspec(dllexport) __declspec(noalias)
void AGP_Manip2D(const int nSegments, const bool variableLengths, const float minTheta,
const float targetX, const float targetY, const int peanoLevels,
const int maxIterPerBranch, const float r, const bool adaptiveMode,
const float epsilon, const unsigned int seed,
const float baseLength, const float stretchFactor,
float** const out_bestQ, size_t* const out_bestQLen, float* const out_bestX,
float* const out_bestY, float* const out_bestF, size_t* const out_iterations,
float* const out_achieved_epsilon) noexcept {
Slab* const __restrict slab = tls.local();
slab->current = slab->base;
while (g_world->iprobe(boost::mpi::any_source, 0)) {
MultiCrossMsg dummy;
g_world->recv(boost::mpi::any_source, 0, dummy);
}
while (g_world->iprobe(boost::mpi::any_source, 2)) {
BestSolutionMsg dummy;
g_world->recv(boost::mpi::any_source, 2, dummy);
}
const int dim = nSegments + (variableLengths ? nSegments : 0);

text
g_mc.permCache.resize(static_cast<size_t>(dim)); int i = 0; while (i < dim) { g_mc.permCache[i] = i; ++i; } unsigned s = g_mc.baseSeed; i = dim - 1; while (i > 0) { s ^= s << 13; s ^= s >> 17; s ^= s << 5; const unsigned j = s % static_cast<unsigned>(i + 1); std::swap(g_mc.permCache[i], g_mc.permCache[j]); --i; } g_mc.invMaskCache.resize(static_cast<size_t>(dim)); int k = 0; while (k < dim) { s ^= s << 13; s ^= s >> 17; s ^= s << 5; g_mc.invMaskCache[k] = static_cast<unsigned long long>(s); ++k; } std::vector<float, boost::alignment::aligned_allocator<float, 16u>> low; std::vector<float, boost::alignment::aligned_allocator<float, 16u>> high; low.reserve(static_cast<size_t>(dim)); high.reserve(static_cast<size_t>(dim)); i = 0; while (i < nSegments) { low.emplace_back(i == 0 ? -1.0471975511965977461542144610932f : -2.6179938779914943653855361527329f); high.emplace_back(2.6179938779914943653855361527329f); ++i; } if (variableLengths) { i = 0; const float lengthLower = baseLength / stretchFactor; const float lengthUpper = baseLength * stretchFactor; while (i < nSegments) { low.emplace_back(lengthLower); high.emplace_back(lengthUpper); ++i; } } const ManipCost cost(nSegments, variableLengths, targetX, targetY, minTheta); const int rank = g_world->rank(), world = g_world->size(); std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ; float bestF = FLT_MAX, bx = 0.0f, by = 0.0f; const int levels0 = static_cast<int>(fminf(static_cast<float>(peanoLevels), 8.0f)); const int maxIter0 = static_cast<int>(fmaf(static_cast<float>(maxIterPerBranch), 0.2f, 0.0f)); const MortonND map0(dim, levels0, low.data(), high.data(), g_mc); std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>> H_coarse; std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ_coarse; float bestF_coarse = FLT_MAX, bx_coarse = 0.0f, by_coarse = 0.0f; size_t total_oi = 0u; float total_oe = 0.0f; size_t oi = 0u; float oe = 0.0f; const float base_M_prior_factor = fmaf(2.0f, static_cast<float>(nSegments), variableLengths ? 1.41421356f : 0.0f); float M_prior = fmaf(base_M_prior_factor, ldexpf(1.0f, -levels0), 0.0f); agp_run_branch_mpi(map0, cost, maxIter0, r, adaptiveMode, epsilon, seed, H_coarse, bestQ_coarse, bestF_coarse, bx_coarse, by_coarse, oi, oe, M_prior); total_oi += oi; total_oe = oe; if (bestF_coarse < bestF) { bestF = bestF_coarse; bestQ = std::move(bestQ_coarse); bx = bx_coarse; by = by_coarse; } if (levels0 < peanoLevels) { while (g_world->iprobe(boost::mpi::any_source, 0)) { MultiCrossMsg dummy; g_world->recv(boost::mpi::any_source, 0, dummy); } while (g_world->iprobe(boost::mpi::any_source, 2)) { BestSolutionMsg dummy; g_world->recv(boost::mpi::any_source, 2, dummy); } const MortonND map1(dim, peanoLevels, low.data(), high.data(), g_mc); std::vector<IntervalND*, boost::alignment::aligned_allocator<IntervalND*, 16u>> H_fine; std::vector<float, boost::alignment::aligned_allocator<float, 16u>> bestQ_fine = bestQ; float bestF_fine = bestF, bx_fine = bx, by_fine = by; size_t oi_fine = 0u; float oe_fine = 0.0f; float M_prior_fine = fmaf(base_M_prior_factor, ldexpf(1.0f, -peanoLevels), 0.0f); HoaraSort(H_coarse.begin(), H_coarse.end() - 1); const float inv_dim = 1.0f / static_cast<float>(dim + 1); size_t ci = static_cast<size_t>(fmaf(static_cast<float>(H_coarse.size()), fmaf(fmaf(inv_dim, fmaf(inv_dim, fmaf(inv_dim, fmaf(inv_dim, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f), 1.0f, -0.7f), 0.0f)); while (ci < H_coarse.size()) { const IntervalND* const C = H_coarse[ci]; alignas(16) float q1[32], q2[32]; float x1, y1, x2, y2; map1.map01ToPoint(C->x1, q1); const float f1 = cost(q1, x1, y1); map1.map01ToPoint(C->x2, q2); const float f2 = cost(q2, x2, y2); IntervalND* const I = new IntervalND(C->x1, C->x2, f1, f2); I->i1 = static_cast<unsigned long long>(fmaf(C->x1, static_cast<float>(map1.scale), 0.0f)); I->i2 = static_cast<unsigned long long>(fmaf(C->x2, static_cast<float>(map1.scale), 0.0f)); I->diam = map1.block_diameter(I->i1, I->i2); I->set_metric(I->diam); H_fine.emplace_back(I); if (f1 < bestF_fine) { bestF_fine = f1; bestQ_fine.assign(q1, q1 + dim); bx_fine = x1; by_fine = y1; } if (f2 < bestF_fine) { bestF_fine = f2; bestQ_fine.assign(q2, q2 + dim); bx_fine = x2; by_fine = y2; } ++ci; } std::make_heap(H_fine.begin(), H_fine.end(), ComparePtrND); agp_run_branch_mpi(map1, cost, fmaf(static_cast<float>(maxIterPerBranch), 1.0f, -static_cast<float>(maxIter0)), r, adaptiveMode, epsilon, seed, H_fine, bestQ_fine, bestF_fine, bx_fine, by_fine, oi_fine, oe_fine, M_prior_fine); total_oi += oi_fine; total_oe = oe_fine; if (bestF_fine < bestF) { bestF = bestF_fine; bestQ = std::move(bestQ_fine); bx = bx_fine; by = by_fine; } } BestSolutionMsg best; best.bestF = bestF; best.bestX = bx; best.bestY = by; best.dim = static_cast<unsigned>(bestQ.size()); memcpy(best.bestQ, bestQ.data(), static_cast<size_t>(best.dim) * sizeof(float)); const size_t iterations = std::bit_width(static_cast<size_t>(world - 1)); bool active = true; size_t itx = 0; while (itx < iterations && active) { const size_t step = 1ULL << itx; const int partner = rank ^ static_cast<int>(step); if (partner < world) { const bool am_sender = (rank & static_cast<int>(step)) != 0; if (am_sender) { g_world->isend(partner, 3, best); active = false; } else { BestSolutionMsg in; g_world->recv(partner, 3, in); if (in.bestF < best.bestF) best = in; } } ++itx; } if (rank == 0) { *out_bestQLen = static_cast<size_t>(best.dim); *out_bestQ = static_cast<float*>(CoTaskMemAlloc(sizeof(float) * (*out_bestQLen))); memcpy(*out_bestQ, best.bestQ, sizeof(float) * (*out_bestQLen)); *out_bestX = best.bestX; *out_bestY = best.bestY; *out_bestF = best.bestF; *out_iterations = total_oi; *out_achieved_epsilon = total_oe; }

}

extern "C" __declspec(dllexport) __declspec(noalias) __forceinline int AgpInit(const int peanoLevel, const float a, const float b, const float c, const float d) noexcept {
g_env = new boost::mpi::environment();
g_world = new boost::mpi::communicator();
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
const int rank = g_world->rank();
const int world_size = g_world->size();
if (world_size == 4) {
new (&gActiveMap) Peano2DMap(peanoLevel, a, b, c, d, rank & 3);
}
g_mc.baseSeed = fmaf(0x9E3779B9u, static_cast<float>(rank), 0x9E3779B9u);
if (rank == 0) {
wchar_t buf[MAX_PATH]{};
GetModuleFileNameW(nullptr, buf, MAX_PATH);
std::wstring ws(buf);
auto pos = ws.find_last_of(L"\/");
if (pos != std::wstring::npos) ws.resize(pos);
int n = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, nullptr, 0, nullptr, nullptr);
g_exeDirCache.resize(n, '\0');
WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, g_exeDirCache.data(), n, nullptr, nullptr);
if (!g_exeDirCache.empty() && g_exeDirCache.back() == '\0') g_exeDirCache.pop_back();

text
g_pyInterpreter = new pybind11::scoped_interpreter{}; pybind11::module_ sys = pybind11::module_::import("sys"); pybind11::list path = sys.attr("path"); const std::string& exeDir = g_exeDirCache; path.attr("insert")(0, pybind11::str(exeDir + "\\env\\Lib\\site-packages")); path.attr("insert")(0, pybind11::str(exeDir + "\\env\\Scripts")); path.attr("insert")(0, pybind11::str(exeDir + "\\env")); path.attr("append")(pybind11::str(exeDir)); pybind11::module_::import("warnings").attr("filterwarnings")("ignore"); g_pyOptimizerBridge = new pybind11::module_(pybind11::module_::import("optimizer_bridge")); } return rank;

}

/// БЕЗ ИЗМЕНЕНИЙ

extern "C" __declspec(dllexport) __declspec(noalias)
void RunPythonOptimization(const char* const backend,
const int n_seg,
const bool var_len,
const float min_theta,
const float tx,
const float ty,
const int levels,
const int max_iter,
const float r_param,
const float eps,
const bool adaptive,
const float baseLength,
const float stretchFactor,
float* const __restrict out_bestF,
float* const __restrict out_bestX,
float* const __restrict out_bestY,
float* const __restrict out_angles,
float* const __restrict out_lengths,
int* const __restrict out_iterations,
float* const __restrict out_eps,
float* const __restrict out_micros) noexcept {
pybind11::gil_scoped_acquire gil;
pybind11::dict result;

text
if (backend && strcmp(backend, "optuna") == 0) { result = (*g_pyOptimizerBridge).attr("run_optuna")(n_seg, var_len, min_theta, tx, ty, max_iter, baseLength, stretchFactor); } else { result = (*g_pyOptimizerBridge).attr("run_iopt")(n_seg, var_len, min_theta, tx, ty, levels, max_iter, r_param, eps, adaptive, baseLength, stretchFactor); } *out_bestF = result["BEST_F"].cast<float>(); *out_bestX = result["BEST_X"].cast<float>(); *out_bestY = result["BEST_Y"].cast<float>(); *out_iterations = result["ITERATIONS"].cast<int>(); *out_eps = result["EPS"].cast<float>(); *out_micros = result["TIME"].cast<float>(); std::vector<float> angles_vec = result["ANGLES"].cast<std::vector<float>>(); std::vector<float> lengths_vec = result["LENGTHS"].cast<std::vector<float>>(); const size_t n = static_cast<size_t>(n_seg); if (out_angles) { const size_t m = angles_vec.size(); const size_t limit = m < n ? m : n; const float* const __restrict src = angles_vec.data(); size_t i = 0;

#pragma loop ivdep
while (i < limit) {
out_angles[i] = src[i];
++i;
}
}

text
if (out_lengths) { if (var_len) { const size_t mL = lengths_vec.size(); const size_t limitL = mL < n ? mL : n; const float* const __restrict srcL = lengths_vec.data(); size_t j = 0;

#pragma loop ivdep
while (j < limitL) {
out_lengths[j] = srcL[j];
++j;
}
}
else {
const float one = 1.0f;
size_t j = 0;
#pragma loop ivdep
while (j < n) {
out_lengths[j] = one;
++j;
}
}
}
}", код пайтоновского скрипта: "import sys
import math
import time
import statistics
import warnings
from io import StringIO

import optuna
from optuna.samplers import CmaEsSampler
import iOpt

def forward_kinematics(angles, lengths):
phi = x = y = 0.0
angle_iter = iter(angles)
length_iter = iter(lengths)
for theta, L in zip(angle_iter, length_iter):
phi += theta
x += L * math.cos(phi)
y += L * math.sin(phi)
return x, y

def manipulator_cost(angles, lengths, target_x, target_y, min_theta):
x, y = forward_kinematics(angles, lengths)
dx, dy = x - target_x, y - target_y
dist = math.hypot(dx, dy)
scale = 2.0 / (min_theta + 1e-6)
pen_c = arch_pen = 0.0

text
for theta in angles: v = abs(theta) - min_theta if v > 0.0: scaled = scale * v if scaled < 100.0: pen_c += 0.05 * (1 << int(scaled)) else: pen_c += 0.05 * (2.0 ** scaled - 1.0) t = -theta * 3.0 if t > 10.0: arch_pen += 0.02 * t else: arch_pen += 0.02 * math.log1p(math.exp(t)) return dist + pen_c + arch_pen

def build_angles_lengths(trial, n_seg, var_len, baseLength, stretchFactor):
angles = []
for i in range(n_seg):
low = -1.0471975511965977 if i == 0 else -2.6179938779914944
angles.append(trial.suggest_float(f"theta_{i}", low, 2.6179938779914944))

text
if var_len: length_lower = baseLength / stretchFactor length_upper = baseLength * stretchFactor lengths = [trial.suggest_float(f"L_{i}", length_lower, length_upper) for i in range(n_seg)] else: lengths = [baseLength] * n_seg return angles, lengths

def run_optuna(n_seg, var_len, min_theta, tx, ty, max_iter,
baseLength, stretchFactor):
warnings.filterwarnings('ignore')
optuna.logging.set_verbosity(optuna.logging.ERROR)

text
dimension = n_seg * (2 if var_len else 1) sampler = CmaEsSampler( seed=42, n_startup_trials=(dimension - 1) ** 2 + 3, consider_pruned_trials=False ) def objective(trial): angles, lengths = build_angles_lengths(trial, n_seg, var_len, baseLength, stretchFactor) return manipulator_cost(angles, lengths, tx, ty, min_theta) study = optuna.create_study( direction="minimize", sampler=sampler, pruner=optuna.pruners.NopPruner() ) start_time = time.perf_counter() study.optimize(objective, n_trials=max_iter, show_progress_bar=False, gc_after_trial=False) elapsed_micros = (time.perf_counter() - start_time) * 1e6 best_trial = study.best_trial params = best_trial.params angles = [params[f"theta_{i}"] for i in range(n_seg)] lengths = [params[f"L_{i}"] for i in range(n_seg)] if var_len else [1.0] * n_seg best_x, best_y = forward_kinematics(angles, lengths) iterations = len(study.trials) recent_trials = iterations // 10 recent_values = [t.value for t in study.trials[-recent_trials:]] achieved_eps = statistics.stdev(recent_values) if recent_trials > 1 else 0.0 return { "BEST_F": float(best_trial.value), "BEST_X": float(best_x), "BEST_Y": float(best_y), "ITERATIONS": int(iterations), "EPS": float(achieved_eps), "TIME": float(elapsed_micros), "ANGLES": angles, "LENGTHS": lengths }

def run_iopt(n_seg, var_len, min_theta, tx, ty, levels, max_iter,
r_param, eps, adaptive, baseLength, stretchFactor):
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = StringIO(), StringIO()

text
def objective(trial): angles, lengths = build_angles_lengths(trial, n_seg, var_len, baseLength, stretchFactor) return manipulator_cost(angles, lengths, tx, ty, min_theta) study = iOpt.create_study() params = iOpt.SolverParameters(eps=eps, r=r_param, iters_limit=max_iter, evolvent_density=levels, refine_solution=adaptive) study.optimize(objective=objective, solver_parameters=params, type_of_painter="none", console_mode="off") sys.stdout, sys.stderr = old_stdout, old_stderr best_params = study.best_float_params_() angles = best_params[:n_seg] lengths = best_params[n_seg:n_seg*2] if var_len else [1.0] * n_seg best_f = study.best_values_() best_x, best_y = forward_kinematics(angles, lengths) solution = getattr(study, "solution", None) achieved_eps = solution.solution_accuracy iterations = solution.number_of_global_trials elapsed_micros = solution.solving_time * 1e6 return { "BEST_F": float(best_f), "BEST_X": float(best_x), "BEST_Y": float(best_y), "ITERATIONS": int(iterations), "EPS": float(achieved_eps), "TIME": float(elapsed_micros), "ANGLES": angles, "LENGTHS": lengths }

def main():
argv = sys.argv
backend, n_seg, var_len = argv[1], int(argv[2]), bool(int(argv[3]))
min_theta, tx, ty = float(argv[4]), float(argv[5]), float(argv[6])
max_iter = int(argv[8])
baseLength = float(argv[9])
stretchFactor = float(argv[10])

text
if backend == "optuna": run_optuna(n_seg, var_len, min_theta, tx, ty, max_iter, baseLength, stretchFactor) else: levels, r_param, eps, adaptive = int(argv[7]), float(argv[11]), float(argv[12]), bool(int(argv[13])) run_iopt(n_seg, var_len, min_theta, tx, ty, levels, max_iter, r_param, eps, adaptive, baseLength, stretchFactor)" - будь особенно внимателен и учитывай что файлы изменились где-то может показаться что изменений нет - но они есть и иногда в мелочах и можно изменения поймать только при внимательном рассмотрении

Pensamento

Adapting code for AGP and experiments

I need to adapt chat01ai's solution based on new instructions. This includes disabling the epsilon stop condition for AGP, adjusting thresholds, increasing run counts, and modifying chart displays. For Chart 1, I’ll add a (0,0) point and convert micros to millis. For Chart 2, I will adjust the X-axis starting at 100.

Adjusting time units and chart details

For chart adjustments, I’ll set AxisX.Minimum to 100 for the iterations. Regarding time units, I should convert micros to milliseconds in the charts, and I could also consider updating Python to return ms instead of micros. For Optuna, I now need to include three lines: best-run, mean, and worst-run, plus AGP and iOpt lines.

Adding Optuna's best and worst lines

For each chart, I’ll include five series: AGP mean, Optuna mean, Optuna best, Optuna worst, and iOpt. The user specified that the best and worst Optuna lines should be placed above and below the mean line, respectively, so I’ll make sure to follow that vertical order in the charts.

Adjusting chart lines and robot experiment settings

I need to adjust the charts: the best and worst lines should represent minimum and maximum values (time or f), while the previous solution only had 3 lines per chart. For the robot experiment, the manipulator should have 3 segments, so I'll set nSegments = 3. Variable length is unclear, but I'll treat it based on earlier assumptions.

Choosing variable lengths and updating the code

Based on their earlier mention of a 3-segment manipulator, I'll set variableLengths=false for simplicity in the comparative tests. It aligns with baseLength and stretch factor, and I can maintain consistency across the tests. Also, the Python optimizer now needs to be run directly via the RunPythonOptimization function, as the previous method with optimizer_bridge.py is outdated.

Following new UI guidelines and adjustments

They want the interface to follow the chat01ai pattern, so we’ll focus on two charts and a stats TextBox, not a manipulator view. Regarding the three graphs they mentioned, we'll add 5 lines per chart: AGP and iOpt with one line each, and Optuna with best/mean/worst lines. I’ll set nSegments=3 for the robot manipulator. Lastly, I'll implement the AGP patch to ignore epsilon stopping and only stop when it == maxIter.

Updating AGP_MANIP signature and code patch

The AGP_1D/2D may still exist for other uses, but since the user specifically asked about manip, we’ll focus on that. We need to update the P_MANIP signature to match the new format, including baseLength and stretchFactor. I'll patch the code to ensure it aligns with the new structure and parameters.

Handling time units for Python optimization

The user wants times in milliseconds, but the Python code exports in microseconds. It seems like a small change in the bridging code will be easiest. Instead of altering the Python function, I'll multiply the time by 1e-3 to convert to milliseconds when storing in MyForm. This way, we keep the changes minimal.

Updating MyForm for new experiment setup

Rather than altering the library, I’ll handle the TIME unit conversion to milliseconds directly in MyForm. Setting nSegments = 3, baseLength = 1.0f, and stretchFactor = 1.5f seems good, following the original UI’s defaults. Since the user prefers a new MyForm interface with updates, I'll rewrite it accordingly.

Adjusting stopping condition for agp_run_branch_mpi

The key change is dealing with the stopping condition for agp_run_branch_mpi. Currently, it doesn't stop by maxIter because of how it is incremented after checking the condition. The plan is to update it so that the check happens after incrementing, ensuring the loop stops at the correct iteration.

Proposing an update for stopping condition and MyForm adjustments

To properly handle the stopping condition, we can replace the current check with one that ensures the loop stops after exactly maxIter iterations. This revision keeps the logic simple: if (it + 1 == maxIter) to return after completing the specified number of iterations, ignoring eps. Additionally, we can update the MyForm to include necessary references for Chart and TextBox while adjusting for the latest library changes.

Examining updated experiments with Python bridging

The experiment code needs to be updated for the new Python bridging approach. Instead of calling external Python processes, we directly use the RunPythonOptimization function with the updated signature. We also need to pass baseLength and stretchFactor for consistency across the methods. These values, though defaulted, maintain the interface signature for geometry consistency. The Python script's changed main() doesn't affect this updated approach.

Deciding on variable length and experiment setup

I’m thinking about the impact of setting variableLengths=true. This would make sure that both AGP and Python align by passing the same base lengths and stretch factors. The user didn’t explicitly mention variable lengths for the experiment, but I believe it’s best to use them for fairness across methods. Then, for the experiment, I’m focusing on calculating best, average, and worst times for Optuna using a loop and plotting best/worst runs in two separate charts based on time and function values.

Planning best/worst implementation for Optuna and chart adjustments

I’m thinking about how to handle the best and worst metrics separately for time and function charts. We’ll need specific arrays and loops to track iteration counts alongside best/worst values. Charts will start from (0,0) for the time chart, but not for the function chart. I’ll also adjust summary details, experiment settings, and axis values accordingly. I need to be cautious while implementing these changes to avoid mistakes.

Finalizing experiment setup and code modifications

Given the detailed parameters, I’ll set up the 3-link manipulator experiments. I need to adapt both the C++ and Python bridging to handle the new configurations, repeat counts, and time formatting, plus chart modifications. I’ll walk through code adjustments for handling epsilon and time, ensuring everything tightly integrates without errors.

Reviewing and adapting MyForm.h for new signatures

I need to go over the chat01ai MyForm.h code, applying changes based on the new function signatures for AGP_Manip2D and ensuring everything aligns. I'll focus on the new typedefs, like P_MANIP, and decide whether to keep or remove older references to System::Diagnostics and System::IO. Multi-rank architecture must be maintained for worker processes to collaborate effectively.

Adapting MyForm for new library interface

The key point is that the AGP_Manip2D signature and Python bridging have changed. The old setup using ProcessStartInfo for Python calls will now be replaced with direct function calls using P_MANIP, P_RUNPY, etc. We will adapt the UI (e.g., nudBaseLength, nudStretchFactor) to match new function signatures and ensure compatibility with the updated library. Additionally, we'll remove the external Python process and integrate with the new bridging system.

Setting up constructor and UI elements for new integrations

In the MyForm constructor, the key steps include acquiring function pointers for the new library and setting default parameter values. After initializing UI components such as charts, we will define the series for time and F charts, including customizations like dashed lines for best/worst data. We also adapt statistical calculations to include new helper functions. For Python bridging, we'll ensure the new script is integrated properly, and adapt UI code accordingly to handle new function signatures and algorithm updates.

Designing the per-threshold loop for experiments

For each threshold, I will run iOpt and Optuna several times, gathering their results into arrays. After each round, I’ll compute averages and track both best and worst outcomes. Then, I’ll update the charts with these values, ensuring to correctly format the results for clarity. I'll also ensure the UI remains responsive during loops. Lastly, I'll adjust labels and units, ensuring accuracy for the final MSE calculations and experiment summary.

Thought for 3m 8s

Resposta

Сразу по сути:

  1. покажу точное место, где вырубить остановку AGP по eps в новой версии agp_run_branch_mpi;
  2. дам полный новый MyForm.h / MyForm.cpp, уже под новую DLLRunPythonOptimization, baseLength, stretchFactor) и с учётом всех пожеланий:
  • 3-звенный манипулятор;
  • Optuna / AGP – 20 прогонов на порог;
  • время в миллисекундах;
  • на первом чарте (время–итерации) — линии начинаются из точки (0, 0);
  • на втором чарте (f–итерации) ось X начинается с 100 (первый порог итераций);
  • для Optuna: на обоих чартах 3 кривые — mean / best / worst;
  • справа остаётся текстовая статистика как у chat01ai, только везде «20 запусков».

1. Отключаем останов по eps в agp_run_branch_mpi

В вашем новом коде сейчас в конце основного цикла:

cpp
IntervalND* const top = H.front(); const float interval_len = top->x2 - top->x1; if ((exp2f((1.0f / dim_f) * log2f(interval_len)) < eps) || (it == maxIter)) { out_iterations = static_cast<size_t>(it); out_achieved_epsilon = interval_len; return; } ... ++it;

Но while (it < maxIter) и инкремент ++it в самом конце означают, что условие (it == maxIter) никогда не сработает — максимум it внутри тела цикла равен maxIter - 1.

Чтобы:

  • полностью игнорировать eps как критерий останова,
  • честно останавливаться ровно по числу итераций,

замените этот фрагмент на такой:

cpp
IntervalND* const top = H.front(); const float interval_len = top->x2 - top->x1; // Отключаем останов по достигнутой точности eps. // Завершаем ветку, когда выполнено ровно maxIter шагов. if (it + 1 == maxIter) { out_iterations = static_cast<size_t>(it + 1); out_achieved_epsilon = interval_len; return; } ++it;

И всё — AGP теперь работает строго по лимиту итераций, без раннего выхода по eps.

Если у вас ещё где-то есть 1D/2D-варианты AGP с условиями вида
interval_len < epsilon || counter == global_iterations, — там аналогично выбрасываете часть interval_len < epsilon, оставляя только критерий по счётчику.


2. Новый интерфейс: только графики + статистика

Кратко, что делает новый MyForm

  • Форма без кнопок и полей — только:

    • chartTime: время ( мс ) vs число итераций:
      • AGP (mean),
      • iOpt,
      • Optuna mean,
      • Optuna best,
      • Optuna worst.
    • chartF: значение целевой функции vs число итераций:
      • те же 5 кривых.
    • tbStats: справа текстовая сводка (μ, σ, √MSE по времени и f, с явным указанием 20 запусков).
  • Параметры эксперимента (одни и те же для трёх алгоритмов):

    cpp
    nSegments = 3; variableLengths = true; // чтобы baseLength/stretchFactor реально участвовали minTheta = 1.5f; targetX = 2.5f; targetY = -1.0f; peanoLevels = 12; rParam = 2.5f; epsParam = 1.0e-5f; baseLength = 1.0f; stretchFactor = 1.5f;
  • Пороги максимального числа итераций:

    cpp
    {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}
  • Количество прогонов:

    • iOpt: 1 запуск на порог (детерминированный).
    • Optuna: 20 запусков на порог.
    • AGP (C++): 20 запусков на порог.
  • Для Optuna на каждом пороге:

    • считаем средние по 20 прогонов (итерации, время, f),
    • считаем σ,
    • отдельно для времени: выбираем запуск с минимальным и с максимальным временем → «best»/«worst» по времени,
    • отдельно для f: выбираем запуск с минимальным и максимальным f → «best»/«worst» по f.
  • Для AGP:

    • считаем средние и σ по 20 прогонов (время, f).
  • Для iOpt:

    • 1 значение на порог.
  • МSE и √MSE:

    • по каждому порогу сравниваем средние значения AGP и Optuna с iOpt по:
      • времени (мс),
      • значению f,
    • суммируем квадрат отклонений, делим на число порогов с валидными данными → √MSE(t) и √MSE(f) для AGP и Optuna.
  • Графики:

    • chartTime:
      • ось X: фактическое число итераций;
      • ось Y: время в миллисекундах;
      • для каждой серии (AGP, Optuna mean/best/worst, iOpt) добавляется стартовая точка (0, 0), чтобы линии шли «из нуля».
    • chartF:
      • ось X: начинается с 100 (AxisX->Minimum = 100.0), т.е. первый порог;
      • ось Y: f, авто-масштаб;
      • никаких точек с X = 0.

2.1. Новый MyForm.h

cpp
#pragma once #define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <cstddef> using namespace System; using namespace System::Drawing; using namespace System::Windows::Forms; using namespace System::Collections::Generic; using namespace System::Globalization; using namespace System::Runtime::CompilerServices; using namespace System::Windows::Forms::DataVisualization::Charting; // AGP_Manip2D typedef void(__cdecl* P_MANIP)( int, bool, float, float, float, int, int, float, bool, float, unsigned int, float, float, // baseLength, stretchFactor float**, std::size_t*, float*, float*, float*, std::size_t*, float* ); // AGP_Free typedef void(__cdecl* P_FREE)(float*); // AgpStartManipND – предполагаем, что в DLL к исходному // прототипу добавлены baseLength и stretchFactor typedef void(__cdecl* P_START)( int, bool, float, float, float, int, int, float, bool, float, unsigned int, float, float // baseLength, stretchFactor ); // RunPythonOptimization из DLL typedef void(__cdecl* P_RUNPY)( const char* backend, int n_seg, bool var_len, float min_theta, float tx, float ty, int levels, int max_iter, float r_param, float eps, bool adaptive, float baseLength, float stretchFactor, float* out_bestF, float* out_bestX, float* out_bestY, float* out_angles, float* out_lengths, int* out_iterations, float* out_eps, float* out_micros // из Python – в МИКРОсекундах, мы переведём в мс ); namespace TESTAGP { public ref class MyForm sealed : public Form { public: MyForm(HMODULE hLib) : hLib(hLib), experimentsStarted(false) { this->Text = L"AGP vs Optuna vs iOpt – сравнение производительности"; this->ClientSize = System::Drawing::Size(1200, 800); this->SetStyle(ControlStyles::AllPaintingInWmPaint | ControlStyles::UserPaint | ControlStyles::OptimizedDoubleBuffer, true); // Загружаем функции из DLL fManip = reinterpret_cast<P_MANIP>(GetProcAddress(hLib, "AGP_Manip2D")); pFree = reinterpret_cast<P_FREE> (GetProcAddress(hLib, "AGP_Free")); pStart = reinterpret_cast<P_START>(GetProcAddress(hLib, "AgpStartManipND")); pRunPython = reinterpret_cast<P_RUNPY>(GetProcAddress(hLib, "RunPythonOptimization")); if (!fManip || !pFree || !pStart || !pRunPython) { MessageBox::Show( L"Не удалось получить адреса AGP_Manip2D / AGP_Free / AgpStartManipND / RunPythonOptimization из TEST_FUNC.dll", L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error); this->Close(); return; } // Параметры задачи: трёхзвенный манипулятор nSegments = 3; variableLengths = true; // используем baseLength/stretchFactor и для AGP, и для Python minTheta = 1.5f; targetX = 2.5f; targetY = -1.0f; peanoLevels = 12; rParam = 2.5f; epsParam = 1.0e-5f; baseLength = 1.0f; stretchFactor = 1.5f; adaptiveAgp = true; adaptiveIOpt = true; uiFontBold11 = gcnew System::Drawing::Font("Yu Gothic UI", 11, FontStyle::Bold); uiFontBold10 = gcnew System::Drawing::Font("Yu Gothic UI", 10, FontStyle::Regular); InitUI(); this->Shown += gcnew EventHandler(this, &MyForm::OnFormShown); this->Resize += gcnew EventHandler(this, &MyForm::OnResizeInternal); } private: // ----------- поля ----------- initonly HMODULE hLib; initonly P_MANIP fManip; initonly P_FREE pFree; initonly P_START pStart; initonly P_RUNPY pRunPython; System::Drawing::Font^ uiFontBold11; System::Drawing::Font^ uiFontBold10; Chart^ chartTime; Chart^ chartF; TextBox^ tbStats; bool experimentsStarted; // Общие параметры задачи int nSegments; bool variableLengths; float minTheta; float targetX; float targetY; int peanoLevels; float rParam; float epsParam; float baseLength; float stretchFactor; bool adaptiveAgp; bool adaptiveIOpt; // ----------- UI ----------- void InitUI() { chartTime = gcnew Chart(); chartF = gcnew Chart(); tbStats = gcnew TextBox(); chartTime->Parent = this; chartF->Parent = this; tbStats->Parent = this; chartTime->BorderlineDashStyle = ChartDashStyle::Solid; chartTime->BorderlineWidth = 1; chartTime->BorderlineColor = Color::Black; chartF->BorderlineDashStyle = ChartDashStyle::Solid; chartF->BorderlineWidth = 1; chartF->BorderlineColor = Color::Black; // --- chartTime: время (мс) vs итерации, X от 0, линии из (0,0) --- ChartArea^ areaTime = gcnew ChartArea("TimeArea"); areaTime->AxisX->Title = "Число итераций"; areaTime->AxisY->Title = "Время, мс"; areaTime->AxisX->Minimum = 0.0; areaTime->AxisY->Minimum = 0.0; chartTime->ChartAreas->Add(areaTime); Legend^ legendTime = gcnew Legend("LegendTime"); legendTime->Docking = Docking::Top; chartTime->Legends->Add(legendTime); // AGP, Optuna mean/best/worst, iOpt CreateSeries(chartTime, "AGP", "TimeArea"); CreateSeries(chartTime, "Optuna mean", "TimeArea"); CreateSeries(chartTime, "Optuna best", "TimeArea"); CreateSeries(chartTime, "Optuna worst", "TimeArea"); CreateSeries(chartTime, "iOpt", "TimeArea"); // --- chartF: f vs итерации, X начиная с 100 --- ChartArea^ areaF = gcnew ChartArea("FArea"); areaF->AxisX->Title = "Число итераций"; areaF->AxisY->Title = "Значение целевой функции f"; areaF->AxisX->Minimum = 100.0; // первый порог по итерациям chartF->ChartAreas->Add(areaF); Legend^ legendF = gcnew Legend("LegendF"); legendF->Docking = Docking::Top; chartF->Legends->Add(legendF); CreateSeries(chartF, "AGP", "FArea"); CreateSeries(chartF, "Optuna mean", "FArea"); CreateSeries(chartF, "Optuna best", "FArea"); CreateSeries(chartF, "Optuna worst", "FArea"); CreateSeries(chartF, "iOpt", "FArea"); // --- stats textbox --- tbStats->Multiline = true; tbStats->ReadOnly = true; tbStats->ScrollBars = ScrollBars::Vertical; tbStats->Font = uiFontBold10; tbStats->Text = L"Подготовка..."; OnResizeInternal(nullptr, nullptr); } void CreateSeries(Chart^ chart, String^ name, String^ areaName) { Series^ s = gcnew Series(name); s->ChartType = SeriesChartType::Line; s->ChartArea = areaName; s->BorderWidth = 3; s->MarkerStyle = MarkerStyle::Circle; s->MarkerSize = 6; chart->Series->Add(s); } void OnResizeInternal(Object^, EventArgs^) { int margin = 10; int statsWidth = 280; int totalWidth = this->ClientSize.Width; int totalHeight = this->ClientSize.Height; if (totalWidth < 400) totalWidth = 400; if (totalHeight < 300) totalHeight = 300; int chartsWidth = totalWidth - statsWidth - 3 * margin; if (chartsWidth < 200) chartsWidth = 200; int chartHeight = (totalHeight - 3 * margin) / 2; chartTime->Location = Point(margin, margin); chartTime->Size = System::Drawing::Size(chartsWidth, chartHeight); chartF->Location = Point(margin, 2 * margin + chartHeight); chartF->Size = System::Drawing::Size(chartsWidth, chartHeight); tbStats->Location = Point(2 * margin + chartsWidth, margin); tbStats->Size = System::Drawing::Size(statsWidth, totalHeight - 2 * margin); } void OnFormShown(Object^, EventArgs^) { if (experimentsStarted) return; experimentsStarted = true; try { RunAllExperiments(); } catch (Exception^ ex) { tbStats->Text = L"Ошибка при выполнении экспериментов:\r\n" + ex->ToString(); } } // ----------- статистические утилиты ----------- static void ComputeMeanStd(List<double>^ values, double% mean, double% stdDev) { int n = values->Count; if (n == 0) { mean = Double::NaN; stdDev = Double::NaN; return; } double sum = 0.0; for each (double v in values) sum += v; mean = sum / n; if (n == 1) { stdDev = 0.0; return; } double var = 0.0; for each (double v in values) { double d = v - mean; var += d * d; } stdDev = Math::Sqrt(var / (n - 1)); } static double ComputeMean(List<double>^ values) { double m, s; ComputeMeanStd(values, m, s); return m; } // ----------- Запуск Python-бэкенда через RunPythonOptimization ----------- bool RunPythonSingle( const char* backend, int maxIter, double% outBestF, double% outBestX, double% outBestY, int% outIterations, double% outEps, double% outMillis) { float bestF = 0.0f; float bestX = 0.0f; float bestY = 0.0f; int iters = 0; float eps = 0.0f; float micros = 0.0f; pRunPython( backend, nSegments, variableLengths, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, epsParam, adaptiveIOpt, baseLength, stretchFactor, &bestF, &bestX, &bestY, nullptr, // out_angles нам не нужен nullptr, // out_lengths нам не нужен &iters, &eps, &micros ); outBestF = static_cast<double>(bestF); outBestX = static_cast<double>(bestX); outBestY = static_cast<double>(bestY); outIterations = iters; outEps = static_cast<double>(eps); outMillis = static_cast<double>(micros) / 1000.0; // микросекунды → миллисекунды return true; } // ----------- Запуск одного AGP (C++) ----------- bool RunAgpSingle( int maxIter, double% outBestF, double% outBestX, double% outBestY, std::size_t% outIterations, double% outAchievedEps, double% outMillis) { float* bestQ = nullptr; std::size_t bestQLen = 0; float bestXf = 0.0f; float bestYf = 0.0f; float bestFf = 0.0f; std::size_t actualIterations = 0; float achievedEps = 0.0f; unsigned int seed = static_cast<unsigned int>(GetTickCount()); // запускаем воркеры pStart( nSegments, variableLengths, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, adaptiveAgp, epsParam, seed, baseLength, stretchFactor ); LARGE_INTEGER t0, t1, fq; QueryPerformanceCounter(&t0); // основной AGP на ранге 0 fManip( nSegments, variableLengths, minTheta, targetX, targetY, peanoLevels, maxIter, rParam, adaptiveAgp, epsParam, seed, baseLength, stretchFactor, &bestQ, &bestQLen, &bestXf, &bestYf, &bestFf, &actualIterations, &achievedEps ); QueryPerformanceCounter(&t1); QueryPerformanceFrequency(&fq); double millis = 1.0e3 * static_cast<double>(t1.QuadPart - t0.QuadPart) / static_cast<double>(fq.QuadPart); if (bestQ != nullptr) { pFree(bestQ); } outBestF = static_cast<double>(bestFf); outBestX = static_cast<double>(bestXf); outBestY = static_cast<double>(bestYf); outIterations = actualIterations; outAchievedEps = static_cast<double>(achievedEps); outMillis = millis; return true; } // ----------- Основной эксперимент ----------- void RunAllExperiments() { // Пороги maxIter array<int>^ thresholds = gcnew array<int>{ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; int steps = thresholds->Length; const int repeatsStochastic = 20; // Optuna и AGP // --- массивы для графиков --- // AGP array<double>^ agpIterMean = gcnew array<double>(steps); array<double>^ agpTimeMean = gcnew array<double>(steps); array<double>^ agpTimeStd = gcnew array<double>(steps); array<double>^ agpFMean = gcnew array<double>(steps); array<double>^ agpFStd = gcnew array<double>(steps); // Optuna – средние array<double>^ optIterMean = gcnew array<double>(steps); array<double>^ optTimeMean = gcnew array<double>(steps); array<double>^ optTimeStd = gcnew array<double>(steps); array<double>^ optFMean = gcnew array<double>(steps); array<double>^ optFStd = gcnew array<double>(steps); // Optuna – best/worst по времени array<double>^ optIterBestTime = gcnew array<double>(steps); array<double>^ optTimeBest = gcnew array<double>(steps); array<double>^ optIterWorstTime = gcnew array<double>(steps); array<double>^ optTimeWorst = gcnew array<double>(steps); // Optuna – best/worst по f array<double>^ optIterBestF = gcnew array<double>(steps); array<double>^ optFBest = gcnew array<double>(steps); array<double>^ optIterWorstF = gcnew array<double>(steps); array<double>^ optFWorst = gcnew array<double>(steps); // iOpt array<double>^ ioptIter = gcnew array<double>(steps); array<double>^ ioptTime = gcnew array<double>(steps); array<double>^ ioptF = gcnew array<double>(steps); // Инициализация NaN for (int i = 0; i < steps; ++i) { agpIterMean[i] = agpTimeMean[i] = agpTimeStd[i] = agpFMean[i] = agpFStd[i] = Double::NaN; optIterMean[i] = optTimeMean[i] = optTimeStd[i] = optFMean[i] = optFStd[i] = Double::NaN; optIterBestTime[i] = optTimeBest[i] = optIterWorstTime[i] = optTimeWorst[i] = Double::NaN; optIterBestF[i] = optFBest[i] = optIterWorstF[i] = optFWorst[i] = Double::NaN; ioptIter[i] = ioptTime[i] = ioptF[i] = Double::NaN; } // Для глобальных μ, σ List<double>^ agpTimesAll = gcnew List<double>(); List<double>^ agpFAll = gcnew List<double>(); List<double>^ optTimesAll = gcnew List<double>(); List<double>^ optFAll = gcnew List<double>(); List<double>^ ioptTimesAll = gcnew List<double>(); List<double>^ ioptFAll = gcnew List<double>(); // Для MSE относительно iOpt double mseTimeAgp = 0.0; double mseTimeOptuna = 0.0; double mseFAgp = 0.0; double mseFOptuna = 0.0; int mseCount = 0; for (int idx = 0; idx < steps; ++idx) { int maxIter = thresholds[idx]; // ----- iOpt: 1 запуск ----- { double bF, bX, bY, eps, tMs; int iters; RunPythonSingle("iopt", maxIter, bF, bX, bY, iters, eps, tMs); ioptIter[idx] = static_cast<double>(iters); ioptTime[idx] = tMs; ioptF[idx] = bF; ioptTimesAll->Add(tMs); ioptFAll->Add(bF); } // ----- Optuna: 20 запусков ----- List<double>^ optIterLoc = gcnew List<double>(); List<double>^ optTimeLoc = gcnew List<double>(); List<double>^ optFLoc = gcnew List<double>(); for (int r = 0; r < repeatsStochastic; ++r) { double bF, bX, bY, eps, tMs; int iters; RunPythonSingle("optuna", maxIter, bF, bX, bY, iters, eps, tMs); optIterLoc->Add(static_cast<double>(iters)); optTimeLoc->Add(tMs); optFLoc->Add(bF); optTimesAll->Add(tMs); optFAll->Add(bF); Application::DoEvents(); } optIterMean[idx] = ComputeMean(optIterLoc); ComputeMeanStd(optTimeLoc, optTimeMean[idx], optTimeStd[idx]); ComputeMeanStd(optFLoc, optFMean[idx], optFStd[idx]); // best / worst по времени if (optTimeLoc->Count > 0) { double bestT = Double::PositiveInfinity; double worstT = Double::NegativeInfinity; int idxBestT = -1, idxWorstT = -1; for (int r = 0; r < optTimeLoc->Count; ++r) { double t = optTimeLoc[r]; if (t < bestT) { bestT = t; idxBestT = r; } if (t > worstT) { worstT = t; idxWorstT = r; } } if (idxBestT >= 0) { optTimeBest[idx] = bestT; optIterBestTime[idx] = optIterLoc[idxBestT]; } if (idxWorstT >= 0) { optTimeWorst[idx] = worstT; optIterWorstTime[idx] = optIterLoc[idxWorstT]; } } // best / worst по f if (optFLoc->Count > 0) { double bestF = Double::PositiveInfinity; double worstF = Double::NegativeInfinity; int idxBestF = -1, idxWorstF = -1; for (int r = 0; r < optFLoc->Count; ++r) { double fv = optFLoc[r]; if (fv < bestF) { bestF = fv; idxBestF = r; } if (fv > worstF) { worstF = fv; idxWorstF = r; } } if (idxBestF >= 0) { optFBest[idx] = bestF; optIterBestF[idx] = optIterLoc[idxBestF]; } if (idxWorstF >= 0) { optFWorst[idx] = worstF; optIterWorstF[idx] = optIterLoc[idxWorstF]; } } // ----- AGP: 20 запусков ----- List<double>^ agpIterLoc = gcnew List<double>(); List<double>^ agpTimeLoc = gcnew List<double>(); List<double>^ agpFLoc = gcnew List<double>(); for (int r = 0; r < repeatsStochastic; ++r) { double bF, bX, bY, eps, tMs; std::size_t iters; RunAgpSingle(maxIter, bF, bX, bY, iters, eps, tMs); agpIterLoc->Add(static_cast<double>(iters)); agpTimeLoc->Add(tMs); agpFLoc->Add(bF); agpTimesAll->Add(tMs); agpFAll->Add(bF); Application::DoEvents(); } agpIterMean[idx] = ComputeMean(agpIterLoc); ComputeMeanStd(agpTimeLoc, agpTimeMean[idx], agpTimeStd[idx]); ComputeMeanStd(agpFLoc, agpFMean[idx], agpFStd[idx]); // ----- Обновляем графики ----- UpdateCharts( steps, idx, agpIterMean, agpTimeMean, optIterMean, optTimeMean, optIterBestTime, optTimeBest, optIterWorstTime, optTimeWorst, ioptIter, ioptTime, agpFMean, optFMean, optIterBestF, optFBest, optIterWorstF, optFWorst, ioptF ); // ----- вклад в MSE относительно iOpt ----- if (!Double::IsNaN(ioptTime[idx]) && !Double::IsNaN(agpTimeMean[idx]) && !Double::IsNaN(optTimeMean[idx]) && !Double::IsNaN(ioptF[idx]) && !Double::IsNaN(agpFMean[idx]) && !Double::IsNaN(optFMean[idx])) { double dtAgp = agpTimeMean[idx] - ioptTime[idx]; double dtOptuna = optTimeMean[idx] - ioptTime[idx]; double dfAgp = agpFMean[idx] - ioptF[idx]; double dfOptuna = optFMean[idx] - ioptF[idx]; mseTimeAgp += dtAgp * dtAgp; mseTimeOptuna += dtOptuna * dtOptuna; mseFAgp += dfAgp * dfAgp; mseFOptuna += dfOptuna * dfOptuna; ++mseCount; } } // --- глобальные μ, σ по всем порогам --- double agpTimeMu, agpTimeSigma; double agpFMu, agpFSigma; double optTimeMu, optTimeSigma; double optFMu, optFSigma; double iTimeMu, iTimeSigma; double iFMu, iFSigma; ComputeMeanStd(agpTimesAll, agpTimeMu, agpTimeSigma); ComputeMeanStd(agpFAll, agpFMu, agpFSigma); ComputeMeanStd(optTimesAll, optTimeMu, optTimeSigma); ComputeMeanStd(optFAll, optFMu, optFSigma); ComputeMeanStd(ioptTimesAll, iTimeMu, iTimeSigma); ComputeMeanStd(ioptFAll, iFMu, iFSigma); double rmseTimeAgp = (mseCount > 0) ? Math::Sqrt(mseTimeAgp / mseCount) : 0.0; double rmseTimeOptuna = (mseCount > 0) ? Math::Sqrt(mseTimeOptuna / mseCount) : 0.0; double rmseFAgp = (mseCount > 0) ? Math::Sqrt(mseFAgp / mseCount) : 0.0; double rmseFOptuna = (mseCount > 0) ? Math::Sqrt(mseFOptuna / mseCount) : 0.0; // --- текст справа --- System::Text::StringBuilder^ sb = gcnew System::Text::StringBuilder(); sb->AppendLine(L"Сводная статистика (пороги maxIter: 100–1000, шаг 100)"); sb->AppendLine(L"====================================================="); sb->AppendLine(); sb->AppendLine(L"iOpt (детерминированный, 1 запуск на порог):"); sb->AppendFormat(L" Время t, мс: μ = {0:F3}, σ = {1:F3}\r\n", iTimeMu, iTimeSigma); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}\r\n", iFMu, iFSigma); sb->AppendLine(); sb->AppendLine(L"AGP (C++, 20 запусков на каждый порог):"); sb->AppendFormat(L" Время t, мс: μ = {0:F3}, σ = {1:F3}, √MSE(t vs iOpt) = {2:F3}\r\n", agpTimeMu, agpTimeSigma, rmseTimeAgp); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}, √MSE(f vs iOpt) = {2:F5}\r\n", agpFMu, agpFSigma, rmseFAgp); sb->AppendLine(); sb->AppendLine(L"Optuna (20 запусков на каждый порог):"); sb->AppendFormat(L" Время t, мс: μ = {0:F3}, σ = {1:F3}, √MSE(t vs iOpt) = {2:F3}\r\n", optTimeMu, optTimeSigma, rmseTimeOptuna); sb->AppendFormat(L" Целевая f: μ = {0:F5}, σ = {1:F5}, √MSE(f vs iOpt) = {2:F5}\r\n", optFMu, optFSigma, rmseFOptuna); tbStats->Text = sb->ToString(); } // ----------- обновление графиков ----------- void UpdateCharts( int steps, int filledUpTo, array<double>^ agpIterMean, array<double>^ agpTimeMean, array<double>^ optIterMean, array<double>^ optTimeMean, array<double>^ optIterBestTime, array<double>^ optTimeBest, array<double>^ optIterWorstTime, array<double>^ optTimeWorst, array<double>^ ioptIter, array<double>^ ioptTime, array<double>^ agpFMean, array<double>^ optFMean, array<double>^ optIterBestF, array<double>^ optFBest, array<double>^ optIterWorstF, array<double>^ optFWorst, array<double>^ ioptF) { // --- chartTime --- Series^ sAgpTime = chartTime->Series["AGP"]; Series^ sOptTimeMean = chartTime->Series["Optuna mean"]; Series^ sOptTimeBest = chartTime->Series["Optuna best"]; Series^ sOptTimeWorst = chartTime->Series["Optuna worst"]; Series^ sIoptTime = chartTime->Series["iOpt"]; sAgpTime->Points->Clear(); sOptTimeMean->Points->Clear(); sOptTimeBest->Points->Clear(); sOptTimeWorst->Points->Clear(); sIoptTime->Points->Clear(); // Чтобы кривые начинались из (0,0) sAgpTime->Points->AddXY(0.0, 0.0); sOptTimeMean->Points->AddXY(0.0, 0.0); sOptTimeBest->Points->AddXY(0.0, 0.0); sOptTimeWorst->Points->AddXY(0.0, 0.0); sIoptTime->Points->AddXY(0.0, 0.0); for (int i = 0; i <= filledUpTo && i < steps; ++i) { if (!Double::IsNaN(agpTimeMean[i])) { sAgpTime->Points->AddXY(agpIterMean[i], agpTimeMean[i]); } if (!Double::IsNaN(optTimeMean[i])) { sOptTimeMean->Points->AddXY(optIterMean[i], optTimeMean[i]); } if (!Double::IsNaN(optTimeBest[i])) { sOptTimeBest->Points->AddXY(optIterBestTime[i], optTimeBest[i]); } if (!Double::IsNaN(optTimeWorst[i])) { sOptTimeWorst->Points->AddXY(optIterWorstTime[i], optTimeWorst[i]); } if (!Double::IsNaN(ioptTime[i])) { sIoptTime->Points->AddXY(ioptIter[i], ioptTime[i]); } } // --- chartF --- Series^ sAgpF = chartF->Series["AGP"]; Series^ sOptFMean = chartF->Series["Optuna mean"]; Series^ sOptFBest = chartF->Series["Optuna best"]; Series^ sOptFWorst = chartF->Series["Optuna worst"]; Series^ sIoptF = chartF->Series["iOpt"]; sAgpF->Points->Clear(); sOptFMean->Points->Clear(); sOptFBest->Points->Clear(); sOptFWorst->Points->Clear(); sIoptF->Points->Clear(); for (int i = 0; i <= filledUpTo && i < steps; ++i) { if (!Double::IsNaN(agpFMean[i])) { sAgpF->Points->AddXY(agpIterMean[i], agpFMean[i]); } if (!Double::IsNaN(optFMean[i])) { sOptFMean->Points->AddXY(optIterMean[i], optFMean[i]); } if (!Double::IsNaN(optFBest[i])) { sOptFBest->Points->AddXY(optIterBestF[i], optFBest[i]); } if (!Double::IsNaN(optFWorst[i])) { sOptFWorst->Points->AddXY(optIterWorstF[i], optFWorst[i]); } if (!Double::IsNaN(ioptF[i])) { sIoptF->Points->AddXY(ioptIter[i], ioptF[i]); } } chartTime->Invalidate(); chartF->Invalidate(); this->Refresh(); } }; } // namespace TESTAGP

Не забудьте в проекте добавить ссылку на сборку
System.Windows.Forms.DataVisualization (Reference → Add Reference…).


2.2. Обновлённый MyForm.cpp

cpp
#include "MyForm.h" using namespace System; using namespace System::Windows::Forms; typedef int(__cdecl* PInit)(int, float, float, float, float); typedef void(__cdecl* PStartWorkers)(); [STAThread] int main() { HMODULE h = LoadLibraryW(L"TEST_FUNC.dll"); if (!h) { MessageBox::Show(L"Не удалось загрузить TEST_FUNC.dll", L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error); return 1; } auto AgpInit = (PInit)GetProcAddress(h, "AgpInit"); auto AgpWaitStartAndRun = (PStartWorkers)GetProcAddress(h, "AgpWaitStartAndRun"); if (!AgpInit || !AgpWaitStartAndRun) { MessageBox::Show(L"Не удалось получить AgpInit/AgpWaitStartAndRun из TEST_FUNC.dll", L"Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error); return 1; } const int rank = AgpInit(12, -2.2f, 1.8f, -2.2f, 1.8f); if (!rank) { Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); Application::Run(gcnew TESTAGP::MyForm(h)); } else { // worker-процессы – крутятся в ожидании параметров и вызывают AGP_Manip2D AgpWaitStartAndRun(); } return 0; }

Если хочешь, дальше можно:

  • немного подшаманить оформление легенд (например, сделать Optuna-best/ worst тоньше или пунктиром);
  • добавить вывод таблицы по порогам (как в твоём текстовом примере) в отдельное окно / сохранение в CSV.

Но для задач научрука: 20 прогонов, время в мс, линии из (0,0) на первом чарте, ось X с 100 на втором, Optuna: mean/best/worst сверху/снизу — всё в этом варианте уже учтено.

Compartilhar esta Q&A