Repository: scipy/scipy

Issue:
Intermittent failures for signal.slepian on Windows
I'm getting intermittent failures on the `test_windows.test_windowfunc_basics` test function on Windows 32-bit.

[Christoph Gohlke](http://mail.scipy.org/pipermail/scipy-dev/2013-August/019118.html) seems to have run into the same thing.

David C said he had also seen these failures when building Enthought installers.

```
======================================================================
FAIL: test_windows.test_windowfunc_basics
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python34\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "C:\Python34\lib\site-packages\scipy\signal\tests\test_windows.py",
line 100, in test_windowfunc_basics
    assert_array_almost_equal(w1, w2)
  File "C:\Python34\lib\site-packages\numpy\testing\utils.py", line
811, in assert_array_almost_equal
    header=('Arrays are not almost equal to %d decimals' % decimal))
  File "C:\Python34\lib\site-packages\numpy\testing\utils.py", line
644, in assert_array_compare
    raise AssertionError(msg)
AssertionError:
Arrays are not almost equal to 6 decimals

(mismatch 100.0%)
 x: array([ 0.1892781 ,  1.        ,  0.30368426,  0.30368426,  0.06227148,
        0.18297787,  0.30368426])
 y: array([ 1.        ,  0.79697112,  0.51113591,  0.00201155,  0.28611295,
        0.4936433 ,  0.00201155])
```

The failures are for the `slepian` window function, and are due to random variation in the return values from `scipy.linalg.eig`.   This script demonstrates the problem (copied / adapted from `signal/windows.py`):

```
from __future__ import division, print_function, absolute_import

import numpy as np
from scipy import special, linalg

np.set_printoptions(precision=3, suppress=True)

niters = 10

# Get parameters
M = 7
width = 2
sym = True

odd = M % 2
if not sym and not odd:
    M = M + 1

twoF = width / 2.0
alpha = (M - 1) / 2.0
m = np.arange(0, M) - alpha
n = m[:, np.newaxis]
k = m[np.newaxis, :]
AF = twoF * special.sinc(twoF * (n - k))

# Loop over iterations
for i in range(niters):
    [lam, vec] = linalg.eig(AF)
    ind = np.argmax(abs(lam), axis=-1)
    w = np.abs(vec[:, ind])
    print(vec[:, ind])
```

This gives output like this:

```
In [76]: run check_slepian.py
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.163+0.j -0.861+0.j  0.261+0.j -0.261+0.j  0.054+0.j -0.157+0.j
  0.261+0.j]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.163+0.j -0.861+0.j  0.261+0.j -0.261+0.j  0.054+0.j -0.157+0.j
  0.261+0.j]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
[-0.671  0.535  0.343  0.001  0.192 -0.331 -0.001]
```

Notice that `vec` returned from `linalg.eig` has changed from run to run, without change in the input.



Discussion:
I get a 2-1 pattern with eigh

```
[-0.277 -0.127 -0.127 -0.829 -0.334 -0.251  0.173]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
[-0.277 -0.127 -0.127 -0.829 -0.334 -0.251  0.173]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
[-0.277 -0.127 -0.127 -0.829 -0.334 -0.251  0.173]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
[-0.277 -0.127 -0.127 -0.829 -0.334 -0.251  0.173]
[ 0.001  0.189 -0.096 -0.814 -0.526 -0.08  -0.097]
```

But all eigenvalues are 1 +/- numerical noise, it uses argmax of noise
and `linalg` is not deterministic   (known at least for a few years)

Nondeterminism comes from random stack and heap alignment, combined with optimizing compilers. Expected, and not a bug. The test probably needs fixing as it's probably doing something numerically unstable.

There is an [alternative calculation in nitime](https://github.com/nipy/nitime/blob/master/nitime/algorithms/spectral.py#L371) with a reference which we should probably switch to using.  Although I haven't tried it out.


Resolving patch:
diff --git a/scipy/signal/windows.py b/scipy/signal/windows.py
--- a/scipy/signal/windows.py
+++ b/scipy/signal/windows.py
@@ -1298,9 +1298,6 @@ def slepian(M, width, sym=True):
     >>> plt.xlabel("Normalized frequency [cycles per sample]")
 
     """
-    if (M * width > 27.38):
-        raise ValueError("Cannot reliably obtain Slepian sequences for"
-                         " M*width > 27.38.")
     if M < 1:
         return np.array([])
     if M == 1:
@@ -1309,20 +1306,21 @@ def slepian(M, width, sym=True):
     if not sym and not odd:
         M = M + 1
 
-    twoF = width / 2.0
-    alpha = (M - 1) / 2.0
-    m = np.arange(0, M) - alpha
-    n = m[:, np.newaxis]
-    k = m[np.newaxis, :]
-    AF = twoF * special.sinc(twoF * (n - k))
-    [lam, vec] = linalg.eig(AF)
-    ind = np.argmax(abs(lam), axis=-1)
-    w = np.abs(vec[:, ind])
-    w = w / max(w)
+    # our width is the full bandwidth
+    width = width / 2
+    # to match the old version 
+    width = width / 2
+    m = np.arange(M, dtype='d')
+    H = np.zeros((2, M))
+    H[0, 1:] = m[1:] * (M - m[1:]) / 2
+    H[1, :] = ((M - 1 - 2 * m) / 2)**2 * np.cos(2 * np.pi * width)
+
+    _, win = linalg.eig_banded(H, select='i', select_range=(M-1, M-1))
+    win = win.ravel() / win.max()
 
     if not sym and not odd:
-        w = w[:-1]
-    return w
+        win = win[:-1]
+    return win
 
 
 def cosine(M, sym=True):


Test patch: