Skip to content

BUG: fix pywt.pad for a zero pad width in 'smooth' and 'antisymmetric' (gh-589) - #854

Open
Hrafz wants to merge 1 commit into
PyWavelets:mainfrom
Hrafz:bugfix/pad-zero-width
Open

BUG: fix pywt.pad for a zero pad width in 'smooth' and 'antisymmetric' (gh-589)#854
Hrafz wants to merge 1 commit into
PyWavelets:mainfrom
Hrafz:bugfix/pad-zero-width

Conversation

@Hrafz

@Hrafz Hrafz commented Aug 5, 2026

Copy link
Copy Markdown

Fixes #589.

A pad width of 0 should leave the array unchanged, but raises in the 'smooth'
and 'antisymmetric' extension modes:

>>> import numpy as np, pywt
>>> x = np.array([1., 2., 3.])
>>> pywt.pad(x, 0, 'smooth')
ValueError: could not broadcast input array from shape (0,) into shape (3,)
>>> pywt.pad(x, (1, 0), 'smooth')
ValueError: could not broadcast input array from shape (0,) into shape (4,)
>>> pywt.pad(x, 0, 'antisymmetric')
ValueError: could not broadcast input array from shape (0,) into shape (3,)
>>> pywt.pad(x, (2, 0), 'antisymmetric')
ValueError: can't extend empty axis 0 using modes other than 'constant' or 'empty'

Six of the nine extension modes return the array untouched for a zero pad
width. 'periodization' is the other exception, and a benign one:
pywt.pad(x, 0, 'periodization') gives [1., 2., 3., 3.], since it promotes
an odd-length axis to even length.

Both failing modes are implemented as numpy.pad callbacks, and both index the
vector from the end using the trailing pad width: vector[-pad_width[1]:] in
pad_smooth and vector[pad_width[0]:-pad_width[-1]] in pad_antisymmetric.
With a trailing width of 0 the first selects the whole vector instead of an
empty slice and the second selects an empty one, hence the two messages above.
The leading width is unaffected because vector[:0] is already empty.

The fix measures both indices from the start of the vector. pad_smooth also
read its right-hand slope with a second negative index (-pad_width[1] - 2),
now expressed relative to the same start-based index. Results are unchanged for
everything that worked before: comparing old against new over 1-D inputs of
length 1 to 5 and both pad widths from 0 to 4, in both modes, the only cases
that differ are ones that raised: a trailing width of 0, plus a length-1 axis
with (0, k) in 'smooth', which raised IndexError.

The message of the pad-width check is also corrected; it read
pad_widths must be > 0 while the check is < 0 and 0 is legal. Happy to drop
that line if you would rather keep the diff to the two callbacks.

Two regression tests are added to pywt/tests/test_dwt_idwt.py:
test_pad_zero_width asserts the no-op property in 1, 2 and 3 dimensions for
every mode except 'periodization', and test_pad_one_sided checks fixed
expected values plus the property that one-sided padding equals the
corresponding slice of two-sided padding, again for every mode except
'periodization'. Both tests fail on main and pass with this change.

PyWaveletsgh-589)

The two modes that numpy.pad cannot express directly are implemented with
callbacks that index the vector from the end using the trailing pad width.
When that width is 0, `vector[-pad_width[1]:]` selects the whole vector
instead of an empty slice, and `vector[pad_width[0]:-pad_width[-1]]` selects
an empty one, so pad() raised a ValueError instead of returning the array
unchanged. Every other extension mode already treats a zero pad width as a
no-op.

Index from the start of the vector instead, and correct the message of the
negative-pad-width check, which reported that 0 is invalid.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Padding error when padding width is 0 for antisymmetric and smooth

1 participant