Algorithm
Problem class
DECUHR targets integrals of the form
where
where
Domain convention
The singularity must be located at the lower-left vertex of the integration domain, i.e. at
Strategy
The algorithm combines three ideas:
Adaptive subdivision (DEADHR / DESBHR). The domain is maintained as a pool of subregions stored in a binary max-heap keyed on the local error estimate. At each step the worst-error region is subdivided:
Singular region (touching the singularity corner): cut into
singul + 1children by halving each singular dimension in turn.Regular region: bisect along the axis with the largest fourth difference.
Richardson extrapolation (DEXTHR). Each time the singular region is subdivided, a new term
of a geometric series is appended. The tableau is extrapolated to accelerate convergence from to machine precision. Automatic singularity estimation (DECALP). When
alpha ≤ -singul(the default), the exponent is estimated by evaluating the integrand along a ray toward the singular vertex and extrapolating the log-ratio sequence with Aitken / Bjorstad–Grosse–Dahlquist acceleration.
Integration rules
Four Genz–Malik-style fully-symmetric rules are available, selected via key:
key | Rule | Dimensions | Points per region |
|---|---|---|---|
1 | Degree-13 | 2D only | 65 |
2 | Degree-11 | 3D only | 127 |
3 | Degree-9 | any nD | |
4 | Degree-7 | any nD | |
0 | Auto | — | key=1 if n=2, key=2 if n=3, else key=3 |
Result, error estimate and diagnostics
DECUHR's error estimator is deliberately conservative: following the original Fortran DEXTHR, the pure extrapolation error is inflated by a heuristic constant before being compared with the tolerance. A returned retcode = MaxIters therefore frequently accompanies a result that is in fact accurate to better than the requested tolerance — the estimate simply never dropped below it within the evaluation budget.
Practical guidance:
Trust
sol.u; readsol.residfor the (conservative) error estimate.Raise
maxitersand/orwrksubto let the estimate catch up to the true accuracy if a certifiedSuccessis required.sol.stats.numevalsreports the number of integrand evaluations andsol.stats.ifailthe raw DECUHR code — handy to confirm you hit the budget rather than a genuine problem.
Generic value type and automatic differentiation
All internal arrays that accumulate integrand values are parameterized on a value type TV, inferred at runtime from a test evaluation:
TV = typeof(f(midpoint, p)) # e.g. Float64 or ForwardDiff.Dual{...}This means the algorithm works out of the box with dual numbers when differentiating through the integral with respect to parameters p:
using ForwardDiff, Integrals, DECUHR
f = (u, p) -> exp(-p[1] * (u[1]^2 + u[2]^2))
prob = IntegralProblem(f, (zeros(2), ones(2)))
# Derivative of the integral w.r.t. p[1] at p₀ = [1.0]
dI_dp = ForwardDiff.gradient(p -> solve(prob, DecuhrAlgorithm(alpha=0.0);
abstol=1e-8).u, [1.0])Alpha auto-estimation and AD
Automatic estimation of alpha ≤ -singul) runs in Float64 on the primal integrand — solve therefore works with or without an explicit alpha:
DecuhrAlgorithm(singul=2, alpha=-0.5) # explicit α
DecuhrAlgorithm(singul=2) # α auto-estimated — also differentiableError codes (ifail)
The low-level driver returns an integer ifail:
| Code | Meaning |
|---|---|
0 | Success — all tolerances satisfied |
1 | Budget (maxiters) exhausted |
2–13 | Invalid input parameters (key, ndim, numfun, bounds, budget, tolerances, singul, logf, minpts, emax, wrksub) |
14 | Integral may not converge ( |
15 | emax < 1 |
17 | wrksub too small for the given budget |
These are mapped to SciMLBase.ReturnCode values by the Integrals.jl integration hook: ifail=0 → Success, ifail=1 → MaxIters, otherwise → Failure.