426 lines
8.6 KiB
C++
426 lines
8.6 KiB
C++
#ifndef NUMEXPR_COMPLEX_FUNCTIONS_HPP
|
|
#define NUMEXPR_COMPLEX_FUNCTIONS_HPP
|
|
|
|
/*********************************************************************
|
|
Numexpr - Fast numerical array expression evaluator for NumPy.
|
|
|
|
License: MIT
|
|
Author: See AUTHORS.txt
|
|
|
|
See LICENSE.txt for details about copyright and rights to use.
|
|
**********************************************************************/
|
|
|
|
// TODO: Could just use std::complex<float> and std::complex<double>
|
|
|
|
/* constants */
|
|
static npy_cdouble nc_1 = {1., 0.};
|
|
static npy_cdouble nc_half = {0.5, 0.};
|
|
static npy_cdouble nc_i = {0., 1.};
|
|
static npy_cdouble nc_i2 = {0., 0.5};
|
|
/*
|
|
static npy_cdouble nc_mi = {0., -1.};
|
|
static npy_cdouble nc_pi2 = {M_PI/2., 0.};
|
|
*/
|
|
|
|
/* *************************** WARNING *****************************
|
|
Due to the way Numexpr places the results of operations, the *x and *r
|
|
pointers do point to the same address (apparently this doesn't happen
|
|
in NumPy). So, measures should be taken so as to not to reuse *x
|
|
after the first *r has been overwritten.
|
|
*********************************************************************
|
|
*/
|
|
|
|
static void
|
|
nc_assign(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
r->real = x->real;
|
|
r->imag = x->imag;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_sum(npy_cdouble *a, npy_cdouble *b, npy_cdouble *r)
|
|
{
|
|
r->real = a->real + b->real;
|
|
r->imag = a->imag + b->imag;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_diff(npy_cdouble *a, npy_cdouble *b, npy_cdouble *r)
|
|
{
|
|
r->real = a->real - b->real;
|
|
r->imag = a->imag - b->imag;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_neg(npy_cdouble *a, npy_cdouble *r)
|
|
{
|
|
r->real = -a->real;
|
|
r->imag = -a->imag;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_conj(npy_cdouble *a, npy_cdouble *r)
|
|
{
|
|
r->real = a->real;
|
|
r->imag = -a->imag;
|
|
return;
|
|
}
|
|
|
|
// Needed for allowing the internal casting in numexpr machinery for
|
|
// conjugate operations
|
|
inline float fconjf(float x)
|
|
{
|
|
return x;
|
|
}
|
|
|
|
// Needed for allowing the internal casting in numexpr machinery for
|
|
// conjugate operations
|
|
inline double fconj(double x)
|
|
{
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
nc_prod(npy_cdouble *a, npy_cdouble *b, npy_cdouble *r)
|
|
{
|
|
double ar=a->real, br=b->real, ai=a->imag, bi=b->imag;
|
|
r->real = ar*br - ai*bi;
|
|
r->imag = ar*bi + ai*br;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_quot(npy_cdouble *a, npy_cdouble *b, npy_cdouble *r)
|
|
{
|
|
double ar=a->real, br=b->real, ai=a->imag, bi=b->imag;
|
|
double d = br*br + bi*bi;
|
|
r->real = (ar*br + ai*bi)/d;
|
|
r->imag = (ai*br - ar*bi)/d;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_sqrt(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double s,d;
|
|
if (x->real == 0. && x->imag == 0.)
|
|
*r = *x;
|
|
else {
|
|
s = sqrt((fabs(x->real) + hypot(x->real,x->imag))/2);
|
|
d = x->imag/(2*s);
|
|
if (x->real > 0.) {
|
|
r->real = s;
|
|
r->imag = d;
|
|
}
|
|
else if (x->imag >= 0.) {
|
|
r->real = d;
|
|
r->imag = s;
|
|
}
|
|
else {
|
|
r->real = -d;
|
|
r->imag = -s;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_log(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double l = hypot(x->real,x->imag);
|
|
r->imag = atan2(x->imag, x->real);
|
|
r->real = log(l);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_log1p(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double l = hypot(x->real + 1.0,x->imag);
|
|
r->imag = atan2(x->imag, x->real + 1.0);
|
|
r->real = log(l);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_exp(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double a = exp(x->real);
|
|
r->real = a*cos(x->imag);
|
|
r->imag = a*sin(x->imag);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_expm1(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double a = exp(x->real);
|
|
r->real = a*cos(x->imag) - 1.0;
|
|
r->imag = a*sin(x->imag);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_pow(npy_cdouble *a, npy_cdouble *b, npy_cdouble *r)
|
|
{
|
|
npy_intp n;
|
|
double ar=a->real, br=b->real, ai=a->imag, bi=b->imag;
|
|
|
|
if (br == 0. && bi == 0.) {
|
|
r->real = 1.;
|
|
r->imag = 0.;
|
|
return;
|
|
}
|
|
if (ar == 0. && ai == 0.) {
|
|
r->real = 0.;
|
|
r->imag = 0.;
|
|
return;
|
|
}
|
|
if (bi == 0 && (n=(npy_intp)br) == br) {
|
|
if (n > -100 && n < 100) {
|
|
npy_cdouble p, aa;
|
|
npy_intp mask = 1;
|
|
if (n < 0) n = -n;
|
|
aa = nc_1;
|
|
p.real = ar; p.imag = ai;
|
|
while (1) {
|
|
if (n & mask)
|
|
nc_prod(&aa,&p,&aa);
|
|
mask <<= 1;
|
|
if (n < mask || mask <= 0) break;
|
|
nc_prod(&p,&p,&p);
|
|
}
|
|
r->real = aa.real; r->imag = aa.imag;
|
|
if (br < 0) nc_quot(&nc_1, r, r);
|
|
return;
|
|
}
|
|
}
|
|
/* complexobject.c uses an inline version of this formula
|
|
investigate whether this had better performance or accuracy */
|
|
nc_log(a, r);
|
|
nc_prod(r, b, r);
|
|
nc_exp(r, r);
|
|
return;
|
|
}
|
|
|
|
|
|
static void
|
|
nc_prodi(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double xr = x->real;
|
|
r->real = -x->imag;
|
|
r->imag = xr;
|
|
return;
|
|
}
|
|
|
|
|
|
static void
|
|
nc_acos(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble a, *pa=&a;
|
|
|
|
nc_assign(x, pa);
|
|
nc_prod(x,x,r);
|
|
nc_diff(&nc_1, r, r);
|
|
nc_sqrt(r, r);
|
|
nc_prodi(r, r);
|
|
nc_sum(pa, r, r);
|
|
nc_log(r, r);
|
|
nc_prodi(r, r);
|
|
nc_neg(r, r);
|
|
return;
|
|
/* return nc_neg(nc_prodi(nc_log(nc_sum(x,nc_prod(nc_i,
|
|
nc_sqrt(nc_diff(nc_1,nc_prod(x,x))))))));
|
|
*/
|
|
}
|
|
|
|
static void
|
|
nc_acosh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble t, a, *pa=&a;
|
|
|
|
nc_assign(x, pa);
|
|
nc_sum(x, &nc_1, &t);
|
|
nc_sqrt(&t, &t);
|
|
nc_diff(x, &nc_1, r);
|
|
nc_sqrt(r, r);
|
|
nc_prod(&t, r, r);
|
|
nc_sum(pa, r, r);
|
|
nc_log(r, r);
|
|
return;
|
|
/*
|
|
return nc_log(nc_sum(x,
|
|
nc_prod(nc_sqrt(nc_sum(x,nc_1)), nc_sqrt(nc_diff(x,nc_1)))));
|
|
*/
|
|
}
|
|
|
|
static void
|
|
nc_asin(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble a, *pa=&a;
|
|
nc_prodi(x, pa);
|
|
nc_prod(x, x, r);
|
|
nc_diff(&nc_1, r, r);
|
|
nc_sqrt(r, r);
|
|
nc_sum(pa, r, r);
|
|
nc_log(r, r);
|
|
nc_prodi(r, r);
|
|
nc_neg(r, r);
|
|
return;
|
|
/*
|
|
return nc_neg(nc_prodi(nc_log(nc_sum(nc_prod(nc_i,x),
|
|
nc_sqrt(nc_diff(nc_1,nc_prod(x,x)))))));
|
|
*/
|
|
}
|
|
|
|
|
|
static void
|
|
nc_asinh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble a, *pa=&a;
|
|
nc_assign(x, pa);
|
|
nc_prod(x, x, r);
|
|
nc_sum(&nc_1, r, r);
|
|
nc_sqrt(r, r);
|
|
nc_sum(r, pa, r);
|
|
nc_log(r, r);
|
|
return;
|
|
/*
|
|
return nc_log(nc_sum(nc_sqrt(nc_sum(nc_1,nc_prod(x,x))),x));
|
|
*/
|
|
}
|
|
|
|
static void
|
|
nc_atan(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble a, *pa=&a;
|
|
nc_diff(&nc_i, x, pa);
|
|
nc_sum(&nc_i, x, r);
|
|
nc_quot(r, pa, r);
|
|
nc_log(r,r);
|
|
nc_prod(&nc_i2, r, r);
|
|
return;
|
|
/*
|
|
return nc_prod(nc_i2,nc_log(nc_quot(nc_sum(nc_i,x),nc_diff(nc_i,x))));
|
|
*/
|
|
}
|
|
|
|
static void
|
|
nc_atanh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
npy_cdouble a, b, *pa=&a, *pb=&b;
|
|
nc_assign(x, pa);
|
|
nc_diff(&nc_1, pa, r);
|
|
nc_sum(&nc_1, pa, pb);
|
|
nc_quot(pb, r, r);
|
|
nc_log(r, r);
|
|
nc_prod(&nc_half, r, r);
|
|
return;
|
|
/*
|
|
return nc_prod(nc_half,nc_log(nc_quot(nc_sum(nc_1,x),nc_diff(nc_1,x))));
|
|
*/
|
|
}
|
|
|
|
static void
|
|
nc_cos(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double xr=x->real, xi=x->imag;
|
|
r->real = cos(xr)*cosh(xi);
|
|
r->imag = -sin(xr)*sinh(xi);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_cosh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double xr=x->real, xi=x->imag;
|
|
r->real = cos(xi)*cosh(xr);
|
|
r->imag = sin(xi)*sinh(xr);
|
|
return;
|
|
}
|
|
|
|
|
|
#define M_LOG10_E 0.434294481903251827651128918916605082294397
|
|
|
|
static void
|
|
nc_log10(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
nc_log(x, r);
|
|
r->real *= M_LOG10_E;
|
|
r->imag *= M_LOG10_E;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_sin(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double xr=x->real, xi=x->imag;
|
|
r->real = sin(xr)*cosh(xi);
|
|
r->imag = cos(xr)*sinh(xi);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_sinh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double xr=x->real, xi=x->imag;
|
|
r->real = cos(xi)*sinh(xr);
|
|
r->imag = sin(xi)*cosh(xr);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_tan(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double sr,cr,shi,chi;
|
|
double rs,is,rc,ic;
|
|
double d;
|
|
double xr=x->real, xi=x->imag;
|
|
sr = sin(xr);
|
|
cr = cos(xr);
|
|
shi = sinh(xi);
|
|
chi = cosh(xi);
|
|
rs = sr*chi;
|
|
is = cr*shi;
|
|
rc = cr*chi;
|
|
ic = -sr*shi;
|
|
d = rc*rc + ic*ic;
|
|
r->real = (rs*rc+is*ic)/d;
|
|
r->imag = (is*rc-rs*ic)/d;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_tanh(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
double si,ci,shr,chr;
|
|
double rs,is,rc,ic;
|
|
double d;
|
|
double xr=x->real, xi=x->imag;
|
|
si = sin(xi);
|
|
ci = cos(xi);
|
|
shr = sinh(xr);
|
|
chr = cosh(xr);
|
|
rs = ci*shr;
|
|
is = si*chr;
|
|
rc = ci*chr;
|
|
ic = si*shr;
|
|
d = rc*rc + ic*ic;
|
|
r->real = (rs*rc+is*ic)/d;
|
|
r->imag = (is*rc-rs*ic)/d;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
nc_abs(npy_cdouble *x, npy_cdouble *r)
|
|
{
|
|
r->real = sqrt(x->real*x->real + x->imag*x->imag);
|
|
r->imag = 0;
|
|
}
|
|
|
|
#endif // NUMEXPR_COMPLEX_FUNCTIONS_HPP
|