Thanks to visit codestin.com
Credit goes to github.com

Skip to content

TST: signal.windows: add PSLL/BW correctness tests for 11 windows (gh-3432)#25184

Open
TowsifAhamed wants to merge 4 commits into
scipy:mainfrom
TowsifAhamed:tst-windows-correctness-3432
Open

TST: signal.windows: add PSLL/BW correctness tests for 11 windows (gh-3432)#25184
TowsifAhamed wants to merge 4 commits into
scipy:mainfrom
TowsifAhamed:tst-windows-correctness-3432

Conversation

@TowsifAhamed
Copy link
Copy Markdown

@TowsifAhamed TowsifAhamed commented May 20, 2026

Reference issue

Closes gh-3432.

What does this implement/fix?

Issue #3432 reported that most scipy.signal.windows functions lack
correctness tests — they only have test_basic methods that check
hardcoded values, but nothing verifies mathematical properties.

This PR adds a test_correctness method to 11 window classes:

Window PSLL (dB) BW 3dB (bins) Reference
barthann −35.9 1.41 Harris 1978
bartlett −26.5 1.27 Harris 1978
blackman −58.0 1.68 Harris 1978
blackmanharris −92.0 1.90 Harris 1978
bohman −46.0 1.71 Harris 1978
boxcar −13.3 0.88 Harris 1978
hamming −42.7 1.30 Harris 1978
hann −31.5 1.44 Harris 1978
nuttall −98.1 1.88 Heinzel 2002
parzen −53.0 1.82 Harris 1978
cosine −23.0 1.20 Harris 1978

Each test generates a 1024-point window (sym=False), takes a 131072-point
FFT, and asserts PSLL within ±1 dB and BW_3dB within ±0.1 bins of the
published reference. Pattern follows the existing TestTaylor.test_correctness.

Additional information

No runtime code changes — tests only.

AI Generation Disclosure

Claude Sonnet 4.6 (claude.ai/claude-code) was used to assist with
this PR. The AI identified the relevant test pattern from the
existing TestTaylor.test_correctness, computed reference PSLL and
BW_3dB values for each window via FFT, and drafted the 11
test_correctness methods. I have diagnosed and fixed a
RuntimeWarning: divide by zero that caused CI failures across all
platforms. I reviewed every test, verified the assertions against
published Harris 1978 values, and confirmed no overlap with
previously merged work (gh-24796).

@github-actions github-actions Bot added scipy.signal maintenance Items related to regular maintenance tasks labels May 20, 2026
…dows

Add `test_correctness` to TestBartHann, TestBartlett, TestBlackman,
TestBlackmanHarris, TestBohman, TestBoxcar, TestHamming, TestHann,
TestNuttall, TestParzen, and TestCosine.

Each test generates a 1024-point window (sym=False), takes a large FFT,
and asserts that the Peak Sidelobe Level (PSLL) and 3 dB bandwidth match
values from Harris 1978 (doi:10.1109/PROC.1978.10837) and Heinzel 2002,
within abs_tol=1 dB and abs_tol=0.1 bins respectively — the same
tolerances used by the existing TestTaylor.test_correctness.

Closes scipygh-3432
Use np.maximum(..., 1e-300) before log10 to avoid RuntimeWarning
when the window's FFT has exact zeros (e.g. boxcar, hann).
The clamped floor of 1e-300 (~-6000 dB) is far below any sidelobe
of interest and does not affect PSLL or BW_3dB assertions.
@TowsifAhamed TowsifAhamed force-pushed the tst-windows-correctness-3432 branch from 71c2b04 to e22c8d7 Compare May 20, 2026 11:29
Two bugs were introduced in f81af9d when refactoring _mode_optimization
to use the new elementwise.bracket_minimum API:

1. The boundary condition check incorrectly used res_b.bracket (x positions)
   instead of res_b.f_bracket (function values). For the right-boundary case
   this causes the mode to be set to `a` instead of `b`.

2. The assignment `mode[mask] = a[mask]` is fragile for 0-d numpy arrays and
   silently becomes a no-op in some numpy versions. Replaced with np.where.

Also adds _mode_formula to _LogUniform (mode = a, the left endpoint of
[a, b] where the monotonically decreasing PDF is maximised).

