Skip to content

Commit cd55a14

Browse files
authored
Merge pull request #8 from steppi/unholy-surgery
ENH: Add Faddeeva and SciPy special's remaining exp and log functions from
2 parents b75ec5a + c748963 commit cd55a14

File tree

5 files changed

+2124
-0
lines changed

5 files changed

+2124
-0
lines changed

include/xsf/erf.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#pragma once
2+
3+
#include "faddeeva.h"
4+
#include "cephes/ndtr.h"
5+
#include "config.h"
6+
7+
namespace xsf {
8+
9+
inline double erf(double x) { return cephes::erf(x); }
10+
11+
inline float erf(float x) { return erf(static_cast<double>(x)); }
12+
13+
inline std::complex<double> erf(std::complex<double> z) { return Faddeeva::erf(z); }
14+
15+
inline std::complex<float> erf(std::complex<float> x) {
16+
return static_cast<std::complex<float>>(erf(static_cast<std::complex<double>>(x)));
17+
}
18+
19+
inline double erfc(double x) { return cephes::erfc(x); }
20+
21+
inline float erfc(float x) { return erfc(static_cast<double>(x)); }
22+
23+
inline std::complex<double> erfc(std::complex<double> z) { return Faddeeva::erfc(z); }
24+
25+
inline std::complex<float> erfc(std::complex<float> x) {
26+
return static_cast<std::complex<float>>(erfc(static_cast<std::complex<double>>(x)));
27+
}
28+
29+
inline double erfcx(double x) { return Faddeeva::erfcx(x); }
30+
31+
inline float erfcx(float x) { return erfcx(static_cast<double>(x)); }
32+
33+
inline std::complex<double> erfcx(std::complex<double> z) { return Faddeeva::erfcx(z); }
34+
35+
inline std::complex<float> erfcx(std::complex<float> x) {
36+
return static_cast<std::complex<float>>(erfcx(static_cast<std::complex<double>>(x)));
37+
}
38+
39+
inline double erfi(double x) { return Faddeeva::erfi(x); }
40+
41+
inline float erfi(float x) { return erfi(static_cast<double>(x)); }
42+
43+
inline std::complex<double> erfi(std::complex<double> z) { return Faddeeva::erfi(z); }
44+
45+
inline std::complex<float> erfi(std::complex<float> z) {
46+
return static_cast<std::complex<float>>(erfi(static_cast<std::complex<double>>(z)));
47+
}
48+
49+
inline double voigt_profile(double x, double sigma, double gamma) {
50+
const double INV_SQRT_2 = 0.707106781186547524401;
51+
const double SQRT_2PI = 2.5066282746310002416123552393401042;
52+
53+
if (sigma == 0) {
54+
if (gamma == 0) {
55+
if (std::isnan(x))
56+
return x;
57+
if (x == 0)
58+
return INFINITY;
59+
return 0;
60+
}
61+
return gamma / M_PI / (x * x + gamma * gamma);
62+
}
63+
if (gamma == 0) {
64+
return 1 / SQRT_2PI / sigma * exp(-(x / sigma) * (x / sigma) / 2);
65+
}
66+
67+
double zreal = x / sigma * INV_SQRT_2;
68+
double zimag = gamma / sigma * INV_SQRT_2;
69+
std::complex<double> z(zreal, zimag);
70+
std::complex<double> w = Faddeeva::w(z);
71+
return w.real() / sigma / SQRT_2PI;
72+
}
73+
74+
inline float voigt_profile(float x, float sigma, float gamma) {
75+
return voigt_profile(static_cast<double>(x), static_cast<double>(sigma), static_cast<double>(gamma));
76+
}
77+
78+
inline std::complex<double> wofz(std::complex<double> z) { return Faddeeva::w(z); }
79+
80+
inline std::complex<float> wofz(std::complex<float> x) {
81+
return static_cast<std::complex<float>>(wofz(static_cast<std::complex<double>>(x)));
82+
}
83+
84+
inline double dawsn(double x) { return Faddeeva::Dawson(x); }
85+
86+
inline float dawsn(float x) { return dawsn(static_cast<double>(x)); }
87+
88+
inline std::complex<double> dawsn(std::complex<double> z) { return Faddeeva::Dawson(z); }
89+
90+
inline std::complex<float> dawsn(std::complex<float> x) {
91+
return static_cast<std::complex<float>>(dawsn(static_cast<std::complex<double>>(x)));
92+
}
93+
94+
} // namespace xsf

include/xsf/exp.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include "xsf/cephes/exp10.h"
4+
#include "xsf/cephes/exp2.h"
5+
6+
namespace xsf {
7+
8+
inline double expm1(double x) { return cephes::expm1(x); }
9+
10+
inline float expm1(float x) { return expm1(static_cast<double>(x)); }
11+
12+
// cexpm1(z) = cexp(z) - 1
13+
//
14+
// The imaginary part of this is easily computed via exp(z.real)*sin(z.imag)
15+
// The real part is difficult to compute when there is cancellation e.g. when
16+
// z.real = -log(cos(z.imag)). There isn't a way around this problem that
17+
// doesn't involve computing exp(z.real) and/or cos(z.imag) to higher
18+
// precision.
19+
inline std::complex<double> expm1(std::complex<double> z) {
20+
if (!std::isfinite(std::real(z)) || !std::isfinite(std::imag(z))) {
21+
return std::exp(z) - 1.0;
22+
}
23+
24+
double x;
25+
double ezr = 0;
26+
if (std::real(z) <= -40) {
27+
x = -1.0;
28+
} else {
29+
ezr = expm1(std::real(z));
30+
x = ezr * std::cos(std::imag(z)) + cosm1(std::imag(z));
31+
}
32+
33+
// don't compute exp(zr) too, unless necessary
34+
double y;
35+
if (std::real(z) > -1.0) {
36+
y = (ezr + 1.0) * sin(std::imag(z));
37+
} else {
38+
y = exp(std::real(z)) * sin(std::imag(z));
39+
}
40+
41+
return std::complex<double>{x, y};
42+
}
43+
44+
inline std::complex<float> expm1(std::complex<float> z) {
45+
return static_cast<std::complex<float>>(expm1(static_cast<std::complex<double>>(z)));
46+
}
47+
48+
double exp2(double x) { return cephes::exp2(x); }
49+
50+
float exp2(float x) { return exp2(static_cast<double>(x)); }
51+
52+
double exp10(double x) { return cephes::exp10(x); }
53+
54+
float exp10(float x) { return exp10(static_cast<double>(x)); }
55+
56+
} // namespace xsf

0 commit comments

Comments
 (0)