def hilbert_freq(x, forward_fourier=True):
    r"""
    Compute the Hilbert transform using PyTorch, separating the real and
    imaginary parts.

    The analytic signal :math:`x_a(t)` of a real-valued signal :math:`x(t)`
    is defined as:

    .. math::

        x_a(t) = x(t) + i y(t) = \mathcal{F}^{-1} \{ U(f) \mathcal{F}\{x(t)\} \}

    where:
    - :math:`\mathcal{F}` is the Fourier transform,
    - :math:`U(f)` is the unit step function,
    - :math:`y(t)` is the Hilbert transform of :math:`x(t)`.


    Parameters
    ----------
    input : torch.Tensor
        Input tensor. The expected shape depends on the `forward_fourier` parameter:

        - If `forward_fourier` is True:
            (..., seq_len)
        - If `forward_fourier` is False:
            (..., seq_len / 2 + 1, 2)

    forward_fourier : bool, optional
        Determines the format of the input tensor.
        - If True, the input is in the forward Fourier domain.
        - If False, the input contains separate real and imaginary parts.
        Default is True.

    Returns
    -------
    torch.Tensor
        Output tensor with shape (..., seq_len, 2), where the last dimension represents
        the real and imaginary parts of the Hilbert transform.

    Examples
    --------
    >>> import torch
    >>> input = torch.randn(10, 100)  # Example input tensor
    >>> output = hilbert_transform(input)
    >>> print(output.shape)
    torch.Size([10, 100, 2])

    Notes
    -----
    The implementation is matching scipy implementation, but using torch.
    https://github.com/scipy/scipy/blob/v1.14.1/scipy/signal/_signaltools.py#L2287-L2394

    """
    if forward_fourier:
        x = torch.fft.rfft(x, norm=None, dim=-1)
        x = torch.view_as_real(x)
    x = x * 2.0
    x[..., 0, :] = x[..., 0, :] / 2.0  # Don't multiply the DC-term by 2
    x = F.pad(
        x, [0, 0, 0, x.shape[-2] - 2]
    )  # Fill Fourier coefficients to retain shape
    x = torch.view_as_complex(x)
    x = torch.fft.ifft(x, norm=None, dim=-1)  # returns complex signal
    x = torch.view_as_real(x)

    return x

    Compute the Hilbert transform using PyTorch, separating the real and
    imaginary parts.

    The analytic signal :math:`x_a(t)` of a real-valued signal :math:`x(t)`
    is defined as:

    .. math::

        x_a(t) = x(t) + i y(t) = \mathcal{F}^{-1} \{ U(f) \mathcal{F}\{x(t)\} \}

    where:
    - :math:`\mathcal{F}` is the Fourier transform,
    - :math:`U(f)` is the unit step function,
    - :math:`y(t)` is the Hilbert transform of :math:`x(t)`.


    Parameters
    ----------
    input : torch.Tensor
        Input tensor. The expected shape depends on the `forward_fourier` parameter:

        - If `forward_fourier` is True:
            (..., seq_len)
        - If `forward_fourier` is False:
            (..., seq_len / 2 + 1, 2)

    forward_fourier : bool, optional
        Determines the format of the input tensor.
        - If True, the input is in the forward Fourier domain.
        - If False, the input contains separate real and imaginary parts.
        Default is True.

    Returns
    -------
    torch.Tensor
        Output tensor with shape (..., seq_len, 2), where the last dimension represents
        the real and imaginary parts of the Hilbert transform.

    Examples
    --------
    >>> import torch
    >>> input = torch.randn(10, 100)  # Example input tensor
    >>> output = hilbert_transform(input)
    >>> print(output.shape)
    torch.Size([10, 100, 2])

    Notes
    -----
    The implementation is matching scipy implementation, but using torch.
    https://github.com/scipy/scipy/blob/v1.14.1/scipy/signal/_signaltools.py#L2287-L2394

    
braindecode/functional/functions.py