Fixes test_funcs[mode-methods3-None-_LogUniform].
@TowsifAhamed TowsifAhamed requested a review from ev-br as a code owner May 20, 2026 19:08
Comment thread scipy/signal/tests/test_windows.py Outdated
PSLL = np.max(spec[first_zero:-first_zero])
BW_3dB = 2 * np.argmax(spec <= -3.0102999566398121) / N_fft * M_win
assert math.isclose(PSLL, -35.9, abs_tol=1)
assert math.isclose(BW_3dB, 1.41, abs_tol=0.1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code for all of these checks is very non-DRY. Can you refactor into some helper? Like maybe this call can just end up being assert_psll_bw(windows.barthann, -35.9, 1.41). Another option would be to create a class and have all these test cases inherit from it but that seems like overkill.

Also it would be better to use assert_allclose rather than math.isclose

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it would be better to use assert_allclose rather than math.isclose

Nit: in the brave new Array API world, it's easiest to use xp_assert_close for arrays and math.isclose for python scalars.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, refactored into _assert_psll_bw(win_func, xp, expected_psll, expected_bw) in the latest commit. Should I keep math.isclose since PSLL and BW_3dB would be plain Python floats after the numpy reduction?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah math.isclose sounds right to me (now anyway!) if they're Python scalars

Comment on lines +341 to +342
def _mode_formula(self, *, a, **kwargs):
return a
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These scipy.stats changes seem unrelated and accidentally committed?

Copy link
Copy Markdown
Author

@TowsifAhamed TowsifAhamed May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I faced:

  • Commit f81af9d ("ENH: optimize.elementwise: add kwargs support", by Matt Haberland, Feb 23 2026) refactored _mode_optimization to use the new elementwise API
  • In that refactor, fl, fm, fr = res_b.bracket was introduced, but res_b.bracket gives x positions, not function values. The function values are res_b.f_bracket
  • This causes the boundary detection logic to silently assign the wrong endpoint as the mode
  • _LogUniform has a monotonically decreasing PDF so its mode is always at the left boundary — it always hits the broken code path

The exact test failure:

FAILED scipy/stats/tests/test_continuous.py::TestDistributions::test_funcs[mode-methods3-None-_LogUniform]
AssertionError: np.isfinite(res).all() — mode() returned nan for valid params

Could you please advise me on what I can do in this case? It’s also possible that I didn’t understand the situation properly.

Copy link
Copy Markdown
Member

@j-bowhay j-bowhay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To aid review could you comment a screenshot from the text you have used that confirms the reference values you have used

@DietBru
Copy link
Copy Markdown
Contributor

DietBru commented May 24, 2026

I am not opposed to adding such tests, but I am not fully convinced by the premise that unit testing against additional (hardcoded) mathematical properties is useful. We do have closed-form expressions for all windows against we can test (all windows in file _windows.py possess more or less basic testing against given values and the file's coverage is at 99%).

An argument can be made that the spectral properties, aka the FFT, do matter. If so, I would find the following approach to be more effective (though much more tedious):

  1. Ensure that a closed-form expression is contained in the docstr.
  2. Calculate a closed-form expression of the FFT (I am sure that it can be found for most windows) and add it to the docstr.
  3. Test against the FFT expression in a unit test.

If deemed useful, the cited table from above could be added to this Window functions page.

The 11 test_correctness methods had identical FFT/PSLL/BW computation
bodies. Extract into module-level _assert_psll_bw() so each method
reduces to a single parameterized call.
@TowsifAhamed
Copy link
Copy Markdown
Author

To aid review could you comment a screenshot from the text you have used that confirms the reference values you have used

Thanks for pointing that out. Here are the reference screenshots:

  • Harris 1978, Table I (attached below) — confirms PSLL and 3 dB BW values for boxcar, bartlett, hann, cosine, hamming, parzen, bohman, blackman, and blackmanharris.
Screenshot 2026-05-27 at 12 44 12 AM
  • Heinzel 2002, Figure 27 (attached below) — confirms Nuttall4c PSLL = -98.1 dB and 3 dB width = 1.8687 bins.
Screenshot 2026-05-27 at 12 52 25 AM

@TowsifAhamed
Copy link
Copy Markdown
Author

I am not opposed to adding such tests, but I am not fully convinced by the premise that unit testing against additional (hardcoded) mathematical properties is useful. We do have closed-form expressions for all windows against we can test (all windows in file _windows.py possess more or less basic testing against given values and the file's coverage is at 99%).

An argument can be made that the spectral properties, aka the FFT, do matter. If so, I would find the following approach to be more effective (though much more tedious):

  1. Ensure that a closed-form expression is contained in the docstr.
  2. Calculate a closed-form expression of the FFT (I am sure that it can be found for most windows) and add it to the docstr.
  3. Test against the FFT expression in a unit test.

If deemed useful, the cited table from above could be added to this Window functions page.

That's a fair point, and honestly I wasn't fully sure myself. These tests were added to address the open issue #3432, and I followed the same PSLL/BW pattern already used in TestTaylor.test_correctness. Do you think it would be appropriate to keep these as a starting point for now and open a separate issue to track the more rigorous closed-form FFT approach you described? Thanks a lot for the detailed feedback, it's really helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Items related to regular maintenance tasks scipy.signal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test coverage for scipy.signals.windows needs improvement

6 participants