docstr: DNN_WPE forward function.
  
  Notation:
      B: Batch
      C: Channel
      T: Time or Sequence length
      F: Freq or Some dimension of the feature vector
  
  Args:
      data: (B, T, C, F)
      ilens: (B,)
  Returns:
      enhanced (torch.Tensor or List[torch.Tensor]): (B, T, C, F)
      ilens: (B,)
      masks (torch.Tensor or List[torch.Tensor]): (B, T, C, F)
      power (List[torch.Tensor]): (B, F, T)
code: def forward(
      self,
      data: Union[torch.Tensor, ComplexTensor],
      ilens: torch.LongTensor,
  ) -> Tuple[
      Union[torch.Tensor, ComplexTensor],
      torch.LongTensor,
      Union[torch.Tensor, ComplexTensor],
  ]:
      """DNN_WPE forward function.
  
      Notation:
          B: Batch
          C: Channel
          T: Time or Sequence length
          F: Freq or Some dimension of the feature vector
  
      Args:
          data: (B, T, C, F)
          ilens: (B,)
      Returns:
          enhanced (torch.Tensor or List[torch.Tensor]): (B, T, C, F)
          ilens: (B,)
          masks (torch.Tensor or List[torch.Tensor]): (B, T, C, F)
          power (List[torch.Tensor]): (B, F, T)
      """
      # (B, T, C, F) -> (B, F, C, T)
      data = data.permute(0, 3, 2, 1)
      enhanced = [data for i in range(self.nmask)]
      masks = None
      power = None
  
      for i in range(self.iterations):
          # Calculate power: (..., C, T)
          power = [enh.real**2 + enh.imag**2 for enh in enhanced]
          if i == 0 and self.use_dnn_mask:
              # mask: (B, F, C, T)
              masks, _ = self.mask_est(data, ilens)
              # floor masks to increase numerical stability
              if self.mask_flooring:
                  masks = [
                      m.clamp(min=self.flooring_thres) for m in masks
                  ]
              if self.normalization:
                  # Normalize along T
                  masks = [
                      m / m.sum(dim=-1, keepdim=True) for m in masks
                  ]
              # (..., C, T) * (..., C, T) -> (..., C, T)
              power = [p * masks[i] for i, p in enumerate(power)]
  
          # Averaging along the channel axis: (..., C, T) -> (..., T)
          power = [p.mean(dim=-2).clamp(min=self.eps) for p in power]
  
          # enhanced: (..., C, T) -> (..., C, T)
          # NOTE(kamo): Calculate in double precision
          enhanced = [
              wpe_one_iteration(
                  to_double(data.contiguous()),
                  to_double(p),
                  taps=self.taps,
                  delay=self.delay,
                  inverse_power=self.inverse_power,
              )
              for p in power
          ]
          enhanced = [
              enh.to(dtype=data.dtype).masked_fill(
                  make_pad_mask(ilens, enh.real), 0
              )
              for enh in enhanced
          ]
  
      # (B, F, C, T) -> (B, T, C, F)
      enhanced = [enh.permute(0, 3, 2, 1) for enh in enhanced]
      if masks is not None:
          masks = (
              [m.transpose(-1, -3) for m in masks]
              if self.nmask > 1
              else masks[0].transpose(-1, -3)
          )
      if self.nmask == 1:
          enhanced = enhanced[0]
  
      return enhanced, ilens, masks, power
  
file_path: espnet/espnet2/enh/layers/dnn_wpe.py
imports: from typing import Tuple, Union
  import torch
  from torch_complex.tensor import ComplexTensor
  from espnet2.enh.layers.complex_utils import to_double, to_float
  from espnet2.enh.layers.mask_estimator import MaskEstimator
  from espnet2.enh.layers.wpe import wpe_one_iteration
  from espnet.nets.pytorch_backend.nets_utils import make_pad_mask
label_desc: Use image node_id 2 for calling the DNN_WPE obj's underlying member method code with example usage: obj.forward(data, ilens) and returns: enhanced,  ilens,  masks,  power