From 0345d521bae4011f91b5cd09b2acb7886bc61ad3 Mon Sep 17 00:00:00 2001 From: Claas Flint Date: Fri, 10 Jul 2026 10:07:32 +0200 Subject: [PATCH] Scale internal tolerances with working precision The hard-coded tolerances 1e-12 (LU singularity check in daqp_lu) and 1e-9/1e-12 (primal/dual warm-start active-set init) lie far below the single-precision roundoff floor (float eps ~ 1.2e-7). When built with -DDAQP_SINGLE_PRECISION this means: - daqp_lu never reports singularity: a singular matrix yields a pivot around float-noise magnitude (~1e-7 for O(1) data), which exceeds 1e-12, so the check passes and the factorization divides by noise (propagating Inf/NaN into the AVI solve). - the warm-start heuristics degenerate, since essentially any nonzero slack/multiplier falls inside/outside the 1e-9/1e-12 bands. Introduce DAQP_LU_SING_TOL / DAQP_INIT_PRIM_TOL / DAQP_INIT_DUAL_TOL in constants.h, selected by DAQP_SINGLE_PRECISION. The double-precision values are byte-identical to the previous literals, so standard builds are unaffected; single precision gets tolerances above the float floor. --- include/constants.h | 11 +++++++++++ src/api.c | 4 ++-- src/utils.c | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/constants.h b/include/constants.h index 8b02018..4543f03 100644 --- a/include/constants.h +++ b/include/constants.h @@ -11,6 +11,17 @@ extern "C" { #define DAQP_UNCONSTRAINED_OPTIMAL -2 #define DAQP_INF ((c_float)1e30) +// PRECISION-DEPENDENT TOLERANCES +#ifdef DAQP_SINGLE_PRECISION +#define DAQP_LU_SING_TOL ((c_float)1e-6) // pivot magnitude below this is singular (daqp_lu) +#define DAQP_INIT_PRIM_TOL ((c_float)1e-5) // slack "on bound" band for primal warm start +#define DAQP_INIT_DUAL_TOL ((c_float)1e-6) // multiplier sign threshold for dual warm start +#else +#define DAQP_LU_SING_TOL ((c_float)1e-12) +#define DAQP_INIT_PRIM_TOL ((c_float)1e-9) +#define DAQP_INIT_DUAL_TOL ((c_float)1e-12) +#endif + // DEFAULT SETTINGS #define DAQP_DEFAULT_PRIM_TOL 1e-6 #define DAQP_DEFAULT_DUAL_TOL 1e-12 diff --git a/src/api.c b/src/api.c index 8ef4754..73efa99 100644 --- a/src/api.c +++ b/src/api.c @@ -539,7 +539,7 @@ int daqp_first_violating(c_float* x, c_float* A, c_float* bu, c_float* bl, int n void daqp_primal_init_active(DAQPProblem* qp, c_float* x){ int i,disp; c_float Ax, slack; - c_float tol= 1e-9; + c_float tol= DAQP_INIT_PRIM_TOL; // Simple constraints for(i=0; i < qp->ms; i++){ @@ -579,7 +579,7 @@ void daqp_primal_init_active(DAQPProblem* qp, c_float* x){ // based on a dual iterate void daqp_dual_init_active(DAQPProblem* qp, c_float* lam){ int i; - c_float tol = 1e-12; + c_float tol = DAQP_INIT_DUAL_TOL; for(i=0; i < qp->m; i++){ if(qp->sense[i] & DAQP_IMMUTABLE) continue; if(lam[i] > tol){ diff --git a/src/utils.c b/src/utils.c index c551dba..7af1dd3 100644 --- a/src/utils.c +++ b/src/utils.c @@ -534,7 +534,7 @@ int daqp_lu(c_float* A, int* P, int n) { } // Check for singularity - if (max_val < 1e-12) return -1; + if (max_val < DAQP_LU_SING_TOL) return -1; // Swap rows in A for (int k = 0; k < n; k++) {