def plv_time(x, forward_fourier=True, epsilon: float = 1e-6):
    """Compute the Phase Locking Value (PLV) metric in the time domain.

    The Phase Locking Value (PLV) is a measure of the synchronization between
    different channels by evaluating the consistency of phase differences
    over time. It ranges from 0 (no synchronization) to 1 (perfect
    synchronization) [1]_.

    Parameters
    ----------
    x : torch.Tensor
        Input tensor containing the signal data.
        - If `forward_fourier` is `True`, the shape should be `(..., channels, time)`.
        - If `forward_fourier` is `False`, the shape should be `(..., channels, freqs, 2)`,
          where the last dimension represents the real and imaginary parts.
    forward_fourier : bool, optional
        Specifies the format of the input tensor `x`.
        - If `True`, `x` is assumed to be in the time domain.
        - If `False`, `x` is assumed to be in the Fourier domain with separate real and
          imaginary components.
        Default is `True`.
    epsilon : float, default 1e-6
        Small numerical value to ensure positivity constraint on the complex part

    Returns
    -------
    plv : torch.Tensor
        The Phase Locking Value matrix with shape `(..., channels, channels)`. Each
        element `[i, j]` represents the PLV between channel `i` and channel `j`.

    References
    ----------
    [1] Lachaux, J. P., Rodriguez, E., Martinerie, J., & Varela, F. J. (1999).
        Measuring phase synchrony in brain signals. Human brain mapping,
        8(4), 194-208.
    """
    # Compute the analytic signal using the Hilbert transform.
    # x_a has separate real and imaginary parts.
    analytic_signal = hilbert_freq(x, forward_fourier)
    # Calculate the amplitude (magnitude) of the analytic signal.
    # Adding a small epsilon (1e-6) to avoid division by zero.
    amplitude = torch.sqrt(
        analytic_signal[..., 0] ** 2 + analytic_signal[..., 1] ** 2 + 1e-6
    )
    # Normalize the analytic signal to obtain unit vectors (phasors).
    unit_phasor = analytic_signal / amplitude.unsqueeze(-1)

    # Compute the real part of the outer product between phasors of
    # different channels.
    real_real = torch.matmul(unit_phasor[..., 0], unit_phasor[..., 0].transpose(-2, -1))

    # Compute the imaginary part of the outer product between phasors of
    # different channels.
    imag_imag = torch.matmul(unit_phasor[..., 1], unit_phasor[..., 1].transpose(-2, -1))

    # Compute the cross-terms for the real and imaginary parts.
    real_imag = torch.matmul(unit_phasor[..., 0], unit_phasor[..., 1].transpose(-2, -1))
    imag_real = torch.matmul(unit_phasor[..., 1], unit_phasor[..., 0].transpose(-2, -1))

    # Combine the real and imaginary parts to form the complex correlation.
    correlation_real = real_real + imag_imag
    correlation_imag = real_imag - imag_real

    # Determine the number of time points (or frequency bins if in Fourier domain).
    time = amplitude.shape[-1]

    # Calculate the PLV by averaging the magnitude of the complex correlation over time.
    # epsilon is small numerical value to ensure positivity constraint on the complex part
    plv_matrix = (
        1 / time * torch.sqrt(correlation_real**2 + correlation_imag**2 + epsilon)
    )

    return plv_matrix
Compute the Phase Locking Value (PLV) metric in the time domain.

    The Phase Locking Value (PLV) is a measure of the synchronization between
    different channels by evaluating the consistency of phase differences
    over time. It ranges from 0 (no synchronization) to 1 (perfect
    synchronization) [1]_.

    Parameters
    ----------
    x : torch.Tensor
        Input tensor containing the signal data.
        - If `forward_fourier` is `True`, the shape should be `(..., channels, time)`.
        - If `forward_fourier` is `False`, the shape should be `(..., channels, freqs, 2)`,
          where the last dimension represents the real and imaginary parts.
    forward_fourier : bool, optional
        Specifies the format of the input tensor `x`.
        - If `True`, `x` is assumed to be in the time domain.
        - If `False`, `x` is assumed to be in the Fourier domain with separate real and
          imaginary components.
        Default is `True`.
    epsilon : float, default 1e-6
        Small numerical value to ensure positivity constraint on the complex part

    Returns
    -------
    plv : torch.Tensor
        The Phase Locking Value matrix with shape `(..., channels, channels)`. Each
        element `[i, j]` represents the PLV between channel `i` and channel `j`.

    References
    ----------
    [1] Lachaux, J. P., Rodriguez, E., Martinerie, J., & Varela, F. J. (1999).
        Measuring phase synchrony in brain signals. Human brain mapping,
        8(4), 194-208.
    
braindecode/functional/functions.py