code: def calculate_sdr_batch(
      estimate: torch.Tensor,
      target: torch.Tensor,
      input_length: Optional[torch.Tensor] = None,
      mask: Optional[torch.Tensor] = None,
      scale_invariant: bool = False,
      convolution_invariant: bool = False,
      convolution_filter_length: Optional[int] = 512,
      remove_mean: bool = True,
      sdr_max: Optional[float] = None,
      eps: float = 1e-8,
  ) -> torch.Tensor:
      """Calculate signal-to-distortion ratio per channel.
  
          SDR = 10 * log10( ||t||_2^2 / (||e-t||_2^2 + alpha * ||t||^2)
  
      where
          alpha = 10^(-sdr_max/10)
  
      Optionally, use scale- or convolution- invariant target signal.
  
      Args:
          estimate: estimated signal, shape (B, C, T)
          target: target signal, shape (B, C, T)
          input_length: Optional, length of valid samples, shape (B,)
          mask: Optional, temporal mask, shape (B, T)
          scale_invariant: Use scale invariant SDR
          convolution_invariant: Use convolution invariant SDR
          convolution_filter_length: Filter length for convolution invariant SDR
          remove_mean: If True, mean will be removed before calculating SDR
          eps: Small regularization constant
  
      Returns:
          SDR in dB for each channel, shape (B, C)
      """
      if scale_invariant and convolution_invariant:
          raise ValueError(
              f"Arguments scale_invariant and convolution_invariant cannot be used simultaneously."
          )
  
      assert (
          estimate.shape == target.shape
      ), f"Estimate shape ({estimate.shape}) not matching target shape ({target.shape})"
  
      if input_length is not None:
          if mask is not None:
              raise RuntimeError(
                  "Argument `input_length` is mutually exclusive with `mask`. Both cannot be used at the same time."
              )
  
          # Construct a binary mask
          mask = make_seq_mask_like(
              lengths=input_length,
              like=estimate,
              time_dim=-1,
              valid_ones=True,
          ).squeeze(1)
  
      if remove_mean:
          estimate = estimate - temporal_mean(
              estimate, mask=mask, keepdim=True, eps=eps
          )
          target = target - temporal_mean(
              target, mask=mask, keepdim=True, eps=eps
          )
  
      if scale_invariant or (
          convolution_invariant and convolution_filter_length == 1
      ):
          target = scale_invariant_target(
              estimate=estimate, target=target, mask=mask, eps=eps
          )
      elif convolution_invariant:
          target = convolution_invariant_target(
              estimate=estimate,
              target=target,
              mask=mask,
              filter_length=convolution_filter_length,
              eps=eps,
          )
  
      distortion = estimate - target
  
      target_pow = temporal_mean(
          torch.abs(target) ** 2, mask=mask, eps=eps
      )
      distortion_pow = temporal_mean(
          torch.abs(distortion) ** 2, mask=mask, eps=eps
      )
  
      if sdr_max is not None:
          distortion_pow = (
              distortion_pow + 10 ** (-sdr_max / 10) * target_pow
          )
  
      sdr = target_pow / (distortion_pow + eps)
      sdr = 10 * torch.log10(sdr + eps)
  
      return sdr
  
file_path: NeMo/nemo/collections/asr/losses/audio_losses.py
imports: import math
  from typing import List, Optional
  import numpy
  import torch
  from nemo.collections.asr.parts.preprocessing.features import make_seq_mask_like
  from nemo.collections.asr.parts.utils.audio_utils import toeplitz
  from nemo.core.classes import Loss, Typing, typecheck
  from nemo.core.neural_types import AudioSignal, LengthsType, LossType, MaskType, NeuralType
  from nemo.utils import logging
label_desc: Use image node_id 4 for calling a global function with example usage: calculate_sdr_batch(estimate, target, input_length, mask, scale_invariant, convolution_invariant, convolution_filter_length, remove_mean, sdr_max, eps) and returns: sdr