Scale internal tolerances with working precision (single-precision robustness)#158
Scale internal tolerances with working precision (single-precision robustness)#158cl445 wants to merge 1 commit into
Conversation
bc41833 to
40276b9
Compare
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.
40276b9 to
0345d52
Compare
|
Thank you for starting this PR! Having different defaults depending on double/single precision makes a lot of sense! When I have used I think it would make sense to also select different values for the |
Problem
Three internal tolerances are hard-coded as double literals that sit below the single-precision roundoff floor (
floateps is about 1.2e-7). When DAQP is built with-DDAQP_SINGLE_PRECISION(a supported path; it is handled in the MATLAB interface) they misbehave:src/utils.c, thedaqp_lusingularity checkmax_val < 1e-12. For a singular matrix the largest pivot is around float-noise magnitude (about 1e-7 for O(1) data), which is larger than1e-12. The check passes, so the factorization divides by that noise and pushesInf/NaNinto the AVI solve. In single precision, singular matrices are essentially never caught.src/api.c,daqp_primal_init_active(tol = 1e-9) anddaqp_dual_init_active(tol = 1e-12). These "on-bound" and multiplier-sign bands are below the float floor, so the warm-start active-set heuristics degenerate: almost any nonzero slack or multiplier lands on the wrong side of the band.Change
Add precision-selected macros in
include/constants.hand use them at the three sites:DAQP_LU_SING_TOLDAQP_INIT_PRIM_TOLDAQP_INIT_DUAL_TOLThe double-precision values match the previous literals exactly, so default builds are unaffected. Only the
DAQP_SINGLE_PRECISIONpath changes.Notes
DAQP_DEFAULT_*settings are left alone to keep the scope small, though several of those are double-tuned too and could get the same treatment.Testing
Builds cleanly in both precisions: