From 0980501b0708e02b81c89fc3534d8417a8c7666a Mon Sep 17 00:00:00 2001 From: govindchari Date: Wed, 6 May 2026 11:29:40 -0700 Subject: [PATCH] Revise duality gap check Use abs(pobj - dobj) for the duality gap (with safe_div scaling by k) instead of the complementary slackness inner product, and drop the now unused ubuff2 alias in check_stopping. --- src/qoco_utils.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/qoco_utils.c b/src/qoco_utils.c index 95acd1b..126b4ce 100644 --- a/src/qoco_utils.c +++ b/src/qoco_utils.c @@ -221,7 +221,6 @@ unsigned char check_stopping(QOCOSolver* solver) QOCOFloat* xbuff = get_data_vectorf(work->xbuff); QOCOFloat* ybuff = get_data_vectorf(work->ybuff); QOCOFloat* ubuff1 = get_data_vectorf(work->ubuff1); - QOCOFloat* ubuff2 = get_data_vectorf(work->ubuff2); QOCOFloat* ubuff3 = get_data_vectorf(work->ubuff3); QOCOFloat* kktres = get_data_vectorf(work->kktres); @@ -292,13 +291,6 @@ unsigned char check_stopping(QOCOSolver* solver) QOCOFloat dres = inf_norm(xbuff, data->n); solver->sol->dres = dres; - // Compute complementary slackness residual. - ew_product(sdata, Fruiz_data, ubuff1, data->m); - ew_product(zdata, Fruiz_data, ubuff2, data->m); - QOCOFloat gap = qoco_dot(ubuff1, ubuff2, data->m); - gap *= work->scaling->kinv; - solver->sol->gap = gap; - // Compute max{Axinf, binf, Gxinf, hinf, sinf}. QOCOFloat pres_rel = qoco_max(Axinf, binf); pres_rel = qoco_max(pres_rel, Gxinf); @@ -311,19 +303,23 @@ unsigned char check_stopping(QOCOSolver* solver) dres_rel = qoco_max(dres_rel, cinf); dres_rel *= work->scaling->kinv; - // Compute max{1, abs(pobj), abs(dobj)}. + // Compute gap as abs(pobj - dobj) and relative scale max{1, abs(pobj), + // abs(dobj)}. QOCOFloat* cdata = get_data_vectorf(work->data->c); QOCOFloat ctx = qoco_dot(cdata, xdata, work->data->n); QOCOFloat bty = qoco_dot(bdata, ydata, work->data->p); QOCOFloat htz = qoco_dot(hdata, zdata, work->data->m); QOCOFloat pobj = 0.5 * xPx + ctx; QOCOFloat dobj = -0.5 * xPx - bty - htz; + pobj = safe_div(pobj, work->scaling->k); + dobj = safe_div(dobj, work->scaling->k); + QOCOFloat pobj_abs = qoco_abs(pobj); + QOCOFloat dobj_abs = qoco_abs(dobj); - pobj = qoco_abs(pobj); - dobj = qoco_abs(dobj); - - QOCOFloat gap_rel = qoco_max(1, pobj); - gap_rel = qoco_max(gap_rel, dobj); + QOCOFloat gap_abs = qoco_abs(pobj - dobj); + QOCOFloat gap_rel = qoco_max(1, pobj_abs); + gap_rel = qoco_max(gap_rel, dobj_abs); + solver->sol->gap = gap_abs; // Composite progress metric in inaccurate-tolerance units. metric <= 1.0 // means the current iterate satisfies the inaccurate stopping criterion. @@ -371,7 +367,7 @@ unsigned char check_stopping(QOCOSolver* solver) if (solver->settings->kkt_dynamic_reg > 1e-6) { if (pres < eabsinacc + erelinacc * pres_rel && dres < eabsinacc + erelinacc * dres_rel && - solver->sol->gap < eabsinacc + erelinacc * gap_rel) { + gap_abs < eabsinacc + erelinacc * gap_rel) { solver->sol->status = QOCO_SOLVED_INACCURATE; return 1; } @@ -382,7 +378,7 @@ unsigned char check_stopping(QOCOSolver* solver) } if (pres < eabs + erel * pres_rel && dres < eabs + erel * dres_rel && - solver->sol->gap < eabs + erel * gap_rel) { + gap_abs < eabs + erel * gap_rel) { solver->sol->status = QOCO_SOLVED; return 1; } @@ -477,4 +473,4 @@ QOCOSettings* copy_settings(QOCOSettings* settings) new_settings->verbose = settings->verbose; return new_settings; -} \ No newline at end of file +}