numpy.unwrap changes targeting 2.6.0 (new _unwrap gufunc)
Iason Krommydas via NumPy-Discussion <[email protected]> Tue, 21 Jul 2026 20:56:13 -0500
| Newsgroups | gmane.comp.python.numeric.general |
|---|---|
| Message-ID | <CAMnQhRFRtr742Gmm8LzXKLYqKBr7X6WJTSu0R2VGxq1ZvC-P-g@mail.gmail.com> |
--===============2604038207089080382== Content-Type: multipart/alternative; boundary="00000000000069a796065729724c" --00000000000069a796065729724c Content-Type: text/plain; charset="UTF-8" Hi all, In gh-31848 <https://github.com/numpy/numpy/pull/31848>, numpy.unwrap has been reimplemented as a generalized ufunc, targeting the 2.6.0 release. Below is a summary of what's changing. The core of unwrap is now a generalized ufunc written in C++. That is a new private ufunc _unwrap with signature (n),(),()->(n), covering the floating point and signed integer dtypes, computed in a single pass without the intermediate arrays the previous python implementation had to allocate. We've seen that this offers a 2x-3x speedup and lower memory usage. As a result, unwrap now preserves ndarray subclasses instead of always returning a base ndarray. The one exception is masked arrays. A mask-aware unwrap is genuinely ill-defined for this algorithm, so masked arrays (and their subclasses) get unmasked and the operation runs on a plain ndarray instead, same as before. I will open a separate issue and have put a TODO in the codebase to perhaps get a proper mask-aware unwrap in the np.ma module. Two edge-case behavior changes: - An explicitly typed discont argument wider than the result dtype used to get promoted. It's now compared at the result dtype instead. This can change the result by up to ~1 ULP in that specific case. - Calling unwrap with an unsigned integer period that can't represent the values needed internally used to raise OverflowError (from an internal mod call). it now raises TypeError (a "no loop found" ufunc error reporting the mismatched dtypes) instead. If you maintain an array library that implements __array_ufunc__ and tries to stay compatible with all of numpy, you'll now see a call to _unwrap where unwrap previously dispatched through a sequence of familiar public ufuncs (subtract, remainder, cumsum, etc.). If your override doesn't recognize _unwrap but still handles those underlying ops, unwrap falls back to a pure Python path built from them, so things should keep working. If it declines everything unconditionally, unwrap will now raise instead of silently doing the wrong thing. Astropy is the library we know of that might care here, flagging in case others maintain something similar. Kind regards, Iason. --00000000000069a796065729724c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><div>Hi all,</div><div><br></div><div>In=C2=A0<a href=3D"h= ttps://github.com/numpy/numpy/pull/31848">gh-31848</a>,=C2=A0numpy.unwrap h= as been reimplemented as a generalized ufunc, targeting=C2=A0the 2.6.0 rele= ase. Below is a summary of what's changing.</div><div><br></div><div>Th= e core of unwrap is now a generalized ufunc written in C++. That is a new p= rivate ufunc _unwrap with signature (n),(),()->(n), covering the=C2=A0 f= loating point and signed integer dtypes, computed in a single pass without = the intermediate arrays the previous python implementation had to allocate.= We've seen that this offers a 2x-3x speedup and lower memory usage.</d= iv><div><br></div><div>As a result, unwrap=C2=A0now preserves ndarray subcl= asses instead of always returning a base ndarray. The one exception is mask= ed arrays. A mask-aware unwrap is genuinely ill-defined for this algorithm,= so masked arrays (and their subclasses) get unmasked and the operation run= s on a plain ndarray instead, same as before. I will open a separate issue = and have put a TODO in the codebase to perhaps get a proper mask-aware unwr= ap in the <a href=3D"http://np.ma">np.ma</a> module.</div><div><br></div><d= iv>Two edge-case behavior changes:<br>- An explicitly typed discont argumen= t wider than the result dtype used to get promoted. It's now compared a= t the result dtype instead. This can change the result by up to ~1 ULP in t= hat specific case.<br>- Calling unwrap with an unsigned integer period that= can't represent the values needed internally used to raise OverflowErr= or (from an internal mod call). it now raises TypeError (a "no loop fo= und" ufunc error reporting the mismatched dtypes) instead.<br><br></di= v><div>If you maintain an array library that implements __array_ufunc__ and= tries to stay compatible with all of=C2=A0 numpy, you'll now see a cal= l to _unwrap where unwrap previously dispatched through a sequence of famil= iar public ufuncs (subtract, remainder, cumsum, =C2=A0 etc.). If your overr= ide doesn't recognize _unwrap but still handles those underlying ops, u= nwrap falls back to a pure Python path built from them, so things should ke= ep working. If it declines everything unconditionally, unwrap will now rais= e instead of silently doing the wrong thing. Astropy is the library we know= of that might care here, flagging in case others maintain something simila= r.</div><div><br></div><div>Kind regards,</div><div>Iason.</div></div> --00000000000069a796065729724c-- --===============2604038207089080382== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ NumPy-Discussion mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/numpy-discussion.python.org Member address: [email protected] --===============2604038207089080382==--