From 36e770f1e9b4ad916c6f4c7b8da03000ea502d54 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sat, 23 Nov 2024 23:54:33 +1100 Subject: [PATCH 01/19] `signal`: Add type stubs for `sawtooth` and `square` in `_waveforms.pyi`. Some notes: 1. It seems the scipy implementation for `sawtooth` and `square` has a bug. The intention is that the dtype of the `t` array when it is `float32` or `float64` determines the output dtype but because the check wrongly uses a list instead of just a string this code path never runs. Therefore the output dtype is fixed to always be `float64`. This makes typing simpler however. This bug has been in the code for 19 years! --- scipy-stubs/signal/_waveforms.pyi | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 11389919..e37eaf12 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -1,9 +1,12 @@ from scipy._typing import Untyped +import optype.numpy as onp +import numpy as np +import numpy.typing as npt __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] -def sawtooth(t: Untyped, width: int = 1) -> Untyped: ... -def square(t: Untyped, duty: float = 0.5) -> Untyped: ... +def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> npt.NDArray[np.float64]: ... +def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> npt.NDArray[np.float64]: ... def gausspulse( t: Untyped, fc: int = 1000, From 157b713879340b16434bac76925904a75718a4d7 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 00:27:23 +1100 Subject: [PATCH 02/19] `signal`: Add type stubs for `sweep_poly` in `_waveforms.pyi` Wanted to add overloads depending on input dtype but because complex is a superclass of float, the overloads overlap and so we can't do it --- scipy-stubs/signal/_waveforms.pyi | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index e37eaf12..34ff0deb 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -25,5 +25,13 @@ def chirp( phi: int = 0, vertex_zero: bool = True, ) -> Untyped: ... -def sweep_poly(t: Untyped, poly: Untyped, phi: int = 0) -> Untyped: ... -def unit_impulse(shape: Untyped, idx: Untyped | None = None, dtype: Untyped = ...) -> Untyped: ... + +# float -> float and complex -> complex +def sweep_poly( + t: onp.ToFloatND | onp.ToComplexND, + poly: onp.ToFloatND | onp.ToComplexND, + phi: onp.ToInt = 0, +) -> npt.NDArray[np.float64 | np.complex128]: ... +def unit_impulse( + shape: Untyped, idx: Untyped | None = None, dtype: Untyped = ... +) -> Untyped: ... From 810224f00f160b7e371754f597770acb5a9a3c10 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 00:42:44 +1100 Subject: [PATCH 03/19] `signal`: Add type stubs to `unit_impulse` in `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 34ff0deb..0d3c14a3 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -1,10 +1,17 @@ -from scipy._typing import Untyped -import optype.numpy as onp +from collections.abc import Iterable +from typing import Literal, TypeVar, overload + import numpy as np import numpy.typing as npt +import optype as op +import optype.numpy as onp +from numpy._typing import _ShapeLike, _DTypeLike +from scipy._typing import Untyped __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] +_SCT = TypeVar("_SCT", bound=np.generic) + def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> npt.NDArray[np.float64]: ... def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> npt.NDArray[np.float64]: ... def gausspulse( @@ -32,6 +39,17 @@ def sweep_poly( poly: onp.ToFloatND | onp.ToComplexND, phi: onp.ToInt = 0, ) -> npt.NDArray[np.float64 | np.complex128]: ... + +# +@overload # dtype is not given def unit_impulse( - shape: Untyped, idx: Untyped | None = None, dtype: Untyped = ... -) -> Untyped: ... + shape: _ShapeLike, + idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, + dtype: type[float] = float, +) -> npt.NDArray[np.float64]: ... +@overload # dtype is given +def unit_impulse( + shape: _ShapeLike, + idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None, + dtype: _DTypeLike[_SCT], +) -> npt.NDArray[_SCT]: ... From dbe336e9b0ad35a4134344d950239db1a61577ef Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 01:19:55 +1100 Subject: [PATCH 04/19] `signal`+tests: Add type stubs for `gausspulse` in `_waveforms.pyi`. Add type tests as well to check overloads are complete --- scipy-stubs/signal/_waveforms.pyi | 93 ++++++++++++++++++++++++++++--- tests/signal/test_waveforms.pyi | 86 ++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 tests/signal/test_waveforms.pyi diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 0d3c14a3..a9f2113f 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -1,5 +1,5 @@ from collections.abc import Iterable -from typing import Literal, TypeVar, overload +from typing import Literal, TypeVar, overload, TypeAlias import numpy as np import numpy.typing as npt @@ -12,17 +12,92 @@ __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impu _SCT = TypeVar("_SCT", bound=np.generic) +_Truthy: TypeAlias = Literal[1, True] +_Falsy: TypeAlias = Literal[0, False] + def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> npt.NDArray[np.float64]: ... def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> npt.NDArray[np.float64]: ... + +# +@overload # retquad: False = ..., retenv: False = ... def gausspulse( - t: Untyped, - fc: int = 1000, - bw: float = 0.5, - bwr: int = -6, - tpr: int = -60, - retquad: bool = False, - retenv: bool = False, -) -> Untyped: ... + t: onp.ToFloatND, + fc: onp.ToInt = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToInt = -6, + tpr: onp.ToInt = -60, + retquad: _Falsy = False, + retenv: _Falsy = False, +) -> npt.NDArray[np.float64]: ... +@overload # retquad: False = ..., retenv: True (keyword) +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToInt = -6, + tpr: onp.ToInt = -60, + retquad: _Falsy = False, + *, + retenv: _Truthy, +) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +@overload # retquad: False (positional), retenv: False (positional) +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt, + bw: onp.ToFloat, + bwr: onp.ToInt, + tpr: onp.ToInt, + retquad: _Falsy, + retenv: _Truthy, +) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +@overload # retquad: True (positional), retenv: False = ... +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt, + bw: onp.ToFloat, + bwr: onp.ToInt, + tpr: onp.ToInt, + retquad: _Truthy, + retenv: _Falsy = False, +) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +@overload # retquad: True (keyword), retenv: False = ... +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToInt = -6, + tpr: onp.ToInt = -60, + *, + retquad: _Truthy, + retenv: _Falsy = False, +) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +@overload # retquad: True (positional), retenv: True (positional/keyword) +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt, + bw: onp.ToFloat, + bwr: onp.ToInt, + tpr: onp.ToInt, + retquad: _Truthy, + retenv: _Truthy, +) -> tuple[ + npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64] +]: ... +@overload # retquad: True (keyword), retenv: True +def gausspulse( + t: onp.ToFloatND, + fc: onp.ToInt = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToInt = -6, + tpr: onp.ToInt = -60, + *, + retquad: _Truthy, + retenv: _Truthy, +) -> tuple[ + npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64] +]: ... + +# def chirp( t: Untyped, f0: Untyped, diff --git a/tests/signal/test_waveforms.pyi b/tests/signal/test_waveforms.pyi new file mode 100644 index 00000000..0ac9d292 --- /dev/null +++ b/tests/signal/test_waveforms.pyi @@ -0,0 +1,86 @@ +from typing import Literal, TypeAlias +from typing_extensions import assert_type + +import numpy as np +import numpy.typing as npt +import optype.numpy as onp +from scipy.signal import gausspulse + +_Array_f8: TypeAlias = npt.NDArray[np.float64] +_Falsy: TypeAlias = Literal[0, False] +_Truthy: TypeAlias = Literal[1, True] + +_time: onp.ToFloatND +_int: onp.ToInt +_float: onp.ToFloat +_truthy: _Truthy +_falsy: _Falsy + +# test gausspulse function overloads +assert_type(gausspulse(_time), _Array_f8) +# Full positional arguments +assert_type(gausspulse(_time, _int, _float, _int, _int, _falsy, _falsy), _Array_f8) +assert_type( + gausspulse(_time, _int, _float, _int, _int, _truthy, _falsy), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse(_time, _int, _float, _int, _int, _falsy, _truthy), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse(_time, _int, _float, _int, _int, _truthy, _truthy), + tuple[_Array_f8, _Array_f8, _Array_f8], +) +# Full keyword arguments +assert_type(gausspulse(t=_time), _Array_f8) +assert_type( + gausspulse( + t=_time, + retquad=_falsy, + retenv=_falsy, + ), + _Array_f8, +) +assert_type( + gausspulse( + t=_time, + retquad=_truthy, + retenv=_falsy, + ), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse( + t=_time, + retquad=_falsy, + retenv=_truthy, + ), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse( + t=_time, + retquad=_truthy, + retenv=_truthy, + ), + tuple[_Array_f8, _Array_f8, _Array_f8], +) + +# Mixed positional and keyword arguments +assert_type( + gausspulse(_time, _int, _float, _int, _int, retquad=_falsy, retenv=_falsy), + _Array_f8, +) +assert_type( + gausspulse(_time, _int, _float, _int, _int, retquad=_truthy, retenv=_falsy), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse(_time, _int, _float, _int, _int, retquad=_falsy, retenv=_truthy), + tuple[_Array_f8, _Array_f8], +) +assert_type( + gausspulse(_time, _int, _float, _int, _int, retquad=_truthy, retenv=_truthy), + tuple[_Array_f8, _Array_f8, _Array_f8], +) From 7a63f34b678ecec3d3a8682cd79eb909fff68fde Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 01:25:25 +1100 Subject: [PATCH 05/19] `signal`: Add type stubs to `chirp` in `_waveforms.pyi`. float16, float32 preserve their while everything else becomes float64. Not sure how to represent this though. --- scipy-stubs/signal/_waveforms.pyi | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index a9f2113f..be512c9e 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -6,7 +6,6 @@ import numpy.typing as npt import optype as op import optype.numpy as onp from numpy._typing import _ShapeLike, _DTypeLike -from scipy._typing import Untyped __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] @@ -97,16 +96,16 @@ def gausspulse( npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64] ]: ... -# +# float16 -> float16, float32 -> float32, ... -> float64 def chirp( - t: Untyped, - f0: Untyped, - t1: Untyped, - f1: Untyped, - method: str = "linear", - phi: int = 0, - vertex_zero: bool = True, -) -> Untyped: ... + t: onp.ToFloatND, + f0: onp.ToFloat, + t1: onp.ToFloat, + f1: onp.ToFloat, + method: Literal["linear", "quadratic", "logarithmic", "hyperbolic"] = "linear", + phi: onp.ToInt = 0, + vertex_zero: op.CanBool = True, +) -> npt.NDArray[np.float16 | np.float32 | np.float64]: ... # float -> float and complex -> complex def sweep_poly( From 7c8aa771ccaa2beaf1550895ec15936a2e3ebdd7 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 01:27:52 +1100 Subject: [PATCH 06/19] `signal`: Don't allow onp.ToComplexND because time should be real-valued --- scipy-stubs/signal/_waveforms.pyi | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index be512c9e..dffdf8b1 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -106,13 +106,11 @@ def chirp( phi: onp.ToInt = 0, vertex_zero: op.CanBool = True, ) -> npt.NDArray[np.float16 | np.float32 | np.float64]: ... - -# float -> float and complex -> complex def sweep_poly( - t: onp.ToFloatND | onp.ToComplexND, - poly: onp.ToFloatND | onp.ToComplexND, + t: onp.ToFloatND, + poly: onp.ToFloatND, phi: onp.ToInt = 0, -) -> npt.NDArray[np.float64 | np.complex128]: ... +) -> npt.NDArray[np.float64]: ... # @overload # dtype is not given From d4163672ed4b2b4271f50ea45c745ad274e21831 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 01:29:35 +1100 Subject: [PATCH 07/19] `signal`: Use a type alias for `npt.NDArray[np.float64]` In `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index dffdf8b1..94deace0 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -13,9 +13,10 @@ _SCT = TypeVar("_SCT", bound=np.generic) _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] +_Array_f8: TypeAlias = npt.NDArray[np.float64] -def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> npt.NDArray[np.float64]: ... -def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> npt.NDArray[np.float64]: ... +def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> _Array_f8: ... +def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> _Array_f8: ... # @overload # retquad: False = ..., retenv: False = ... @@ -27,7 +28,7 @@ def gausspulse( tpr: onp.ToInt = -60, retquad: _Falsy = False, retenv: _Falsy = False, -) -> npt.NDArray[np.float64]: ... +) -> _Array_f8: ... @overload # retquad: False = ..., retenv: True (keyword) def gausspulse( t: onp.ToFloatND, @@ -38,7 +39,7 @@ def gausspulse( retquad: _Falsy = False, *, retenv: _Truthy, -) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: False (positional), retenv: False (positional) def gausspulse( t: onp.ToFloatND, @@ -48,7 +49,7 @@ def gausspulse( tpr: onp.ToInt, retquad: _Falsy, retenv: _Truthy, -) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: False = ... def gausspulse( t: onp.ToFloatND, @@ -58,7 +59,7 @@ def gausspulse( tpr: onp.ToInt, retquad: _Truthy, retenv: _Falsy = False, -) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: False = ... def gausspulse( t: onp.ToFloatND, @@ -69,7 +70,7 @@ def gausspulse( *, retquad: _Truthy, retenv: _Falsy = False, -) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: ... +) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: True (positional/keyword) def gausspulse( t: onp.ToFloatND, @@ -79,9 +80,7 @@ def gausspulse( tpr: onp.ToInt, retquad: _Truthy, retenv: _Truthy, -) -> tuple[ - npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64] -]: ... +) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: True def gausspulse( t: onp.ToFloatND, @@ -92,9 +91,7 @@ def gausspulse( *, retquad: _Truthy, retenv: _Truthy, -) -> tuple[ - npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64] -]: ... +) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... # float16 -> float16, float32 -> float32, ... -> float64 def chirp( @@ -110,7 +107,7 @@ def sweep_poly( t: onp.ToFloatND, poly: onp.ToFloatND, phi: onp.ToInt = 0, -) -> npt.NDArray[np.float64]: ... +) -> _Array_f8: ... # @overload # dtype is not given @@ -118,7 +115,7 @@ def unit_impulse( shape: _ShapeLike, idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, dtype: type[float] = float, -) -> npt.NDArray[np.float64]: ... +) -> _Array_f8: ... @overload # dtype is given def unit_impulse( shape: _ShapeLike, From cfb7713a8575861941ea63418eeb4ed23773637b Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 10:56:15 +1100 Subject: [PATCH 08/19] `signal`: Sort imports in `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 94deace0..6f6cba0d 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -1,11 +1,11 @@ from collections.abc import Iterable -from typing import Literal, TypeVar, overload, TypeAlias +from typing import Literal, TypeAlias, TypeVar, overload import numpy as np import numpy.typing as npt import optype as op import optype.numpy as onp -from numpy._typing import _ShapeLike, _DTypeLike +from numpy._typing import _DTypeLike, _ShapeLike __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] From 2d60ea05479f72cf6293b7f1d0df890510801fc5 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 10:57:49 +1100 Subject: [PATCH 09/19] `signal`: Omit default `float` value in `_waveforms`. Ruff complains that we can only use simple values and `float` (the type) is not a simple value. --- scipy-stubs/signal/_waveforms.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 6f6cba0d..520356c7 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -114,7 +114,7 @@ def sweep_poly( def unit_impulse( shape: _ShapeLike, idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, - dtype: type[float] = float, + dtype: type[float] = ..., ) -> _Array_f8: ... @overload # dtype is given def unit_impulse( From f2a870b0c0e6ad811202096ce1a6b9b931e05089 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 13:39:58 +1100 Subject: [PATCH 10/19] `signal`: Fix up some mistakes in `_waveforms.pyi`. --- scipy-stubs/signal/_waveforms.pyi | 63 ++++++++++++++++--------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 520356c7..8fc5e3d2 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -5,7 +5,8 @@ import numpy as np import numpy.typing as npt import optype as op import optype.numpy as onp -from numpy._typing import _DTypeLike, _ShapeLike +from numpy._typing import _DTypeLike +from scipy._typing import AnyShape __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] @@ -13,29 +14,29 @@ _SCT = TypeVar("_SCT", bound=np.generic) _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] -_Array_f8: TypeAlias = npt.NDArray[np.float64] +_Array_f8: TypeAlias = onp.ArrayND[np.float64] -def sawtooth(t: onp.ToFloatND, width: onp.ToInt = 1) -> _Array_f8: ... -def square(t: onp.ToFloatND, duty: onp.ToFloat = 0.5) -> _Array_f8: ... +def sawtooth(t: onp.ToFloat | onp.ToFloatND, width: onp.ToFloat | onp.ToFloatND = 1) -> _Array_f8: ... +def square(t: onp.ToFloat | onp.ToFloatND, duty: onp.ToFloat | onp.ToFloatND = 0.5) -> _Array_f8: ... # @overload # retquad: False = ..., retenv: False = ... def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt = 1000, + fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, - bwr: onp.ToInt = -6, - tpr: onp.ToInt = -60, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat | Literal["cutoff"] = -60, retquad: _Falsy = False, retenv: _Falsy = False, ) -> _Array_f8: ... @overload # retquad: False = ..., retenv: True (keyword) def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt = 1000, + fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, - bwr: onp.ToInt = -6, - tpr: onp.ToInt = -60, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat | Literal["cutoff"] = -60, retquad: _Falsy = False, *, retenv: _Truthy, @@ -43,30 +44,30 @@ def gausspulse( @overload # retquad: False (positional), retenv: False (positional) def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt, + fc: onp.ToFloat, bw: onp.ToFloat, - bwr: onp.ToInt, - tpr: onp.ToInt, + bwr: onp.ToFloat, + tpr: onp.ToFloat | Literal["cutoff"], retquad: _Falsy, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: False = ... def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt, + fc: onp.ToFloat, bw: onp.ToFloat, - bwr: onp.ToInt, - tpr: onp.ToInt, + bwr: onp.ToFloat, + tpr: onp.ToFloat | Literal["cutoff"], retquad: _Truthy, retenv: _Falsy = False, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: False = ... def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt = 1000, + fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, - bwr: onp.ToInt = -6, - tpr: onp.ToInt = -60, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat | Literal["cutoff"] = -60, *, retquad: _Truthy, retenv: _Falsy = False, @@ -74,20 +75,20 @@ def gausspulse( @overload # retquad: True (positional), retenv: True (positional/keyword) def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt, + fc: onp.ToFloat, bw: onp.ToFloat, - bwr: onp.ToInt, - tpr: onp.ToInt, + bwr: onp.ToFloat, + tpr: onp.ToFloat | Literal["cutoff"], retquad: _Truthy, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: True def gausspulse( t: onp.ToFloatND, - fc: onp.ToInt = 1000, + fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, - bwr: onp.ToInt = -6, - tpr: onp.ToInt = -60, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat | Literal["cutoff"] = -60, *, retquad: _Truthy, retenv: _Truthy, @@ -100,25 +101,25 @@ def chirp( t1: onp.ToFloat, f1: onp.ToFloat, method: Literal["linear", "quadratic", "logarithmic", "hyperbolic"] = "linear", - phi: onp.ToInt = 0, + phi: onp.ToFloat = 0, vertex_zero: op.CanBool = True, ) -> npt.NDArray[np.float16 | np.float32 | np.float64]: ... def sweep_poly( - t: onp.ToFloatND, - poly: onp.ToFloatND, - phi: onp.ToInt = 0, + t: onp.ToFloat | onp.ToFloatND, + poly: onp.ToFloatND | np.poly1d, + phi: onp.ToFloat = 0, ) -> _Array_f8: ... # @overload # dtype is not given def unit_impulse( - shape: _ShapeLike, + shape: AnyShape, idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, dtype: type[float] = ..., ) -> _Array_f8: ... @overload # dtype is given def unit_impulse( - shape: _ShapeLike, + shape: AnyShape, idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None, dtype: _DTypeLike[_SCT], ) -> npt.NDArray[_SCT]: ... From bb7ec093ce309e225466c2338b9b37472411d1bb Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 13:49:17 +1100 Subject: [PATCH 11/19] `signal`: Allow scalars as `t` in `gausspulse` --- scipy-stubs/signal/_waveforms.pyi | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 8fc5e3d2..6524fda0 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -14,15 +14,16 @@ _SCT = TypeVar("_SCT", bound=np.generic) _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] +_ArrayLikeFloat: TypeAlias = onp.ToFloat | onp.ToFloatND _Array_f8: TypeAlias = onp.ArrayND[np.float64] -def sawtooth(t: onp.ToFloat | onp.ToFloatND, width: onp.ToFloat | onp.ToFloatND = 1) -> _Array_f8: ... -def square(t: onp.ToFloat | onp.ToFloatND, duty: onp.ToFloat | onp.ToFloatND = 0.5) -> _Array_f8: ... +def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... +def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... # @overload # retquad: False = ..., retenv: False = ... def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -32,7 +33,7 @@ def gausspulse( ) -> _Array_f8: ... @overload # retquad: False = ..., retenv: True (keyword) def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -43,7 +44,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: False (positional), retenv: False (positional) def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -53,7 +54,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: False = ... def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -63,7 +64,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: False = ... def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -74,7 +75,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: True (positional/keyword) def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -84,7 +85,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: True def gausspulse( - t: onp.ToFloatND, + t: _ArrayLikeFloat, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -105,7 +106,7 @@ def chirp( vertex_zero: op.CanBool = True, ) -> npt.NDArray[np.float16 | np.float32 | np.float64]: ... def sweep_poly( - t: onp.ToFloat | onp.ToFloatND, + t: _ArrayLikeFloat, poly: onp.ToFloatND | np.poly1d, phi: onp.ToFloat = 0, ) -> _Array_f8: ... From d656dd63f71829864bc763a2d4b4286ff071a5f8 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 13:50:15 +1100 Subject: [PATCH 12/19] tests: Update `test_waveforms` --- tests/signal/test_waveforms.pyi | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/signal/test_waveforms.pyi b/tests/signal/test_waveforms.pyi index 0ac9d292..2c7bd7c3 100644 --- a/tests/signal/test_waveforms.pyi +++ b/tests/signal/test_waveforms.pyi @@ -2,16 +2,14 @@ from typing import Literal, TypeAlias from typing_extensions import assert_type import numpy as np -import numpy.typing as npt import optype.numpy as onp from scipy.signal import gausspulse -_Array_f8: TypeAlias = npt.NDArray[np.float64] +_Array_f8: TypeAlias = onp.ArrayND[np.float64] _Falsy: TypeAlias = Literal[0, False] _Truthy: TypeAlias = Literal[1, True] -_time: onp.ToFloatND -_int: onp.ToInt +_time: onp.ToFloat | onp.ToFloatND _float: onp.ToFloat _truthy: _Truthy _falsy: _Falsy @@ -19,17 +17,17 @@ _falsy: _Falsy # test gausspulse function overloads assert_type(gausspulse(_time), _Array_f8) # Full positional arguments -assert_type(gausspulse(_time, _int, _float, _int, _int, _falsy, _falsy), _Array_f8) +assert_type(gausspulse(_time, _float, _float, _float, _float, _falsy, _falsy), _Array_f8) assert_type( - gausspulse(_time, _int, _float, _int, _int, _truthy, _falsy), + gausspulse(_time, _float, _float, _float, _float, _truthy, _falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _int, _float, _int, _int, _falsy, _truthy), + gausspulse(_time, _float, _float, _float, _float, _falsy, _truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _int, _float, _int, _int, _truthy, _truthy), + gausspulse(_time, _float, _float, _float, _float, _truthy, _truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) # Full keyword arguments @@ -69,18 +67,18 @@ assert_type( # Mixed positional and keyword arguments assert_type( - gausspulse(_time, _int, _float, _int, _int, retquad=_falsy, retenv=_falsy), + gausspulse(_time, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), _Array_f8, ) assert_type( - gausspulse(_time, _int, _float, _int, _int, retquad=_truthy, retenv=_falsy), + gausspulse(_time, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _int, _float, _int, _int, retquad=_falsy, retenv=_truthy), + gausspulse(_time, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _int, _float, _int, _int, retquad=_truthy, retenv=_truthy), + gausspulse(_time, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) From 7524442442017c152655c44e7c0e98e5bb596e07 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 13:52:14 +1100 Subject: [PATCH 13/19] `signal`: `t` in `gausspulse` accepts the string "cutoff" not the `tpr` param --- scipy-stubs/signal/_waveforms.pyi | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 6524fda0..45499cb8 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -16,6 +16,7 @@ _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] _ArrayLikeFloat: TypeAlias = onp.ToFloat | onp.ToFloatND _Array_f8: TypeAlias = onp.ArrayND[np.float64] +_GaussPulseTime: TypeAlias = _ArrayLikeFloat | Literal["cutoff"] def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... @@ -23,73 +24,73 @@ def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... # @overload # retquad: False = ..., retenv: False = ... def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, - tpr: onp.ToFloat | Literal["cutoff"] = -60, + tpr: onp.ToFloat = -60, retquad: _Falsy = False, retenv: _Falsy = False, ) -> _Array_f8: ... @overload # retquad: False = ..., retenv: True (keyword) def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, - tpr: onp.ToFloat | Literal["cutoff"] = -60, + tpr: onp.ToFloat = -60, retquad: _Falsy = False, *, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: False (positional), retenv: False (positional) def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, - tpr: onp.ToFloat | Literal["cutoff"], + tpr: onp.ToFloat, retquad: _Falsy, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: False = ... def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, - tpr: onp.ToFloat | Literal["cutoff"], + tpr: onp.ToFloat, retquad: _Truthy, retenv: _Falsy = False, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: False = ... def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, - tpr: onp.ToFloat | Literal["cutoff"] = -60, + tpr: onp.ToFloat = -60, *, retquad: _Truthy, retenv: _Falsy = False, ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: True (positional/keyword) def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, - tpr: onp.ToFloat | Literal["cutoff"], + tpr: onp.ToFloat, retquad: _Truthy, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: True def gausspulse( - t: _ArrayLikeFloat, + t: _GaussPulseTime, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, - tpr: onp.ToFloat | Literal["cutoff"] = -60, + tpr: onp.ToFloat = -60, *, retquad: _Truthy, retenv: _Truthy, From 7b975ec78066a8eb34d43f51660f8db51003a1c3 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 13:53:10 +1100 Subject: [PATCH 14/19] tests: Update `test_waveforms` --- tests/signal/test_waveforms.pyi | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/signal/test_waveforms.pyi b/tests/signal/test_waveforms.pyi index 2c7bd7c3..7bbdfc6e 100644 --- a/tests/signal/test_waveforms.pyi +++ b/tests/signal/test_waveforms.pyi @@ -9,32 +9,32 @@ _Array_f8: TypeAlias = onp.ArrayND[np.float64] _Falsy: TypeAlias = Literal[0, False] _Truthy: TypeAlias = Literal[1, True] -_time: onp.ToFloat | onp.ToFloatND +_gauss_pulse_time: onp.ToFloat | onp.ToFloatND | Literal["cutoff"] _float: onp.ToFloat _truthy: _Truthy _falsy: _Falsy # test gausspulse function overloads -assert_type(gausspulse(_time), _Array_f8) +assert_type(gausspulse(_gauss_pulse_time), _Array_f8) # Full positional arguments -assert_type(gausspulse(_time, _float, _float, _float, _float, _falsy, _falsy), _Array_f8) +assert_type(gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _falsy, _falsy), _Array_f8) assert_type( - gausspulse(_time, _float, _float, _float, _float, _truthy, _falsy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _truthy, _falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _float, _float, _float, _float, _falsy, _truthy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _falsy, _truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _float, _float, _float, _float, _truthy, _truthy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _truthy, _truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) # Full keyword arguments -assert_type(gausspulse(t=_time), _Array_f8) +assert_type(gausspulse(t=_gauss_pulse_time), _Array_f8) assert_type( gausspulse( - t=_time, + t=_gauss_pulse_time, retquad=_falsy, retenv=_falsy, ), @@ -42,7 +42,7 @@ assert_type( ) assert_type( gausspulse( - t=_time, + t=_gauss_pulse_time, retquad=_truthy, retenv=_falsy, ), @@ -50,7 +50,7 @@ assert_type( ) assert_type( gausspulse( - t=_time, + t=_gauss_pulse_time, retquad=_falsy, retenv=_truthy, ), @@ -58,7 +58,7 @@ assert_type( ) assert_type( gausspulse( - t=_time, + t=_gauss_pulse_time, retquad=_truthy, retenv=_truthy, ), @@ -67,18 +67,18 @@ assert_type( # Mixed positional and keyword arguments assert_type( - gausspulse(_time, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), _Array_f8, ) assert_type( - gausspulse(_time, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_time, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), + gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) From 647b1c3aa058e5066abc53b360769b09372bda53 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 14:04:22 +1100 Subject: [PATCH 15/19] `signal`: Type annotate `chirp` so that the output dtype can be inferred In `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 33 ++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 45499cb8..f870384b 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -5,7 +5,7 @@ import numpy as np import numpy.typing as npt import optype as op import optype.numpy as onp -from numpy._typing import _DTypeLike +from numpy._typing import _ArrayLike, _DTypeLike, _NestedSequence from scipy._typing import AnyShape __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] @@ -18,6 +18,16 @@ _ArrayLikeFloat: TypeAlias = onp.ToFloat | onp.ToFloatND _Array_f8: TypeAlias = onp.ArrayND[np.float64] _GaussPulseTime: TypeAlias = _ArrayLikeFloat | Literal["cutoff"] +# Type vars to annotate `chirp` +_NBT1 = TypeVar("_NBT1", bound=npt.NBitBase) +_NBT2 = TypeVar("_NBT2", bound=npt.NBitBase) +_NBT3 = TypeVar("_NBT3", bound=npt.NBitBase) +_NBT4 = TypeVar("_NBT4", bound=npt.NBitBase) +_NBT5 = TypeVar("_NBT5", bound=npt.NBitBase) +_ChirpTime: TypeAlias = _ArrayLike[np.floating[_NBT1] | np.integer[_NBT1]] +_ChirpScalar: TypeAlias = float | np.floating[_NBT1] | np.integer[_NBT1] +_ChirpMethod: TypeAlias = Literal["linear", "quadratic", "logarithmic", "hyperbolic"] + def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... @@ -96,16 +106,29 @@ def gausspulse( retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... -# float16 -> float16, float32 -> float32, ... -> float64 +# +@overload # Static type checking for float values +def chirp( + t: _ChirpTime[_NBT1], + f0: _ChirpScalar[_NBT2], + t1: _ChirpScalar[_NBT3], + f1: _ChirpScalar[_NBT4], + method: _ChirpMethod = "linear", + phi: _ChirpScalar[_NBT5] = 0, + vertex_zero: op.CanBool = True, +) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... +@overload # Other dtypes default to np.float64 def chirp( - t: onp.ToFloatND, + t: onp.ToFloatND | _NestedSequence[float], f0: onp.ToFloat, t1: onp.ToFloat, f1: onp.ToFloat, - method: Literal["linear", "quadratic", "logarithmic", "hyperbolic"] = "linear", + method: _ChirpMethod = "linear", phi: onp.ToFloat = 0, vertex_zero: op.CanBool = True, -) -> npt.NDArray[np.float16 | np.float32 | np.float64]: ... +) -> _Array_f8: ... + +# def sweep_poly( t: _ArrayLikeFloat, poly: onp.ToFloatND | np.poly1d, From 3278292fb242e8b20e5a7dffe9fb11643c083cc7 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 14:22:41 +1100 Subject: [PATCH 16/19] `signal`+tests: Add all possible overloads for `gausspulse`. `t` can be either an array or a scalar or "cutoff". Add corresponding tests --- scipy-stubs/signal/_waveforms.pyi | 186 ++++++++++++++++++++++-------- tests/signal/test_waveforms.pyi | 154 ++++++++++++++++++++++--- 2 files changed, 275 insertions(+), 65 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index f870384b..8e0c03f6 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -16,7 +16,6 @@ _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] _ArrayLikeFloat: TypeAlias = onp.ToFloat | onp.ToFloatND _Array_f8: TypeAlias = onp.ArrayND[np.float64] -_GaussPulseTime: TypeAlias = _ArrayLikeFloat | Literal["cutoff"] # Type vars to annotate `chirp` _NBT1 = TypeVar("_NBT1", bound=npt.NBitBase) @@ -32,9 +31,127 @@ def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... # +@overload # Static type checking for float values +def chirp( + t: _ChirpTime[_NBT1], + f0: _ChirpScalar[_NBT2], + t1: _ChirpScalar[_NBT3], + f1: _ChirpScalar[_NBT4], + method: _ChirpMethod = "linear", + phi: _ChirpScalar[_NBT5] = 0, + vertex_zero: op.CanBool = True, +) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... +@overload # Other dtypes default to np.float64 +def chirp( + t: onp.ToFloatND | _NestedSequence[float], + f0: onp.ToFloat, + t1: onp.ToFloat, + f1: onp.ToFloat, + method: _ChirpMethod = "linear", + phi: onp.ToFloat = 0, + vertex_zero: op.CanBool = True, +) -> _Array_f8: ... + +# +def sweep_poly( + t: _ArrayLikeFloat, + poly: onp.ToFloatND | np.poly1d, + phi: onp.ToFloat = 0, +) -> _Array_f8: ... + +# +@overload # dtype is not given +def unit_impulse( + shape: AnyShape, + idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, + dtype: type[float] = ..., +) -> _Array_f8: ... +@overload # dtype is given +def unit_impulse( + shape: AnyShape, + idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None, + dtype: _DTypeLike[_SCT], +) -> npt.NDArray[_SCT]: ... + +# Overloads for gausspulse when `t` is scalar +@overload # retquad: False = ..., retenv: False = ... +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + retquad: _Falsy = False, + retenv: _Falsy = False, +) -> np.float64: ... +@overload # retquad: False = ..., retenv: True (keyword) +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + retquad: _Falsy = False, + *, + retenv: _Truthy, +) -> tuple[np.float64, np.float64]: ... +@overload # retquad: False (positional), retenv: False (positional) +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat, + bw: onp.ToFloat, + bwr: onp.ToFloat, + tpr: onp.ToFloat, + retquad: _Falsy, + retenv: _Truthy, +) -> tuple[np.float64, np.float64]: ... +@overload # retquad: True (positional), retenv: False = ... +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat, + bw: onp.ToFloat, + bwr: onp.ToFloat, + tpr: onp.ToFloat, + retquad: _Truthy, + retenv: _Falsy = False, +) -> tuple[np.float64, np.float64]: ... +@overload # retquad: True (keyword), retenv: False = ... +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + *, + retquad: _Truthy, + retenv: _Falsy = False, +) -> tuple[np.float64, np.float64]: ... +@overload # retquad: True (positional), retenv: True (positional/keyword) +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat, + bw: onp.ToFloat, + bwr: onp.ToFloat, + tpr: onp.ToFloat, + retquad: _Truthy, + retenv: _Truthy, +) -> tuple[np.float64, np.float64, np.float64]: ... +@overload # retquad: True (keyword), retenv: True +def gausspulse( + t: onp.ToFloat, + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + *, + retquad: _Truthy, + retenv: _Truthy, +) -> tuple[np.float64, np.float64, np.float64]: ... + +# Overloads for `gausspulse` when `t` is a non-scalar array like @overload # retquad: False = ..., retenv: False = ... def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -44,7 +161,7 @@ def gausspulse( ) -> _Array_f8: ... @overload # retquad: False = ..., retenv: True (keyword) def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -55,7 +172,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: False (positional), retenv: False (positional) def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -65,7 +182,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: False = ... def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -75,7 +192,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: False = ... def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -86,7 +203,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8]: ... @overload # retquad: True (positional), retenv: True (positional/keyword) def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat, bw: onp.ToFloat, bwr: onp.ToFloat, @@ -96,7 +213,7 @@ def gausspulse( ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... @overload # retquad: True (keyword), retenv: True def gausspulse( - t: _GaussPulseTime, + t: onp.ToFloatND, fc: onp.ToFloat = 1000, bw: onp.ToFloat = 0.5, bwr: onp.ToFloat = -6, @@ -106,45 +223,14 @@ def gausspulse( retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... -# -@overload # Static type checking for float values -def chirp( - t: _ChirpTime[_NBT1], - f0: _ChirpScalar[_NBT2], - t1: _ChirpScalar[_NBT3], - f1: _ChirpScalar[_NBT4], - method: _ChirpMethod = "linear", - phi: _ChirpScalar[_NBT5] = 0, - vertex_zero: op.CanBool = True, -) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... -@overload # Other dtypes default to np.float64 -def chirp( - t: onp.ToFloatND | _NestedSequence[float], - f0: onp.ToFloat, - t1: onp.ToFloat, - f1: onp.ToFloat, - method: _ChirpMethod = "linear", - phi: onp.ToFloat = 0, - vertex_zero: op.CanBool = True, -) -> _Array_f8: ... - -# -def sweep_poly( - t: _ArrayLikeFloat, - poly: onp.ToFloatND | np.poly1d, - phi: onp.ToFloat = 0, -) -> _Array_f8: ... - -# -@overload # dtype is not given -def unit_impulse( - shape: AnyShape, - idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None = None, - dtype: type[float] = ..., -) -> _Array_f8: ... -@overload # dtype is given -def unit_impulse( - shape: AnyShape, - idx: op.CanIndex | Iterable[op.CanIndex] | Literal["mid"] | None, - dtype: _DTypeLike[_SCT], -) -> npt.NDArray[_SCT]: ... +# Overloads for gausspulse when `t` is `"cutoff"` +@overload # retquad: False = ..., retenv: False = ... +def gausspulse( + t: Literal["cutoff"], + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + retquad: op.CanBool = False, + retenv: op.CanBool = False, +) -> np.float64: ... diff --git a/tests/signal/test_waveforms.pyi b/tests/signal/test_waveforms.pyi index 7bbdfc6e..94bbf253 100644 --- a/tests/signal/test_waveforms.pyi +++ b/tests/signal/test_waveforms.pyi @@ -6,35 +6,39 @@ import optype.numpy as onp from scipy.signal import gausspulse _Array_f8: TypeAlias = onp.ArrayND[np.float64] +_Scalar_f8: TypeAlias = np.float64 _Falsy: TypeAlias = Literal[0, False] _Truthy: TypeAlias = Literal[1, True] -_gauss_pulse_time: onp.ToFloat | onp.ToFloatND | Literal["cutoff"] +_time_scalar: onp.ToFloat +_time_array: onp.ToFloatND +_time_cutoff: Literal["cutoff"] _float: onp.ToFloat _truthy: _Truthy _falsy: _Falsy # test gausspulse function overloads -assert_type(gausspulse(_gauss_pulse_time), _Array_f8) +# `time` as an array +assert_type(gausspulse(_time_array), _Array_f8) # Full positional arguments -assert_type(gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _falsy, _falsy), _Array_f8) +assert_type(gausspulse(_time_array, _float, _float, _float, _float, _falsy, _falsy), _Array_f8) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _truthy, _falsy), + gausspulse(_time_array, _float, _float, _float, _float, _truthy, _falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _falsy, _truthy), + gausspulse(_time_array, _float, _float, _float, _float, _falsy, _truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, _truthy, _truthy), + gausspulse(_time_array, _float, _float, _float, _float, _truthy, _truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) # Full keyword arguments -assert_type(gausspulse(t=_gauss_pulse_time), _Array_f8) +assert_type(gausspulse(t=_time_array), _Array_f8) assert_type( gausspulse( - t=_gauss_pulse_time, + t=_time_array, retquad=_falsy, retenv=_falsy, ), @@ -42,7 +46,7 @@ assert_type( ) assert_type( gausspulse( - t=_gauss_pulse_time, + t=_time_array, retquad=_truthy, retenv=_falsy, ), @@ -50,7 +54,7 @@ assert_type( ) assert_type( gausspulse( - t=_gauss_pulse_time, + t=_time_array, retquad=_falsy, retenv=_truthy, ), @@ -58,7 +62,7 @@ assert_type( ) assert_type( gausspulse( - t=_gauss_pulse_time, + t=_time_array, retquad=_truthy, retenv=_truthy, ), @@ -67,18 +71,138 @@ assert_type( # Mixed positional and keyword arguments assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), + gausspulse(_time_array, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), _Array_f8, ) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), + gausspulse(_time_array, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), + gausspulse(_time_array, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), tuple[_Array_f8, _Array_f8], ) assert_type( - gausspulse(_gauss_pulse_time, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), + gausspulse(_time_array, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), tuple[_Array_f8, _Array_f8, _Array_f8], ) + +# `time` as a scalar +assert_type(gausspulse(_time_scalar), _Scalar_f8) +# Full positional arguments +assert_type(gausspulse(_time_scalar, _float, _float, _float, _float, _falsy, _falsy), _Scalar_f8) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, _truthy, _falsy), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, _falsy, _truthy), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, _truthy, _truthy), + tuple[_Scalar_f8, _Scalar_f8, _Scalar_f8], +) +# Full keyword arguments +assert_type(gausspulse(t=_time_scalar), _Scalar_f8) +assert_type( + gausspulse( + t=_time_scalar, + retquad=_falsy, + retenv=_falsy, + ), + _Scalar_f8, +) +assert_type( + gausspulse( + t=_time_scalar, + retquad=_truthy, + retenv=_falsy, + ), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse( + t=_time_scalar, + retquad=_falsy, + retenv=_truthy, + ), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse( + t=_time_scalar, + retquad=_truthy, + retenv=_truthy, + ), + tuple[_Scalar_f8, _Scalar_f8, _Scalar_f8], +) + +# Mixed positional and keyword arguments +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), + _Scalar_f8, +) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), + tuple[_Scalar_f8, _Scalar_f8], +) +assert_type( + gausspulse(_time_scalar, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), + tuple[_Scalar_f8, _Scalar_f8, _Scalar_f8], +) + +# `time` as the literal `"cutoff"` +assert_type(gausspulse(_time_cutoff), _Scalar_f8) +# Full positional arguments +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, _falsy, _falsy), _Scalar_f8) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, _truthy, _falsy), _Scalar_f8) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, _falsy, _truthy), _Scalar_f8) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, _truthy, _truthy), _Scalar_f8) +# Full keyword arguments +assert_type(gausspulse(t=_time_cutoff), _Scalar_f8) +assert_type( + gausspulse( + t=_time_cutoff, + retquad=_falsy, + retenv=_falsy, + ), + _Scalar_f8, +) +assert_type( + gausspulse( + t=_time_cutoff, + retquad=_truthy, + retenv=_falsy, + ), + _Scalar_f8, +) +assert_type( + gausspulse( + t=_time_cutoff, + retquad=_falsy, + retenv=_truthy, + ), + _Scalar_f8, +) +assert_type( + gausspulse( + t=_time_cutoff, + retquad=_truthy, + retenv=_truthy, + ), + _Scalar_f8, +) + +# Mixed positional and keyword arguments +assert_type( + gausspulse(_time_cutoff, _float, _float, _float, _float, retquad=_falsy, retenv=_falsy), + _Scalar_f8, +) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, retquad=_truthy, retenv=_falsy), _Scalar_f8) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, retquad=_falsy, retenv=_truthy), _Scalar_f8) +assert_type(gausspulse(_time_cutoff, _float, _float, _float, _float, retquad=_truthy, retenv=_truthy), _Scalar_f8) From 4d74dc839a7a9cf3bab45b059f50404bc27dfdcb Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Sun, 24 Nov 2024 16:36:53 +1100 Subject: [PATCH 17/19] `signal`: Move `gausspulse` overloads around to satisfy mypy This doesn't fix `stubtest` however but it does fix `typetest`. --- scipy-stubs/signal/_waveforms.pyi | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index 8e0c03f6..ac1483aa 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -73,6 +73,18 @@ def unit_impulse( dtype: _DTypeLike[_SCT], ) -> npt.NDArray[_SCT]: ... +# Overloads for gausspulse when `t` is `"cutoff"` +@overload # retquad: False = ..., retenv: False = ... +def gausspulse( + t: Literal["cutoff"], + fc: onp.ToFloat = 1000, + bw: onp.ToFloat = 0.5, + bwr: onp.ToFloat = -6, + tpr: onp.ToFloat = -60, + retquad: op.CanBool = False, + retenv: op.CanBool = False, +) -> np.float64: ... + # Overloads for gausspulse when `t` is scalar @overload # retquad: False = ..., retenv: False = ... def gausspulse( @@ -222,15 +234,3 @@ def gausspulse( retquad: _Truthy, retenv: _Truthy, ) -> tuple[_Array_f8, _Array_f8, _Array_f8]: ... - -# Overloads for gausspulse when `t` is `"cutoff"` -@overload # retquad: False = ..., retenv: False = ... -def gausspulse( - t: Literal["cutoff"], - fc: onp.ToFloat = 1000, - bw: onp.ToFloat = 0.5, - bwr: onp.ToFloat = -6, - tpr: onp.ToFloat = -60, - retquad: op.CanBool = False, - retenv: op.CanBool = False, -) -> np.float64: ... From 60570a04f65d2b50d50af2fb80235a1e5ff54fdb Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Mon, 25 Nov 2024 23:47:24 +1100 Subject: [PATCH 18/19] `signal`: Fix `chirp` overload overlap in `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index ac1483aa..e4d4cee6 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -5,7 +5,7 @@ import numpy as np import numpy.typing as npt import optype as op import optype.numpy as onp -from numpy._typing import _ArrayLike, _DTypeLike, _NestedSequence +from numpy._typing import _ArrayLike, _DTypeLike from scipy._typing import AnyShape __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] @@ -23,7 +23,6 @@ _NBT2 = TypeVar("_NBT2", bound=npt.NBitBase) _NBT3 = TypeVar("_NBT3", bound=npt.NBitBase) _NBT4 = TypeVar("_NBT4", bound=npt.NBitBase) _NBT5 = TypeVar("_NBT5", bound=npt.NBitBase) -_ChirpTime: TypeAlias = _ArrayLike[np.floating[_NBT1] | np.integer[_NBT1]] _ChirpScalar: TypeAlias = float | np.floating[_NBT1] | np.integer[_NBT1] _ChirpMethod: TypeAlias = Literal["linear", "quadratic", "logarithmic", "hyperbolic"] @@ -31,19 +30,9 @@ def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... # -@overload # Static type checking for float values -def chirp( - t: _ChirpTime[_NBT1], - f0: _ChirpScalar[_NBT2], - t1: _ChirpScalar[_NBT3], - f1: _ChirpScalar[_NBT4], - method: _ChirpMethod = "linear", - phi: _ChirpScalar[_NBT5] = 0, - vertex_zero: op.CanBool = True, -) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... @overload # Other dtypes default to np.float64 def chirp( - t: onp.ToFloatND | _NestedSequence[float], + t: onp.SequenceND[float], f0: onp.ToFloat, t1: onp.ToFloat, f1: onp.ToFloat, @@ -51,6 +40,16 @@ def chirp( phi: onp.ToFloat = 0, vertex_zero: op.CanBool = True, ) -> _Array_f8: ... +@overload # Static type checking for float values +def chirp( + t: _ArrayLike[np.floating[_NBT1] | np.integer[_NBT1]], + f0: _ChirpScalar[_NBT2], + t1: _ChirpScalar[_NBT3], + f1: _ChirpScalar[_NBT4], + method: _ChirpMethod = "linear", + phi: _ChirpScalar[_NBT5] = 0, + vertex_zero: op.CanBool = True, +) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... # def sweep_poly( From 76785f557713244fee78e1630e461c5c857cc895 Mon Sep 17 00:00:00 2001 From: Pavadol Yamsiri Date: Tue, 26 Nov 2024 02:14:37 +1100 Subject: [PATCH 19/19] `signal`: Simplify overloads for `chirp` in `_waveforms.pyi` --- scipy-stubs/signal/_waveforms.pyi | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/scipy-stubs/signal/_waveforms.pyi b/scipy-stubs/signal/_waveforms.pyi index e4d4cee6..4886184b 100644 --- a/scipy-stubs/signal/_waveforms.pyi +++ b/scipy-stubs/signal/_waveforms.pyi @@ -1,11 +1,11 @@ from collections.abc import Iterable -from typing import Literal, TypeAlias, TypeVar, overload +from typing import Any, Literal, TypeAlias, TypeVar, overload import numpy as np import numpy.typing as npt import optype as op import optype.numpy as onp -from numpy._typing import _ArrayLike, _DTypeLike +from numpy._typing import _DTypeLike from scipy._typing import AnyShape __all__ = ["chirp", "gausspulse", "sawtooth", "square", "sweep_poly", "unit_impulse"] @@ -15,41 +15,35 @@ _SCT = TypeVar("_SCT", bound=np.generic) _Truthy: TypeAlias = Literal[1, True] _Falsy: TypeAlias = Literal[0, False] _ArrayLikeFloat: TypeAlias = onp.ToFloat | onp.ToFloatND +_Array_f: TypeAlias = onp.ArrayND[np.floating[Any]] _Array_f8: TypeAlias = onp.ArrayND[np.float64] -# Type vars to annotate `chirp` -_NBT1 = TypeVar("_NBT1", bound=npt.NBitBase) -_NBT2 = TypeVar("_NBT2", bound=npt.NBitBase) -_NBT3 = TypeVar("_NBT3", bound=npt.NBitBase) -_NBT4 = TypeVar("_NBT4", bound=npt.NBitBase) -_NBT5 = TypeVar("_NBT5", bound=npt.NBitBase) -_ChirpScalar: TypeAlias = float | np.floating[_NBT1] | np.integer[_NBT1] _ChirpMethod: TypeAlias = Literal["linear", "quadratic", "logarithmic", "hyperbolic"] def sawtooth(t: _ArrayLikeFloat, width: _ArrayLikeFloat = 1) -> _Array_f8: ... def square(t: _ArrayLikeFloat, duty: _ArrayLikeFloat = 0.5) -> _Array_f8: ... # -@overload # Other dtypes default to np.float64 +@overload # Arrays def chirp( - t: onp.SequenceND[float], + t: onp.ToFloatND, f0: onp.ToFloat, t1: onp.ToFloat, f1: onp.ToFloat, method: _ChirpMethod = "linear", phi: onp.ToFloat = 0, vertex_zero: op.CanBool = True, -) -> _Array_f8: ... -@overload # Static type checking for float values +) -> _Array_f: ... +@overload # Scalars def chirp( - t: _ArrayLike[np.floating[_NBT1] | np.integer[_NBT1]], - f0: _ChirpScalar[_NBT2], - t1: _ChirpScalar[_NBT3], - f1: _ChirpScalar[_NBT4], + t: onp.ToFloat, + f0: onp.ToFloat, + t1: onp.ToFloat, + f1: onp.ToFloat, method: _ChirpMethod = "linear", - phi: _ChirpScalar[_NBT5] = 0, + phi: onp.ToFloat = 0, vertex_zero: op.CanBool = True, -) -> onp.ArrayND[np.floating[_NBT1 | _NBT2 | _NBT3 | _NBT4 | _NBT5]]: ... +) -> np.floating[Any]: ... # def sweep_poly(