class_docstr: Implementation of a multiscale attention block. Compare to a conventional attention
  block, a multiscale attention block optionally supports pooling (either
  before or after qkv projection). If pooling is not used, a multiscale attention
  block is equivalent to a conventional attention block.
  
  ::
                                 Input
                                   |
                  |----------------|-----------------|
                  ↓                ↓                 ↓
                Linear           Linear            Linear
                  &                &                 &
               Pool (Q)         Pool (K)          Pool (V)
                  → -------------- ←                 |
                           ↓                         |
                     MatMul & Scale                  |
                           ↓                         |
                        Softmax                      |
                           → ----------------------- ←
                                       ↓
                                 MatMul & Scale
                                       ↓
                                    DropOut
code: def _qkv_pool(
      self,
      q: torch.Tensor,
      k: torch.Tensor,
      v: torch.Tensor,
      thw_shape: List[int],
  ) -> Tuple[
      torch.Tensor,
      List[int],
      torch.Tensor,
      List[int],
      torch.Tensor,
      List[int],
  ]:
      if self.pool_q is None:
          q_shape = thw_shape
      else:
          q, q_shape = _pre_attention_pool(
              q, [thw_shape[0], thw_shape[1], thw_shape[2]]
          )
          q = nn.functional.gelu(q)
          q = self.pool_q(q)
          q, q_shape = _post_attention_pool(
              q,
              q_shape,
          )
  
      if self.pool_k is None:
          k_shape = thw_shape
      else:
          k, k_shape = _pre_attention_pool(
              k,
              [thw_shape[0], thw_shape[1], thw_shape[2]],
          )
          k = nn.functional.gelu(k)
          k = self.pool_k(k)
          k, k_shape = _post_attention_pool(
              k,
              k_shape,
          )
      if self.pool_v is None:
          v_shape = thw_shape
      else:
          v, v_shape = _pre_attention_pool(
              v,
              [thw_shape[0], thw_shape[1], thw_shape[2]],
          )
          v = nn.functional.gelu(v)
          v = self.pool_v(v)
          v, v_shape = _post_attention_pool(
              v,
              v_shape,
          )
      return q, q_shape, k, k_shape, v, v_shape
  
imports: from typing import List, Optional, Tuple
  import numpy
  import torch
  import torch.fx
  import torch.nn
  from torch.nn.common_types import _size_3_t
  from .drop_path import DropPath
label_desc: Use image node_id 3 for calling the MultiScaleAttention obj's underlying member method code with example usage: obj._qkv_pool(q, k, v, thw_shape) and returns: q,  q_shape,  k,  k_shape,  v,  v_shape