code: """
  The core part of the SOTA model of CPSC2019,
  branched, and has different scope (in terms of dilation) in each branch
  """
  from copy import deepcopy
  from itertools import repeat
  from collections import OrderedDict
  from typing import Union, Optional, Sequence, NoReturn
  
  import numpy as np
  np.set_printoptions(precision=5, suppress=True)
  import torch
  from torch import nn
  from torch import Tensor
  
  from ...cfg import CFG, DEFAULTS
  from ...utils.utils_nn import compute_module_size, SizeMixin
  from ...utils.misc import dict_to_str
  from ...models._nets import (
      Conv_Bn_Activation,
      DownSample,
      NonLocalBlock, SEBlock, GlobalContextBlock,
  )
  
  
  if DEFAULTS.torch_dtype == torch.float64:
      torch.set_default_tensor_type(torch.DoubleTensor)
  
  
  __all__ = [
      "MultiScopicCNN",
      "MultiScopicBasicBlock",
      "MultiScopicBranch",
  ]
  
  
  class MultiScopicBasicBlock(SizeMixin, nn.Sequential):
      """ finished, checked,
  
      basic building block of the CNN part of the SOTA model
      from CPSC2019 challenge (entry 0416)
  
      (conv -> activation) * N --> bn --> down_sample
      """
      __DEBUG__ = False
      __name__ = "MultiScopicBasicBlock"
  
      def __init__(self,
                   in_channels:int,
                   scopes:Sequence[int],
                   num_filters:Union[int,Sequence[int]],
                   filter_lengths:Union[int,Sequence[int]],
                   subsample_length:int,
                   groups:int=1,
                   **config) -> NoReturn:
          """ finished, checked,
  
          Parameters
          ----------
          in_channels: int,
              number of channels in the input
          scopes: sequence of int,
              scopes of the convolutional layers, via `dilation`
          num_filters: int or sequence of int,
              number of filters of the convolutional layer(s)
          filter_lengths: int or sequence of int,
              filter length(s) (kernel size(s)) of the convolutional layer(s)
          subsample_length: int,
              subsample length (ratio) at the last layer of the block
          """
          super().__init__()
          self.__in_channels = in_channels
          self.__scopes = scopes
          self.__num_convs = len(self.__scopes)
          if isinstance(num_filters, int):
              self.__out_channels = list(repeat(num_filters, self.__num_convs))
          else:
              self.__out_channels = num_filters
              assert len(self.__out_channels) == self.__num_convs, \
                  f"`scopes` indicates {self.__num_convs} convolutional layers, while `num_filters` indicates {len(self.__out_channels)}"
          if isinstance(filter_lengths, int):
              self.__filter_lengths = list(repeat(filter_lengths, self.__num_convs))
          else:
              self.__filter_lengths = filter_lengths
              assert len(self.__filter_lengths) == self.__num_convs, \
                  f"`scopes` indicates {self.__num_convs} convolutional layers, while `filter_lengths` indicates {len(self.__filter_lengths)}"
          self.__subsample_length = subsample_length
          self.__groups = groups
          self.config = CFG(deepcopy(config))
  
          conv_in_channels = self.__in_channels
          for idx in range(self.__num_convs):
              self.add_module(
                  f"ca_{idx}",
                  Conv_Bn_Activation(
                      in_channels=conv_in_channels,
                      out_channels=self.__out_channels[idx],
                      kernel_size=self.__filter_lengths[idx],
                      stride=1,
                      dilation=self.__scopes[idx],
                      groups=self.__groups,
                      batch_norm=self.config.batch_norm,
                      # kw_bn=self.config.kw_bn,
                      activation=self.config.activation,
                      kw_activation=self.config.kw_activation,
                      kernel_initializer=self.config.kernel_initializer,
                      kw_initializer=self.config.kw_initializer,
                      bias=self.config.bias,
                  )
              )
              conv_in_channels = self.__out_channels[idx]
          self.add_module(
              "bn",
              nn.BatchNorm1d(self.__out_channels[-1])
          )
          self.add_module(
              "down",
              DownSample(
                  down_scale=self.__subsample_length,
                  in_channels=self.__out_channels[-1],
                  groups=self.__groups,
                  # padding=
                  batch_norm=False,
                  mode=self.config.subsample_mode,
              )
          )
          if self.config.dropout > 0:
              self.add_module(
                  "dropout",
                  nn.Dropout(self.config.dropout, inplace=False)
              )
  
      def forward(self, input:Tensor) -> Tensor:
          """ finished, checked,
  
          Parameters
          ----------
          input: Tensor,
              of shape (batch_size, n_channels, seq_len)
  
          Returns
          -------
          output: Tensor,
              of shape (batch_size, n_channels, seq_len)
          """
          output = super().forward(input)
          return output
  
      def compute_output_shape(self, seq_len:Optional[int]=None, batch_size:Optional[int]=None) -> Sequence[Union[int, None]]:
          """ finished, checked,
  
          Parameters
          ----------
          seq_len: int,
              length of the 1d sequence
          batch_size: int, optional,
              the batch size, can be None
  
          Returns
          -------
          output_shape: sequence,
              the output shape of this block, given `seq_len` and `batch_size`
          """
          _seq_len = seq_len
          for idx, module in enumerate(self):
              if idx == self.__num_convs:  # bn layer
                  continue
              elif self.config.dropout > 0 and idx == len(self)-1:  # dropout layer
                  continue
              output_shape = module.compute_output_shape(_seq_len, batch_size)
              _, _, _seq_len = output_shape
          return output_shape
  
  
  class MultiScopicBranch(SizeMixin, nn.Sequential):
      """ finished, checked,
      
      branch path of the CNN part of the SOTA model
      from CPSC2019 challenge (entry 0416)
      """
      __DEBUG__ = False
      __name__ = "MultiScopicBranch"
  
      def __init__(self,
                   in_channels:int,
                   scopes:Sequence[Sequence[int]],
                   num_filters:Union[Sequence[int],Sequence[Sequence[int]]],
                   filter_lengths:Union[Sequence[int],Sequence[Sequence[int]]],
                   subsample_lengths:Union[int,Sequence[int]],
                   groups:int=1,
                   **config) -> NoReturn:
          """ finished, checked,
  
          Parameters
          ----------
          in_channels: int,
              number of features (channels) of the input
          scopes: sequence of sequences of int,
              scopes (in terms of `dilation`) for the convolutional layers,
              each sequence of int is for one branch
          num_filters: sequence of int, or sequence of sequences of int,
              number of filters for the convolutional layers,
              if is sequence of int,
              then convolutionaly layers in one branch will have the same number of filters
          filter_lengths: sequence of int, or sequence of sequences of int,
              filter length (kernel size) of the convolutional layers,
              if is sequence of int,
              then convolutionaly layers in one branch will have the same filter length
          subsample_lengths: int, or sequence of int,
              subsample length (stride) of the convolutional layers,
              if is sequence of int,
              then convolutionaly layers in one branch will have the same subsample length
          groups: int, default 1,
              connection pattern (of channels) of the inputs and outputs
          config: dict,
              other hyper-parameters, including
              dropout, activation choices, weight initializer, etc.
          """
          super().__init__()
          self.__in_channels = in_channels
          self.__scopes = scopes
          self.__num_blocks = len(self.__scopes)
          self.__num_filters = num_filters
          assert len(self.__num_filters) == self.__num_blocks, \
              f"`scopes` indicates {self.__num_blocks} `MultiScopicBasicBlock`s, while `num_filters` indicates {len(self.__num_filters)}"
          self.__filter_lengths = filter_lengths
          assert len(self.__filter_lengths) == self.__num_blocks, \
              f"`scopes` indicates {self.__num_blocks} `MultiScopicBasicBlock`s, while `filter_lengths` indicates {len(self.__filter_lengths)}"
          if isinstance(subsample_lengths, int):
              self.__subsample_lengths = list(repeat(subsample_lengths, self.__num_blocks))
          else:
              self.__subsample_lengths = filter_lengths
              assert len(self.__subsample_lengths) == self.__num_blocks, \
              f"`scopes` indicates {self.__num_blocks} `MultiScopicBasicBlock`s, while `subsample_lengths` indicates {len(self.__subsample_lengths)}"
          self.__groups = groups
          self.config = CFG(deepcopy(config))
  
          block_in_channels = self.__in_channels
          for idx in range(self.__num_blocks):
              self.add_module(
                  f"block_{idx}",
                  MultiScopicBasicBlock(
                      in_channels=block_in_channels,
                      scopes=self.__scopes[idx],
                      num_filters=self.__num_filters[idx],
                      filter_lengths=self.__filter_lengths[idx],
                      subsample_length=self.__subsample_lengths[idx],
                      groups=self.__groups,
                      dropout=self.config.dropouts[idx],
                      **(self.config.block)
                  )
              )
              block_in_channels = self.__num_filters[idx]
  
      def forward(self, input:Tensor) -> Tensor:
          """ finished, checked,
  
          Parameters
          ----------
          input: Tensor,
              of shape (batch_size, n_channels, seq_len)
  
          Returns
          -------
          output: Tensor,
              of shape (batch_size, n_channels, seq_len)
          """
          output = super().forward(input)
          return output
  
      def compute_output_shape(self, seq_len:Optional[int]=None, batch_size:Optional[int]=None) -> Sequence[Union[int, None]]:
          """ finished, checked,
  
          Parameters
          ----------
          seq_len: int,
              length of the 1d sequence
          batch_size: int, optional,
              the batch size, can be None
  
          Returns
          -------
          output_shape: sequence,
              the output shape of this block, given `seq_len` and `batch_size`
          """
          _seq_len = seq_len
          for idx, module in enumerate(self):
              output_shape = module.compute_output_shape(_seq_len, batch_size)
              _, _, _seq_len = output_shape
          return output_shape
  
  
  class MultiScopicCNN(SizeMixin, nn.Module):
      """ finished, checked,
  
      CNN part of the SOTA model from CPSC2019 challenge (entry 0416)
      """
      __DEBUG__ = False
      __name__ = "MultiScopicCNN"
  
      def __init__(self, in_channels:int, **config) -> NoReturn:
          """ finished, checked,
  
          Parameters
          ----------
          in_channels: int,
              number of channels in the input
          config: dict,
              other hyper-parameters of the Module, ref. corresponding config file
              key word arguments that have to be set:
              scopes: sequence of sequences of sequences of int,
                  scopes (in terms of dilation) of each convolution
              num_filters: sequence of sequences (of int or of sequences of int),
                  number of filters of the convolutional layers,
                  with granularity to each block of each branch,
                  or to each convolution of each block of each branch
              filter_lengths: sequence of sequences (of int or of sequences of int),
                  filter length(s) (kernel size(s)) of the convolutions,
                  with granularity to each block of each branch,
                  or to each convolution of each block of each branch
              subsample_lengths: sequence of int or sequence of sequences of int,
                  subsampling length(s) (ratio(s)) of all blocks,
                  with granularity to each branch or to each block of each branch,
                  each subsamples after the last convolution of each block
              dropouts: sequence of int or sequence of sequences of int,
                  dropout rates of all blocks,
                  with granularity to each branch or to each block of each branch,
                  each dropouts at the last of each block
              groups: int,
                  connection pattern (of channels) of the inputs and outputs
              block: dict,
                  other parameters that can be set for the building blocks
              for a full list of configurable parameters, ref. corr. config file
          """
          super().__init__()
          self.__in_channels = in_channels
          self.config = CFG(deepcopy(config))
          self.__scopes = self.config.scopes
          self.__num_branches = len(self.__scopes)
  
          if self.__DEBUG__:
              print(f"configuration of {self.__name__} is as follows\n{dict_to_str(self.config)}")
  
          self.branches = nn.ModuleDict()
          for idx in range(self.__num_branches):
              self.branches[f"branch_{idx}"] = \
                  MultiScopicBranch(
                      in_channels=self.__in_channels,
                      scopes=self.__scopes[idx],
                      num_filters=self.config.num_filters[idx],
                      filter_lengths=self.config.filter_lengths[idx],
                      subsample_lengths=self.config.subsample_lengths[idx],
                      groups=self.config.groups,
                      dropouts=self.config.dropouts[idx],
                      block=self.config.block,  # a dict
                  )
  
      def forward(self, input:Tensor) -> Tensor:
          """ finished, checked,
          
          Parameters
          ----------
          input: Tensor,
              of shape (batch_size, n_channels, seq_len)
  
          Returns
          -------
          output: Tensor,
              of shape (batch_size, n_channels, seq_len)
          """
          branch_out = OrderedDict()
          for idx in range(self.__num_branches):
              key = f"branch_{idx}"
              branch_out[key] = self.branches[key].forward(input)
          output = torch.cat(
              [branch_out[f"branch_{idx}"] for idx in range(self.__num_branches)],
              dim=1,  # along channels
          )
          return output
      
      def compute_output_shape(self, seq_len:Optional[int]=None, batch_size:Optional[int]=None) -> Sequence[Union[int, None]]:
          """ finished, checked,
  
          Parameters
          ----------
          seq_len: int,
              length of the 1d sequence
          batch_size: int, optional,
              the batch size, can be None
  
          Returns
          -------
          output_shape: sequence,
              the output shape of this block, given `seq_len` and `batch_size`
          """
          out_channels = 0
          for idx in range(self.__num_branches):
              key = f"branch_{idx}"
              _, _branch_oc, _seq_len = \
                  self.branches[key].compute_output_shape(seq_len, batch_size)
              out_channels += _branch_oc
          output_shape = (batch_size, out_channels, _seq_len)
          return output_shape
  
apis: torch.nn.BatchNorm1d numpy.set_printoptions torch.set_default_tensor_type torch.nn.ModuleDict torch.nn.Dropout