Fixed the issue by adding a fallback to bissection#2066
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2066 +/- ##
==========================================
+ Coverage 86.63% 86.75% +0.11%
==========================================
Files 149 149
Lines 8921 8887 -34
==========================================
- Hits 7729 7710 -19
+ Misses 1192 1177 -15 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
The solution here is too crude. The more fundamental problem is that the stopping rule doesn't take into account the granularity of the floating point numbers around the solution. Try to ask the AI to consider that and about how this is traditionally handled in quantile functions. I also think we should have a |
- Add magnitude-aware tolerance floor to prevent chasing floating-point granularity - Reduce max_iter to 50 (proper stopping rule should rarely need it) - Add clear warnings for non-convergence with fallback to bisection - Detect 2-cycles explicitly as sign of ill-conditioning
|
Hi, thanks for the comment, Also dropped |
|
I think we should use Roots.jl. This would also easily allow us to use better bracketing root solvers such as ITP instead of vanilla bisection. |
|
Okay that makes sense., I switched the fallback to use Roots.jl with ITP instead. |
|
My point was to generally make use of Roots. Being able to use ITP instead of vanilla bisection is just one benefit. Generally, the algorithms and numerical corner cases should be much better battle tested in Roots. |
|
Just to make sure I understand: are you suggesting the we replace the internal implementation of |
|
Yes, that's my suggestion. We would tune only the options if the default ones are not appropriate. |
Delegates core root-finding logic in quantile_newton functions to Roots.find_zero using Newton(). Upgrades quantile_bisect to use the ITP() bracketing solver as suggested. Retains precision-based xrtol tolerance scaling.
|
Okay, I've now refactored the core logic of |
…equal - the `chernoff` distribution now uses `quantile_newton` for none precomputed quantiles - Added test cases for the following issues (JuliaStats#1571, JuliaStats#1611, JuliaStats#1807, JuliaStats#1869, JuliaStats#1999, JuliaStats#2061)
|
I added regression tests for the six quantile-related issues you listed. I found a couple of other quantile-related issues like #1415, but they don't seem related to the convergence issues this PR focuses on. Should I fix and include those too? |
Building on #2066, simplify the quantile solvers to rely on Roots' default tolerances instead of hand-tuned options: - `quantile_bisect` delegates to `find_zero(g, (lx, rx), ITP())`. ITP's relative `xrtol = eps` converges even for brackets far from zero, where the previous absolute `cbrt(eps)^2` tolerance fell below the floating-point spacing and looped forever (#1611, #1807). - The four `_newton` functions delegate to `find_zero((F, f), x, Newton(), ITP())`. Roots switches to the ITP bracketing method as soon as Newton brackets the root, which resolves the oscillation/stalling seen for extreme quantiles (#1571, #1898, #2061). The `tol`/`max_iters` parameters and the explicit tolerance floors are dropped: Roots' Newton defaults combine an absolute (`xatol = eps`) and a relative (`xrtol = eps`) x-tolerance, so they converge both near `p ≈ 1` and for roots near zero without bespoke floors. `quantile_bisect` keeps only the exact `rx == lx` degenerate guard (#1501); the approximate "collapsed bracket" handling is dropped, as a non-bracketing interval is a separate concern from the solver choice. Tests use `isapprox`'s type-appropriate defaults rather than fixed tolerances, and are split one regression per testset. Adds coverage for #1898 and a `Float32` round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Building on #2066, simplify the quantile solvers to rely on Roots' default tolerances instead of hand-tuned options: - `quantile_bisect` delegates to `find_zero(g, (lx, rx), ITP())`. ITP's relative `xrtol = eps` converges even for brackets far from zero, where the previous absolute `cbrt(eps)^2` tolerance fell below the floating-point spacing and looped forever (#1611, #1807). - The four `_newton` functions delegate to `find_zero((F, f), x, Newton(), ITP())`. Roots switches to the ITP bracketing method as soon as Newton brackets the root, which resolves the oscillation/stalling seen for extreme quantiles (#1571, #1898, #2061). The `tol`/`max_iters` parameters and the explicit tolerance floors are dropped: Roots' Newton defaults combine an absolute (`xatol = eps`) and a relative (`xrtol = eps`) x-tolerance, so they converge both near `p ≈ 1` and for roots near zero without bespoke floors. `quantile_bisect` keeps only the exact `rx == lx` degenerate guard (#1501); the approximate "collapsed bracket" handling is dropped, as a non-bracketing interval is a separate concern from the solver choice. Tests use `isapprox`'s type-appropriate defaults rather than fixed tolerances, and are split one regression per testset. Adds coverage for #1898 and a `Float32` round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fixed the issue by adding a fallback to bissection * Improve quantile_newton stopping criterion - Add magnitude-aware tolerance floor to prevent chasing floating-point granularity - Reduce max_iter to 50 (proper stopping rule should rarely need it) - Add clear warnings for non-convergence with fallback to bisection - Detect 2-cycles explicitly as sign of ill-conditioning * Now uses ITP from Roots.jl for Newton quantile fallback * Refactor quantile algorithms to use Roots.jl Delegates core root-finding logic in quantile_newton functions to Roots.find_zero using Newton(). Upgrades quantile_bisect to use the ITP() bracketing solver as suggested. Retains precision-based xrtol tolerance scaling. * - `quantile_bisect` now uses isapprox to check whether lx and rx are equal - the `chernoff` distribution now uses `quantile_newton` for none precomputed quantiles - Added test cases for the following issues (#1571, #1611, #1807, #1869, #1999, #2061) * Fix quantile bisection convergence for collapsed brackets * Handle equal quantile bisection endpoints * Use Roots defaults with a bracketing fallback for quantile root-finding Building on #2066, simplify the quantile solvers to rely on Roots' default tolerances instead of hand-tuned options: - `quantile_bisect` delegates to `find_zero(g, (lx, rx), ITP())`. ITP's relative `xrtol = eps` converges even for brackets far from zero, where the previous absolute `cbrt(eps)^2` tolerance fell below the floating-point spacing and looped forever (#1611, #1807). - The four `_newton` functions delegate to `find_zero((F, f), x, Newton(), ITP())`. Roots switches to the ITP bracketing method as soon as Newton brackets the root, which resolves the oscillation/stalling seen for extreme quantiles (#1571, #1898, #2061). The `tol`/`max_iters` parameters and the explicit tolerance floors are dropped: Roots' Newton defaults combine an absolute (`xatol = eps`) and a relative (`xrtol = eps`) x-tolerance, so they converge both near `p ≈ 1` and for roots near zero without bespoke floors. `quantile_bisect` keeps only the exact `rx == lx` degenerate guard (#1501); the approximate "collapsed bracket" handling is dropped, as a non-bracketing interval is a separate concern from the solver choice. Tests use `isapprox`'s type-appropriate defaults rather than fixed tolerances, and are split one regression per testset. Adds coverage for #1898 and a `Float32` round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Bump version from 0.25.127 to 0.25.128 --------- Co-authored-by: cedri <desinorcedric3@gmail.com> Co-authored-by: Cedriq1astaken <113813505+Cedriq1astaken@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Completed by #2070 |
close #2061
Issue
The issue described by jherekhealy occurs in the
quantile_newtonmethod when dealing with extreme value near 0 or 1 (more often near 1). It fails to converge to a value due to floating point error and the default tolerance being too small resulting in either an infinite loop as it oscillates between values, or converging very slowly.Fix
I modified
quantile_newton,cquantile_newton,invlogcdf_newton,invlogccdf_newtonby adding:maxiter(which by default = 10_000), which defines a maximum iteration before it falls backWhen either cases occur,
quantile_newtonfalls back toquantile_bisectTest
I wrote a Monte Carlo simulation to test extreme values near 1, [0.9999; 0.999999999999]
Here are the results for 1,000,000 samples at
tol = 1e-12:The test shows that the fallback correctly detects the 2-cycles and converges successfully every time instead of infinitely looping.
AI Assistance Disclosure
The debugging strategy, proposed fix, and interpretation of the numerical issue were developed by the author. An AI coding tool was used to help draft and refine parts of the implementation, tests, and PR text. All AI-assisted changes were reviewed, edited, and validated by the author before submission.