Skip to content

Scale internal tolerances with working precision (single-precision robustness)#158

Open
cl445 wants to merge 1 commit into
darnstrom:masterfrom
cl445:fix/single-precision-tolerances
Open

Scale internal tolerances with working precision (single-precision robustness)#158
cl445 wants to merge 1 commit into
darnstrom:masterfrom
cl445:fix/single-precision-tolerances

Conversation

@cl445

@cl445 cl445 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Three internal tolerances are hard-coded as double literals that sit below the single-precision roundoff floor (float eps 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, the daqp_lu singularity check max_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 than 1e-12. The check passes, so the factorization divides by that noise and pushes Inf/NaN into the AVI solve. In single precision, singular matrices are essentially never caught.
  • src/api.c, daqp_primal_init_active (tol = 1e-9) and daqp_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.h and use them at the three sites:

Macro double single
DAQP_LU_SING_TOL 1e-12 1e-6
DAQP_INIT_PRIM_TOL 1e-9 1e-5
DAQP_INIT_DUAL_TOL 1e-12 1e-6

The double-precision values match the previous literals exactly, so default builds are unaffected. Only the DAQP_SINGLE_PRECISION path changes.

Notes

  • The single-precision values are picked to sit above the float roundoff floor; treat them as a starting point.
  • The user-facing 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:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build
cmake -S . -B build_sp -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-DDAQP_SINGLE_PRECISION" && cmake --build build_sp

@cl445 cl445 force-pushed the fix/single-precision-tolerances branch from bc41833 to 40276b9 Compare July 10, 2026 08:10
@cl445 cl445 marked this pull request as ready for review July 10, 2026 08:12
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.
@cl445 cl445 force-pushed the fix/single-precision-tolerances branch from 40276b9 to 0345d52 Compare July 10, 2026 08:13
@cl445 cl445 marked this pull request as draft July 10, 2026 08:26
@cl445 cl445 marked this pull request as ready for review July 10, 2026 08:30
@cl445 cl445 marked this pull request as draft July 10, 2026 09:22
@cl445 cl445 marked this pull request as ready for review July 10, 2026 10:48
@darnstrom

Copy link
Copy Markdown
Owner

Thank you for starting this PR!

Having different defaults depending on double/single precision makes a lot of sense! When I have used daqp for single-precision scenarios, I have used similar values that you selected here primal, dual, and zero tolerances. But it would definitely be nice to have most default settings be different if the user sets the single-precision flag out of convenience.

I think it would make sense to also select different values for the DAQP_DEFAULT_* values. I suppose the square root of most floating-point tolerances already set should be sufficient as a first attempt. Even better, it would be nice to have some test cases where single precision is used to make sure that the default values for the tolerances in single precision make sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants