class_docstr: The base class for `TopologyInstance` and `TopologySearch`.
  
  Args:
      arch_code: `[arch_code_a, arch_code_c]`, numpy arrays. The architecture codes defining the model.
          For example, for a ``num_depths=4, num_blocks=12`` search space:
  
          - `arch_code_a` is a 12x10 (10 paths) binary matrix representing if a path is activated.
          - `arch_code_c` is a 12x10x5 (5 operations) binary matrix representing if a cell operation is used.
          - `arch_code` in ``__init__()`` is used for creating the network and remove unused network blocks. If None,
  
          all paths and cells operations will be used, and must be in the searching stage (is_search=True).
      channel_mul: adjust intermediate channel number, default is 1.
      cell: operation of each node.
      num_blocks: number of blocks (depth in the horizontal direction) of the DiNTS search space.
      num_depths: number of image resolutions of the DiNTS search space: 1, 1/2, 1/4 ... in each dimension.
      use_downsample: use downsample in the stem. If False, the search space will be in resolution [1, 1/2, 1/4, 1/8],
          if True, the search space will be in resolution [1/2, 1/4, 1/8, 1/16].
      device: `'cpu'`, `'cuda'`, or device ID.
  
  
  Predefined variables:
      `filter_nums`: default to 32. Double the number of channels after downsample.
      topology related variables:
  
          - `arch_code2in`: path activation to its incoming node index (resolution). For depth = 4,
            arch_code2in = [0, 1, 0, 1, 2, 1, 2, 3, 2, 3]. The first path outputs from node 0 (top resolution),
            the second path outputs from node 1 (second resolution in the search space),
            the third path outputs from node 0, etc.
          - `arch_code2ops`: path activation to operations of upsample 1, keep 0, downsample -1. For depth = 4,
            arch_code2ops = [0, 1, -1, 0, 1, -1, 0, 1, -1, 0]. The first path does not change
            resolution, the second path perform upsample, the third perform downsample, etc.
          - `arch_code2out`: path activation to its output node index.
            For depth = 4, arch_code2out = [0, 0, 1, 1, 1, 2, 2, 2, 3, 3],
            the first and second paths connects to node 0 (top resolution), the 3,4,5 paths connects to node 1, etc.
class_docstr_tok: ["The","base","class","for","`","TopologyInstance","`","and","`","TopologySearch","`",".","Args",":","arch_code",":","`","[","arch_code_a",",","arch_code_c","]","`",",","numpy","arrays",".","The","architecture","codes","defining","the","model",".","For","example",",","for","a","``","num_depths=4",",","num_blocks=12","``","search","space",":","-","`","arch_code_a","`","is","a","12x10","(","10","paths",")","binary","matrix","representing","if","a","path","is","activated",".","-","`","arch_code_c","`","is","a","12x10x5","(","5","operations",")","binary","matrix","representing","if","a","cell","operation","is","used",".","-","`","arch_code","`","in","``","__init__","(",")","``","is","used","for","creating","the","network","and","remove","unused","network","blocks",".","If","None",",","all","paths","and","cells","operations","will","be","used",",","and","must","be","in","the","searching","stage","(","is_search=True",")",".","channel_mul",":","adjust","intermediate","channel","number",",","default","is","1.","cell",":","operation","of","each","node",".","num_blocks",":","number","of","blocks","(","depth","in","the","horizontal","direction",")","of","the","DiNTS","search","space",".","num_depths",":","number","of","image","resolutions","of","the","DiNTS","search","space",":","1",",","1\/2",",","1\/4","...","in","each","dimension",".","use_downsample",":","use","downsample","in","the","stem",".","If","False",",","the","search","space","will","be","in","resolution","[","1",",","1\/2",",","1\/4",",","1\/8","]",",","if","True",",","the","search","space","will","be","in","resolution","[","1\/2",",","1\/4",",","1\/8",",","1\/16","]",".","device",":","`","'cpu","'","`",",","`","'cuda","'","`",",","or","device","ID",".","Predefined","variables",":","`","filter_nums","`",":","default","to","32",".","Double","the","number","of","channels","after","downsample",".","topology","related","variables",":","-","`","arch_code2in","`",":","path","activation","to","its","incoming","node","index","(","resolution",")",".","For","depth","=","4",",","arch_code2in","=","[","0",",","1",",","0",",","1",",","2",",","1",",","2",",","3",",","2",",","3","]",".","The","first","path","outputs","from","node","0","(","top","resolution",")",",","the","second","path","outputs","from","node","1","(","second","resolution","in","the","search","space",")",",","the","third","path","outputs","from","node","0",",","etc",".","-","`","arch_code2ops","`",":","path","activation","to","operations","of","upsample","1",",","keep","0",",","downsample","-1",".","For","depth","=","4",",","arch_code2ops","=","[","0",",","1",",","-1",",","0",",","1",",","-1",",","0",",","1",",","-1",",","0","]",".","The","first","path","does","not","change","resolution",",","the","second","path","perform","upsample",",","the","third","perform","downsample",",","etc",".","-","`","arch_code2out","`",":","path","activation","to","its","output","node","index",".","For","depth","=","4",",","arch_code2out","=","[","0",",","0",",","1",",","1",",","1",",","2",",","2",",","2",",","3",",","3","]",",","the","first","and","second","paths","connects","to","node","0","(","top","resolution",")",",","the","3,4,5","paths","connects","to","node","1",",","etc","."]
code: def __init__(
      self,
      arch_code: list | None = None,
      channel_mul: float = 1.0,
      cell=Cell,
      num_blocks: int = 6,
      num_depths: int = 3,
      spatial_dims: int = 3,
      act_name: tuple | str = "RELU",
      norm_name: tuple | str = ("INSTANCE", {"affine": True}),
      use_downsample: bool = True,
      device: str = "cpu",
  ):
      super().__init__()
  
      n_feats = tuple([32 * (2**_i) for _i in range(num_depths + 1)])
      self.filter_nums = [
          int(n_feat * channel_mul) for n_feat in n_feats
      ]
  
      self.num_blocks = num_blocks
      self.num_depths = num_depths
      print(
          "{} - Length of input patch is recommended to be a multiple of {:d}.".format(
              datetime.datetime.now(),
              2 ** (num_depths + int(use_downsample)),
          )
      )
  
      self._spatial_dims = spatial_dims
      self._act_name = act_name
      self._norm_name = norm_name
      self.use_downsample = use_downsample
      self.device = device
      self.num_cell_ops = 0
      if self._spatial_dims == 2:
          self.num_cell_ops = len(cell.OPS2D)
      elif self._spatial_dims == 3:
          self.num_cell_ops = len(cell.OPS3D)
  
      # Calculate predefined parameters for topology search and decoding
      arch_code2in, arch_code2out = [], []
      for i in range(Cell.DIRECTIONS * self.num_depths - 2):
          arch_code2in.append(
              (i + 1) // Cell.DIRECTIONS - 1 + (i + 1) % Cell.DIRECTIONS
          )
      arch_code2ops = ([-1, 0, 1] * self.num_depths)[1:-1]
      for m in range(self.num_depths):
          arch_code2out.extend([m, m, m])
      arch_code2out = arch_code2out[1:-1]
      self.arch_code2in = arch_code2in
      self.arch_code2ops = arch_code2ops
      self.arch_code2out = arch_code2out
  
      # define NAS search space
      if arch_code is None:
          arch_code_a = torch.ones(
              (self.num_blocks, len(self.arch_code2out))
          ).to(self.device)
          arch_code_c = torch.ones(
              (
                  self.num_blocks,
                  len(self.arch_code2out),
                  self.num_cell_ops,
              )
          ).to(self.device)
      else:
          arch_code_a = torch.from_numpy(arch_code[0]).to(self.device)
          arch_code_c = F.one_hot(
              torch.from_numpy(arch_code[1]).to(torch.int64),
              self.num_cell_ops,
          ).to(self.device)
  
      self.arch_code_a = arch_code_a
      self.arch_code_c = arch_code_c
      # define cell operation on each path
      self.cell_tree = nn.ModuleDict()
      for blk_idx in range(self.num_blocks):
          for res_idx in range(len(self.arch_code2out)):
              if self.arch_code_a[blk_idx, res_idx] == 1:
                  self.cell_tree[str((blk_idx, res_idx))] = cell(
                      self.filter_nums[
                          self.arch_code2in[res_idx]
                          + int(use_downsample)
                      ],
                      self.filter_nums[
                          self.arch_code2out[res_idx]
                          + int(use_downsample)
                      ],
                      self.arch_code2ops[res_idx],
                      self.arch_code_c[blk_idx, res_idx],
                      self._spatial_dims,
                      self._act_name,
                      self._norm_name,
                  )
  
imports: from __future__ import annotations
  import datetime
  import warnings
  from typing import Optional
  import numpy
  import torch
  import torch.nn
  import torch.nn.functional
  from monai.networks.blocks.dints_block import ActiConvNormBlock, FactorizedIncreaseBlock, FactorizedReduceBlock, P3DActiConvNormBlock
  from monai.networks.layers.factories import Conv
  from monai.networks.layers.utils import get_act_layer, get_norm_layer
  from monai.utils import optional_import
label_desc: Use image node_id 1 to create a new TopologyConstruction object from inherited base classes: nn with example: obj = TopologyConstruction(arch_code, channel_mul, cell, num_blocks, num_depths, spatial_dims, act_name, norm_name, use_downsample, device)