Summary
CauchyDistribution is implemented as a delegation wrapper over StudentTDistribution(ν=1). While mathematically correct, this means getCumulativeProbability evaluates the regularised incomplete-beta function — an expensive iterative algorithm — when the Cauchy CDF has a trivial closed form:
F(x; x₀, γ) = (1/π) · arctan((x − x₀) / γ) + 0.5
One arctan per element, no iteration.
Evidence
From the benchmarks/scipy_comparison.py cross-machine run (v0.3.2, scipy 1.18.0):
| Machine |
SIMD |
Cauchy CDF N=1k |
N=10k |
N=100k |
N=1M |
| Zen4 / AVX-512 (Windows) |
AVX-512 |
0.4× |
0.2× |
0.2× |
0.3× |
| Kaby Lake / AVX2+FMA (macOS) |
AVX2+FMA |
0.8× |
0.9× |
1.0× |
1.1× |
On Kaby Lake the delegation is merely at parity with scipy; on Zen4 (where scipy can exploit AVX-512 vectorised arctan) Cauchy CDF is 3–5× slower than scipy. The gap widens with SIMD width, meaning this will worsen on any wider future architecture.
For comparison, Cauchy PDF and log_pdf are competitive (3–9× faster than scipy at all sizes), confirming the issue is specific to the CDF path.
Root cause
src/cauchy.cpp getCumulativeProbability(double) delegates to StudentTDistribution::getCumulativeProbability, which calls the regularised incomplete-beta path. scipy's cauchy.cdf uses the arctan closed form, which is both faster and simpler.
Proposed fix
Implement getCumulativeProbability directly in CauchyDistribution:
double CauchyDistribution::getCumulativeProbability(double x) const noexcept {
return 0.5 + std::atan((x - x0_) / gamma_) * detail::INV_PI;
}
The batch span overload should call vector_atan (or scalar loop until a SIMD atan primitive is available). This also removes one delegation indirection from every scalar CDF call.
Related
If a vector_atan SIMD primitive is added, the batch CDF path would benefit immediately. The existing dispatch table already includes vector_cos; vector_atan follows the same pattern.
Summary
CauchyDistributionis implemented as a delegation wrapper overStudentTDistribution(ν=1). While mathematically correct, this meansgetCumulativeProbabilityevaluates the regularised incomplete-beta function — an expensive iterative algorithm — when the Cauchy CDF has a trivial closed form:One arctan per element, no iteration.
Evidence
From the
benchmarks/scipy_comparison.pycross-machine run (v0.3.2, scipy 1.18.0):On Kaby Lake the delegation is merely at parity with scipy; on Zen4 (where scipy can exploit AVX-512 vectorised arctan) Cauchy CDF is 3–5× slower than scipy. The gap widens with SIMD width, meaning this will worsen on any wider future architecture.
For comparison, Cauchy PDF and log_pdf are competitive (3–9× faster than scipy at all sizes), confirming the issue is specific to the CDF path.
Root cause
src/cauchy.cppgetCumulativeProbability(double)delegates toStudentTDistribution::getCumulativeProbability, which calls the regularised incomplete-beta path. scipy'scauchy.cdfuses the arctan closed form, which is both faster and simpler.Proposed fix
Implement
getCumulativeProbabilitydirectly inCauchyDistribution:The batch span overload should call
vector_atan(or scalar loop until a SIMD atan primitive is available). This also removes one delegation indirection from every scalar CDF call.Related
If a
vector_atanSIMD primitive is added, the batch CDF path would benefit immediately. The existing dispatch table already includesvector_cos;vector_atanfollows the same pattern.