xref: /aosp_15_r20/external/pytorch/torch/signal/windows/windows.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1# mypy: allow-untyped-decorators
2# mypy: allow-untyped-defs
3from typing import Optional, Iterable
4
5import torch
6from math import sqrt
7
8from torch import Tensor
9from torch._torch_docs import factory_common_args, parse_kwargs, merge_dicts
10
11__all__ = [
12    'bartlett',
13    'blackman',
14    'cosine',
15    'exponential',
16    'gaussian',
17    'general_cosine',
18    'general_hamming',
19    'hamming',
20    'hann',
21    'kaiser',
22    'nuttall',
23]
24
25window_common_args = merge_dicts(
26    parse_kwargs(
27        """
28    M (int): the length of the window.
29        In other words, the number of points of the returned window.
30    sym (bool, optional): If `False`, returns a periodic window suitable for use in spectral analysis.
31        If `True`, returns a symmetric window suitable for use in filter design. Default: `True`.
32"""
33    ),
34    factory_common_args,
35    {
36        "normalization": "The window is normalized to 1 (maximum value is 1). However, the 1 doesn't appear if "
37                         ":attr:`M` is even and :attr:`sym` is `True`.",
38    }
39)
40
41
42def _add_docstr(*args):
43    r"""Adds docstrings to a given decorated function.
44
45    Specially useful when then docstrings needs string interpolation, e.g., with
46    str.format().
47    REMARK: Do not use this function if the docstring doesn't need string
48    interpolation, just write a conventional docstring.
49
50    Args:
51        args (str):
52    """
53
54    def decorator(o):
55        o.__doc__ = "".join(args)
56        return o
57
58    return decorator
59
60
61def _window_function_checks(function_name: str, M: int, dtype: torch.dtype, layout: torch.layout) -> None:
62    r"""Performs common checks for all the defined windows.
63     This function should be called before computing any window.
64
65     Args:
66         function_name (str): name of the window function.
67         M (int): length of the window.
68         dtype (:class:`torch.dtype`): the desired data type of returned tensor.
69         layout (:class:`torch.layout`): the desired layout of returned tensor.
70     """
71    if M < 0:
72        raise ValueError(f'{function_name} requires non-negative window length, got M={M}')
73    if layout is not torch.strided:
74        raise ValueError(f'{function_name} is implemented for strided tensors only, got: {layout}')
75    if dtype not in [torch.float32, torch.float64]:
76        raise ValueError(f'{function_name} expects float32 or float64 dtypes, got: {dtype}')
77
78
79@_add_docstr(
80    r"""
81Computes a window with an exponential waveform.
82Also known as Poisson window.
83
84The exponential window is defined as follows:
85
86.. math::
87    w_n = \exp{\left(-\frac{|n - c|}{\tau}\right)}
88
89where `c` is the ``center`` of the window.
90    """,
91    r"""
92
93{normalization}
94
95Args:
96    {M}
97
98Keyword args:
99    center (float, optional): where the center of the window will be located.
100        Default: `M / 2` if `sym` is `False`, else `(M - 1) / 2`.
101    tau (float, optional): the decay value.
102        Tau is generally associated with a percentage, that means, that the value should
103        vary within the interval (0, 100]. If tau is 100, it is considered the uniform window.
104        Default: 1.0.
105    {sym}
106    {dtype}
107    {layout}
108    {device}
109    {requires_grad}
110
111Examples::
112
113    >>> # Generates a symmetric exponential window of size 10 and with a decay value of 1.0.
114    >>> # The center will be at (M - 1) / 2, where M is 10.
115    >>> torch.signal.windows.exponential(10)
116    tensor([0.0111, 0.0302, 0.0821, 0.2231, 0.6065, 0.6065, 0.2231, 0.0821, 0.0302, 0.0111])
117
118    >>> # Generates a periodic exponential window and decay factor equal to .5
119    >>> torch.signal.windows.exponential(10, sym=False,tau=.5)
120    tensor([4.5400e-05, 3.3546e-04, 2.4788e-03, 1.8316e-02, 1.3534e-01, 1.0000e+00, 1.3534e-01, 1.8316e-02, 2.4788e-03, 3.3546e-04])
121    """.format(
122        **window_common_args
123    ),
124)
125def exponential(
126        M: int,
127        *,
128        center: Optional[float] = None,
129        tau: float = 1.0,
130        sym: bool = True,
131        dtype: Optional[torch.dtype] = None,
132        layout: torch.layout = torch.strided,
133        device: Optional[torch.device] = None,
134        requires_grad: bool = False
135) -> Tensor:
136    if dtype is None:
137        dtype = torch.get_default_dtype()
138
139    _window_function_checks('exponential', M, dtype, layout)
140
141    if tau <= 0:
142        raise ValueError(f'Tau must be positive, got: {tau} instead.')
143
144    if sym and center is not None:
145        raise ValueError('Center must be None for symmetric windows')
146
147    if M == 0:
148        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
149
150    if center is None:
151        center = (M if not sym and M > 1 else M - 1) / 2.0
152
153    constant = 1 / tau
154
155    k = torch.linspace(start=-center * constant,
156                       end=(-center + (M - 1)) * constant,
157                       steps=M,
158                       dtype=dtype,
159                       layout=layout,
160                       device=device,
161                       requires_grad=requires_grad)
162
163    return torch.exp(-torch.abs(k))
164
165
166@_add_docstr(
167    r"""
168Computes a window with a simple cosine waveform, following the same implementation as SciPy.
169This window is also known as the sine window.
170
171The cosine window is defined as follows:
172
173.. math::
174    w_n = \sin\left(\frac{\pi (n + 0.5)}{M}\right)
175
176This formula differs from the typical cosine window formula by incorporating a 0.5 term in the numerator,
177which shifts the sample positions. This adjustment results in a window that starts and ends with non-zero values.
178
179""",
180    r"""
181
182{normalization}
183
184Args:
185    {M}
186
187Keyword args:
188    {sym}
189    {dtype}
190    {layout}
191    {device}
192    {requires_grad}
193
194Examples::
195
196    >>> # Generates a symmetric cosine window.
197    >>> torch.signal.windows.cosine(10)
198    tensor([0.1564, 0.4540, 0.7071, 0.8910, 0.9877, 0.9877, 0.8910, 0.7071, 0.4540, 0.1564])
199
200    >>> # Generates a periodic cosine window.
201    >>> torch.signal.windows.cosine(10, sym=False)
202    tensor([0.1423, 0.4154, 0.6549, 0.8413, 0.9595, 1.0000, 0.9595, 0.8413, 0.6549, 0.4154])
203""".format(
204        **window_common_args,
205    ),
206)
207def cosine(
208        M: int,
209        *,
210        sym: bool = True,
211        dtype: Optional[torch.dtype] = None,
212        layout: torch.layout = torch.strided,
213        device: Optional[torch.device] = None,
214        requires_grad: bool = False
215) -> Tensor:
216    if dtype is None:
217        dtype = torch.get_default_dtype()
218
219    _window_function_checks('cosine', M, dtype, layout)
220
221    if M == 0:
222        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
223
224    start = 0.5
225    constant = torch.pi / (M + 1 if not sym and M > 1 else M)
226
227    k = torch.linspace(start=start * constant,
228                       end=(start + (M - 1)) * constant,
229                       steps=M,
230                       dtype=dtype,
231                       layout=layout,
232                       device=device,
233                       requires_grad=requires_grad)
234
235    return torch.sin(k)
236
237
238@_add_docstr(
239    r"""
240Computes a window with a gaussian waveform.
241
242The gaussian window is defined as follows:
243
244.. math::
245    w_n = \exp{\left(-\left(\frac{n}{2\sigma}\right)^2\right)}
246    """,
247    r"""
248
249{normalization}
250
251Args:
252    {M}
253
254Keyword args:
255    std (float, optional): the standard deviation of the gaussian. It controls how narrow or wide the window is.
256        Default: 1.0.
257    {sym}
258    {dtype}
259    {layout}
260    {device}
261    {requires_grad}
262
263Examples::
264
265    >>> # Generates a symmetric gaussian window with a standard deviation of 1.0.
266    >>> torch.signal.windows.gaussian(10)
267    tensor([4.0065e-05, 2.1875e-03, 4.3937e-02, 3.2465e-01, 8.8250e-01, 8.8250e-01, 3.2465e-01, 4.3937e-02, 2.1875e-03, 4.0065e-05])
268
269    >>> # Generates a periodic gaussian window and standard deviation equal to 0.9.
270    >>> torch.signal.windows.gaussian(10, sym=False,std=0.9)
271    tensor([1.9858e-07, 5.1365e-05, 3.8659e-03, 8.4658e-02, 5.3941e-01, 1.0000e+00, 5.3941e-01, 8.4658e-02, 3.8659e-03, 5.1365e-05])
272""".format(
273        **window_common_args,
274    ),
275)
276def gaussian(
277        M: int,
278        *,
279        std: float = 1.0,
280        sym: bool = True,
281        dtype: Optional[torch.dtype] = None,
282        layout: torch.layout = torch.strided,
283        device: Optional[torch.device] = None,
284        requires_grad: bool = False
285) -> Tensor:
286    if dtype is None:
287        dtype = torch.get_default_dtype()
288
289    _window_function_checks('gaussian', M, dtype, layout)
290
291    if std <= 0:
292        raise ValueError(f'Standard deviation must be positive, got: {std} instead.')
293
294    if M == 0:
295        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
296
297    start = -(M if not sym and M > 1 else M - 1) / 2.0
298
299    constant = 1 / (std * sqrt(2))
300
301    k = torch.linspace(start=start * constant,
302                       end=(start + (M - 1)) * constant,
303                       steps=M,
304                       dtype=dtype,
305                       layout=layout,
306                       device=device,
307                       requires_grad=requires_grad)
308
309    return torch.exp(-k ** 2)
310
311
312@_add_docstr(
313    r"""
314Computes the Kaiser window.
315
316The Kaiser window is defined as follows:
317
318.. math::
319    w_n = I_0 \left( \beta \sqrt{1 - \left( {\frac{n - N/2}{N/2}} \right) ^2 } \right) / I_0( \beta )
320
321where ``I_0`` is the zeroth order modified Bessel function of the first kind (see :func:`torch.special.i0`), and
322``N = M - 1 if sym else M``.
323    """,
324    r"""
325
326{normalization}
327
328Args:
329    {M}
330
331Keyword args:
332    beta (float, optional): shape parameter for the window. Must be non-negative. Default: 12.0
333    {sym}
334    {dtype}
335    {layout}
336    {device}
337    {requires_grad}
338
339Examples::
340
341    >>> # Generates a symmetric gaussian window with a standard deviation of 1.0.
342    >>> torch.signal.windows.kaiser(5)
343    tensor([4.0065e-05, 2.1875e-03, 4.3937e-02, 3.2465e-01, 8.8250e-01, 8.8250e-01, 3.2465e-01, 4.3937e-02, 2.1875e-03, 4.0065e-05])
344    >>> # Generates a periodic gaussian window and standard deviation equal to 0.9.
345    >>> torch.signal.windows.kaiser(5, sym=False,std=0.9)
346    tensor([1.9858e-07, 5.1365e-05, 3.8659e-03, 8.4658e-02, 5.3941e-01, 1.0000e+00, 5.3941e-01, 8.4658e-02, 3.8659e-03, 5.1365e-05])
347""".format(
348        **window_common_args,
349    ),
350)
351def kaiser(
352        M: int,
353        *,
354        beta: float = 12.0,
355        sym: bool = True,
356        dtype: Optional[torch.dtype] = None,
357        layout: torch.layout = torch.strided,
358        device: Optional[torch.device] = None,
359        requires_grad: bool = False
360) -> Tensor:
361    if dtype is None:
362        dtype = torch.get_default_dtype()
363
364    _window_function_checks('kaiser', M, dtype, layout)
365
366    if beta < 0:
367        raise ValueError(f'beta must be non-negative, got: {beta} instead.')
368
369    if M == 0:
370        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
371
372    if M == 1:
373        return torch.ones((1,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
374
375    # Avoid NaNs by casting `beta` to the appropriate dtype.
376    beta = torch.tensor(beta, dtype=dtype, device=device)
377
378    start = -beta
379    constant = 2.0 * beta / (M if not sym else M - 1)
380    end = torch.minimum(beta, start + (M - 1) * constant)
381
382    k = torch.linspace(start=start,
383                       end=end,
384                       steps=M,
385                       dtype=dtype,
386                       layout=layout,
387                       device=device,
388                       requires_grad=requires_grad)
389
390    return torch.i0(torch.sqrt(beta * beta - torch.pow(k, 2))) / torch.i0(beta)
391
392
393@_add_docstr(
394    r"""
395Computes the Hamming window.
396
397The Hamming window is defined as follows:
398
399.. math::
400    w_n = \alpha - \beta\ \cos \left( \frac{2 \pi n}{M - 1} \right)
401    """,
402    r"""
403
404{normalization}
405
406Arguments:
407    {M}
408
409Keyword args:
410    {sym}
411    alpha (float, optional): The coefficient :math:`\alpha` in the equation above.
412    beta (float, optional): The coefficient :math:`\beta` in the equation above.
413    {dtype}
414    {layout}
415    {device}
416    {requires_grad}
417
418Examples::
419
420    >>> # Generates a symmetric Hamming window.
421    >>> torch.signal.windows.hamming(10)
422    tensor([0.0800, 0.1876, 0.4601, 0.7700, 0.9723, 0.9723, 0.7700, 0.4601, 0.1876, 0.0800])
423
424    >>> # Generates a periodic Hamming window.
425    >>> torch.signal.windows.hamming(10, sym=False)
426    tensor([0.0800, 0.1679, 0.3979, 0.6821, 0.9121, 1.0000, 0.9121, 0.6821, 0.3979, 0.1679])
427""".format(
428        **window_common_args
429    ),
430)
431def hamming(M: int,
432            *,
433            sym: bool = True,
434            dtype: Optional[torch.dtype] = None,
435            layout: torch.layout = torch.strided,
436            device: Optional[torch.device] = None,
437            requires_grad: bool = False) -> Tensor:
438    return general_hamming(M, sym=sym, dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
439
440
441@_add_docstr(
442    r"""
443Computes the Hann window.
444
445The Hann window is defined as follows:
446
447.. math::
448    w_n = \frac{1}{2}\ \left[1 - \cos \left( \frac{2 \pi n}{M - 1} \right)\right] =
449    \sin^2 \left( \frac{\pi n}{M - 1} \right)
450    """,
451    r"""
452
453{normalization}
454
455Arguments:
456    {M}
457
458Keyword args:
459    {sym}
460    {dtype}
461    {layout}
462    {device}
463    {requires_grad}
464
465Examples::
466
467    >>> # Generates a symmetric Hann window.
468    >>> torch.signal.windows.hann(10)
469    tensor([0.0000, 0.1170, 0.4132, 0.7500, 0.9698, 0.9698, 0.7500, 0.4132, 0.1170, 0.0000])
470
471    >>> # Generates a periodic Hann window.
472    >>> torch.signal.windows.hann(10, sym=False)
473    tensor([0.0000, 0.0955, 0.3455, 0.6545, 0.9045, 1.0000, 0.9045, 0.6545, 0.3455, 0.0955])
474""".format(
475        **window_common_args
476    ),
477)
478def hann(M: int,
479         *,
480         sym: bool = True,
481         dtype: Optional[torch.dtype] = None,
482         layout: torch.layout = torch.strided,
483         device: Optional[torch.device] = None,
484         requires_grad: bool = False) -> Tensor:
485    return general_hamming(M,
486                           alpha=0.5,
487                           sym=sym,
488                           dtype=dtype,
489                           layout=layout,
490                           device=device,
491                           requires_grad=requires_grad)
492
493
494@_add_docstr(
495    r"""
496Computes the Blackman window.
497
498The Blackman window is defined as follows:
499
500.. math::
501    w_n = 0.42 - 0.5 \cos \left( \frac{2 \pi n}{M - 1} \right) + 0.08 \cos \left( \frac{4 \pi n}{M - 1} \right)
502    """,
503    r"""
504
505{normalization}
506
507Arguments:
508    {M}
509
510Keyword args:
511    {sym}
512    {dtype}
513    {layout}
514    {device}
515    {requires_grad}
516
517Examples::
518
519    >>> # Generates a symmetric Blackman window.
520    >>> torch.signal.windows.blackman(5)
521    tensor([-1.4901e-08,  3.4000e-01,  1.0000e+00,  3.4000e-01, -1.4901e-08])
522
523    >>> # Generates a periodic Blackman window.
524    >>> torch.signal.windows.blackman(5, sym=False)
525    tensor([-1.4901e-08,  2.0077e-01,  8.4923e-01,  8.4923e-01,  2.0077e-01])
526""".format(
527        **window_common_args
528    ),
529)
530def blackman(M: int,
531             *,
532             sym: bool = True,
533             dtype: Optional[torch.dtype] = None,
534             layout: torch.layout = torch.strided,
535             device: Optional[torch.device] = None,
536             requires_grad: bool = False) -> Tensor:
537    if dtype is None:
538        dtype = torch.get_default_dtype()
539
540    _window_function_checks('blackman', M, dtype, layout)
541
542    return general_cosine(M, a=[0.42, 0.5, 0.08], sym=sym, dtype=dtype, layout=layout, device=device,
543                          requires_grad=requires_grad)
544
545
546@_add_docstr(
547    r"""
548Computes the Bartlett window.
549
550The Bartlett window is defined as follows:
551
552.. math::
553    w_n = 1 - \left| \frac{2n}{M - 1} - 1 \right| = \begin{cases}
554        \frac{2n}{M - 1} & \text{if } 0 \leq n \leq \frac{M - 1}{2} \\
555        2 - \frac{2n}{M - 1} & \text{if } \frac{M - 1}{2} < n < M \\ \end{cases}
556    """,
557    r"""
558
559{normalization}
560
561Arguments:
562    {M}
563
564Keyword args:
565    {sym}
566    {dtype}
567    {layout}
568    {device}
569    {requires_grad}
570
571Examples::
572
573    >>> # Generates a symmetric Bartlett window.
574    >>> torch.signal.windows.bartlett(10)
575    tensor([0.0000, 0.2222, 0.4444, 0.6667, 0.8889, 0.8889, 0.6667, 0.4444, 0.2222, 0.0000])
576
577    >>> # Generates a periodic Bartlett window.
578    >>> torch.signal.windows.bartlett(10, sym=False)
579    tensor([0.0000, 0.2000, 0.4000, 0.6000, 0.8000, 1.0000, 0.8000, 0.6000, 0.4000, 0.2000])
580""".format(
581        **window_common_args
582    ),
583)
584def bartlett(M: int,
585             *,
586             sym: bool = True,
587             dtype: Optional[torch.dtype] = None,
588             layout: torch.layout = torch.strided,
589             device: Optional[torch.device] = None,
590             requires_grad: bool = False) -> Tensor:
591    if dtype is None:
592        dtype = torch.get_default_dtype()
593
594    _window_function_checks('bartlett', M, dtype, layout)
595
596    if M == 0:
597        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
598
599    if M == 1:
600        return torch.ones((1,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
601
602    start = -1
603    constant = 2 / (M if not sym else M - 1)
604
605    k = torch.linspace(start=start,
606                       end=start + (M - 1) * constant,
607                       steps=M,
608                       dtype=dtype,
609                       layout=layout,
610                       device=device,
611                       requires_grad=requires_grad)
612
613    return 1 - torch.abs(k)
614
615
616@_add_docstr(
617    r"""
618Computes the general cosine window.
619
620The general cosine window is defined as follows:
621
622.. math::
623    w_n = \sum^{M-1}_{i=0} (-1)^i a_i \cos{ \left( \frac{2 \pi i n}{M - 1}\right)}
624    """,
625    r"""
626
627{normalization}
628
629Arguments:
630    {M}
631
632Keyword args:
633    a (Iterable): the coefficients associated to each of the cosine functions.
634    {sym}
635    {dtype}
636    {layout}
637    {device}
638    {requires_grad}
639
640Examples::
641
642    >>> # Generates a symmetric general cosine window with 3 coefficients.
643    >>> torch.signal.windows.general_cosine(10, a=[0.46, 0.23, 0.31], sym=True)
644    tensor([0.5400, 0.3376, 0.1288, 0.4200, 0.9136, 0.9136, 0.4200, 0.1288, 0.3376, 0.5400])
645
646    >>> # Generates a periodic general cosine window wit 2 coefficients.
647    >>> torch.signal.windows.general_cosine(10, a=[0.5, 1 - 0.5], sym=False)
648    tensor([0.0000, 0.0955, 0.3455, 0.6545, 0.9045, 1.0000, 0.9045, 0.6545, 0.3455, 0.0955])
649""".format(
650        **window_common_args
651    ),
652)
653def general_cosine(M, *,
654                   a: Iterable,
655                   sym: bool = True,
656                   dtype: Optional[torch.dtype] = None,
657                   layout: torch.layout = torch.strided,
658                   device: Optional[torch.device] = None,
659                   requires_grad: bool = False) -> Tensor:
660    if dtype is None:
661        dtype = torch.get_default_dtype()
662
663    _window_function_checks('general_cosine', M, dtype, layout)
664
665    if M == 0:
666        return torch.empty((0,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
667
668    if M == 1:
669        return torch.ones((1,), dtype=dtype, layout=layout, device=device, requires_grad=requires_grad)
670
671    if not isinstance(a, Iterable):
672        raise TypeError("Coefficients must be a list/tuple")
673
674    if not a:
675        raise ValueError("Coefficients cannot be empty")
676
677    constant = 2 * torch.pi / (M if not sym else M - 1)
678
679    k = torch.linspace(start=0,
680                       end=(M - 1) * constant,
681                       steps=M,
682                       dtype=dtype,
683                       layout=layout,
684                       device=device,
685                       requires_grad=requires_grad)
686
687    a_i = torch.tensor([(-1) ** i * w for i, w in enumerate(a)], device=device, dtype=dtype, requires_grad=requires_grad)
688    i = torch.arange(a_i.shape[0], dtype=a_i.dtype, device=a_i.device, requires_grad=a_i.requires_grad)
689    return (a_i.unsqueeze(-1) * torch.cos(i.unsqueeze(-1) * k)).sum(0)
690
691
692@_add_docstr(
693    r"""
694Computes the general Hamming window.
695
696The general Hamming window is defined as follows:
697
698.. math::
699    w_n = \alpha - (1 - \alpha) \cos{ \left( \frac{2 \pi n}{M-1} \right)}
700    """,
701    r"""
702
703{normalization}
704
705Arguments:
706    {M}
707
708Keyword args:
709    alpha (float, optional): the window coefficient. Default: 0.54.
710    {sym}
711    {dtype}
712    {layout}
713    {device}
714    {requires_grad}
715
716Examples::
717
718    >>> # Generates a symmetric Hamming window with the general Hamming window.
719    >>> torch.signal.windows.general_hamming(10, sym=True)
720    tensor([0.0800, 0.1876, 0.4601, 0.7700, 0.9723, 0.9723, 0.7700, 0.4601, 0.1876, 0.0800])
721
722    >>> # Generates a periodic Hann window with the general Hamming window.
723    >>> torch.signal.windows.general_hamming(10, alpha=0.5, sym=False)
724    tensor([0.0000, 0.0955, 0.3455, 0.6545, 0.9045, 1.0000, 0.9045, 0.6545, 0.3455, 0.0955])
725""".format(
726        **window_common_args
727    ),
728)
729def general_hamming(M,
730                    *,
731                    alpha: float = 0.54,
732                    sym: bool = True,
733                    dtype: Optional[torch.dtype] = None,
734                    layout: torch.layout = torch.strided,
735                    device: Optional[torch.device] = None,
736                    requires_grad: bool = False) -> Tensor:
737    return general_cosine(M,
738                          a=[alpha, 1. - alpha],
739                          sym=sym,
740                          dtype=dtype,
741                          layout=layout,
742                          device=device,
743                          requires_grad=requires_grad)
744
745
746@_add_docstr(
747    r"""
748Computes the minimum 4-term Blackman-Harris window according to Nuttall.
749
750.. math::
751    w_n = 1 - 0.36358 \cos{(z_n)} + 0.48917 \cos{(2z_n)} - 0.13659 \cos{(3z_n)} + 0.01064 \cos{(4z_n)}
752
753where :math:`z_n = \frac{2 \pi n}{M}`.
754    """,
755    """
756
757{normalization}
758
759Arguments:
760    {M}
761
762Keyword args:
763    {sym}
764    {dtype}
765    {layout}
766    {device}
767    {requires_grad}
768
769References::
770
771    - A. Nuttall, "Some windows with very good sidelobe behavior,"
772      IEEE Transactions on Acoustics, Speech, and Signal Processing, vol. 29, no. 1, pp. 84-91,
773      Feb 1981. https://doi.org/10.1109/TASSP.1981.1163506
774
775    - Heinzel G. et al., "Spectrum and spectral density estimation by the Discrete Fourier transform (DFT),
776      including a comprehensive list of window functions and some new flat-top windows",
777      February 15, 2002 https://holometer.fnal.gov/GH_FFT.pdf
778
779Examples::
780
781    >>> # Generates a symmetric Nutall window.
782    >>> torch.signal.windows.general_hamming(5, sym=True)
783    tensor([3.6280e-04, 2.2698e-01, 1.0000e+00, 2.2698e-01, 3.6280e-04])
784
785    >>> # Generates a periodic Nuttall window.
786    >>> torch.signal.windows.general_hamming(5, sym=False)
787    tensor([3.6280e-04, 1.1052e-01, 7.9826e-01, 7.9826e-01, 1.1052e-01])
788""".format(
789        **window_common_args
790    ),
791)
792def nuttall(
793        M: int,
794        *,
795        sym: bool = True,
796        dtype: Optional[torch.dtype] = None,
797        layout: torch.layout = torch.strided,
798        device: Optional[torch.device] = None,
799        requires_grad: bool = False
800) -> Tensor:
801    return general_cosine(M,
802                          a=[0.3635819, 0.4891775, 0.1365995, 0.0106411],
803                          sym=sym,
804                          dtype=dtype,
805                          layout=layout,
806                          device=device,
807                          requires_grad=requires_grad)
808