question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

expm1 low accuracy for complex argument

See original GitHub issue

expm1(1j*x) has the same (low) accuracy for small complex arguments as exp(1j*x) - 1. For real arguments, the accuracy is as expected and on par with numpy: image

Code to reproduce:
import matplotlib.pyplot as plt
import numpy as np
import numexpr as ne

x = np.geomspace(1e-18, 1e-6, 101)

numpy = {}
numpy_naive = {}
numexpr = {}

numexpr['float'] = ne.evaluate('expm1(x)')
numpy['float'] = np.expm1(x)
numpy_naive['float'] = np.exp(x) - 1

numexpr['complex'] = ne.evaluate('expm1(1j*x)')
numpy['complex'] = np.expm1(1j*x)
numpy_naive['complex'] = np.exp(1j*x) - 1

fig, ax = plt.subplots(2, 2, sharex=True, constrained_layout=True)
ax[0, 0].loglog(x, numpy['float'].real, label='numpy')
ax[0, 0].loglog(x, numpy_naive['float'].real, '-.', label='numpy naive')
ax[0, 0].loglog(x, numexpr['float'].real, '--', label='numexpr')
ax[1, 0].loglog(x, abs((numpy['float'] - numexpr['float']).real), label='|np - ne| (expm1)')
ax[0, 0].legend()
ax[1, 0].legend()
ax[0, 0].grid(True)
ax[1, 0].grid(True)

ax[0, 1].loglog(x, -numpy['complex'].real, label='numpy')
ax[0, 1].loglog(x, -numpy_naive['complex'].real, '-.', label='numpy naive')
ax[0, 1].loglog(x, -numexpr['complex'].real, '--', label='numexpr')
ax[1, 1].loglog(x, abs((numpy['complex'] - numexpr['complex']).real), label='|np - ne| (expm1)')
ax[0, 1].legend()
ax[1, 1].legend()
ax[0, 1].grid(True)
ax[1, 1].grid(True)

ax[0, 0].set_title('expm1(x)')
ax[0, 1].set_title('-real(expm1(1j*x))')
ax[1, 0].set_yscale('linear')
ax[1, 0].set_xlabel('x')
ax[1, 1].set_xlabel('x')

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:8 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
robbmcleodcommented, Oct 26, 2022

Ok, sorry for the long delay but frustrated with the obtuse implementation of NumExpr. I eventually gave up and just overwrote the complex exp function to do a comparison. Your function is 25 % slower, but it is indeed significantly more accurate for the real component over a wide range of values. Therefore I have merged your changes into master. I’ll prepare a new release immediately since Python 3.11 is out.

0reactions
thangleitercommented, Sep 11, 2022

@thangleiter so I have not been able to replicate your problem. I made a new branch and pushed changes to it so I can test both functions simultaneously. Here’s my test script:

https://github.com/pydata/numexpr/blob/issue418/issues/issue418.py

As you can see I simultaneously have both versions of the function implemented. Would you look at it any see if I am doing anything wrong in my evaluation?

The implementation of expm1x looks good to me. However, I am noticing some really strange behavior when testing with both expm1 and expm1x. Somehow, evaluate('expm1x(z)') gives different results depending on the implementation of nc_expm1. I.e., if the latter is implemented as

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;
}

the real part of expm1x is inaccurate, whereas if it is implemented as

static void
nc_expm1(npy_cdouble *x, npy_cdouble *r)
{
    double a = sin(x->imag / 2);
    double b = exp(x->real);
    r->real = expm1(x->real) * cos(x->imag) - 2 * a * a;
    r->imag = b * sin(x->imag);
    return;
}

everything is accurate compared to np.expm1. So it looks like evaluate('expm1x(z)') actually uses nc_expm1.

When you found problems with NumExpr in the initial issue report, were you using a NumExpr compiled with Intel MKL support? Either from conda or Gohkle’s repo?

I am using NumExpr from conda but don’t use MKL:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version:   2.8.4dev1
NumPy version:     1.23.1
Python version:    3.10.4 | packaged by conda-forge | (main, Mar 30 2022, 08:38:02) [MSC v.1916 64 bit (AMD64)]
Platform:          win32-AMD64-10.0.22000
CPU vendor:        AuthenticAMD
CPU model:         AMD Ryzen 7 PRO 4750U with Radeon Graphics
CPU clock speed:   1697 MHz
VML available?     False
Number of threads used by default: 8 (out of 16 detected cores)
Maximum number of threads: 64
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

@thangleiter also which compiler are you using? We can check with Godbolt if there’s some compiler optimization doing something strange.

I tested this on both MSVC143 and g++.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Computation of expm1(x) = exp(x) − 1
The exponential function therefore cannot be computed accurately for large x without re- sorting to higher precision, but for small arguments, ...
Read more >
Fixed-Accuracy Arithmetic Functions
This chapter describes Intel® IPP fixed-accuracy transcendental mathematical real and complex functions of vector arguments. These functions take an input ...
Read more >
Numerical instability for expm1 with complex arguments #14019
The output of np.expm1 becomes unstable for small values (around 10^{-15} to 10^{-16}) when the argument is complex.
Read more >
Math library functions that seem unnecessary
This post will give several examples of functions include in the standard C math library that seem unnecessary at first glance.
Read more >
Expm1 - Intel
Vector Mathematics computes elementary functions on vector arguments. It can improve performance for applications.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found