class_docstr: An inference block combining multiple perception blocks into one prediction module.
          Conditions on using the InferenceBlock object:
              1. All keys of the perception_blocks dictionary have to be unique
              2. All out_keys used when creating the blocks have to be unique
              3. All block keys in the perception_blocks dict have to sub-strings of all their corresponding out_keys
              4. The given in_keys should be a subset of the inputs of the computational graph
              5. The given out_keys should be a subset of the outputs of the computational graph
  
  :param in_keys: Keys identifying the input tensors.
  :param out_keys: Keys identifying the output tensors.
  :param in_shapes: List of input shapes.
  :param perception_blocks: Dictionary of perception blocks.
class_docstr_tok: ["An","inference","block","combining","multiple","perception","blocks","into","one","prediction","module",".","Conditions","on","using","the","InferenceBlock","object",":","1",".","All","keys","of","the","perception_blocks","dictionary","have","to","be","unique","2",".","All","out_keys","used","when","creating","the","blocks","have","to","be","unique","3",".","All","block","keys","in","the","perception_blocks","dict","have","to","sub-strings","of","all","their","corresponding","out_keys","4",".","The","given","in_keys","should","be","a","subset","of","the","inputs","of","the","computational","graph","5",".","The","given","out_keys","should","be","a","subset","of","the","outputs","of","the","computational","graph",":","param","in_keys",":","Keys","identifying","the","input","tensors",".",":","param","out_keys",":","Keys","identifying","the","output","tensors",".",":","param","in_shapes",":","List","of","input","shapes",".",":","param","perception_blocks",":","Dictionary","of","perception","blocks","."]
code: def __init__(
      self,
      in_keys: Union[str, List[str]],
      out_keys: Union[str, List[str]],
      in_shapes: Union[Sequence[int], List[Sequence[int]]],
      perception_blocks: Dict[str, PerceptionBlock],
  ):
      super().__init__(
          in_keys=in_keys, out_keys=out_keys, in_shapes=in_shapes
      )
      self._test_condition(
          perception_blocks, self.in_keys, self.out_keys
      )
      self.block_keys = list(perception_blocks.keys())
      self.perception_dict = perception_blocks
      self.perception_blocks = nn.ModuleDict(perception_blocks)
      # Copy the in_keys
      self.execution_plan = self._build_execution_graph(
          computed_keys=self.in_keys[:], execution_plan=dict()
      )
      blocks_not_in_execution_graph = list(
          filter(
              lambda bk: bk
              not in sum(self.execution_plan.values(), []),
              self.perception_dict.keys(),
          )
      )
      # Delete unused perception blocks from the inference block
      if len(blocks_not_in_execution_graph) > 0:
          print(
              f"Deleting unused blocks: {blocks_not_in_execution_graph}"
          )
      for block_key in blocks_not_in_execution_graph:
          del self.perception_dict[block_key]
  
imports: import os
  import pickle
  from typing import Union, List, Dict, Optional, Sequence, Tuple
  import networkx
  import torch
  from matplotlib import pyplot
  from torch import nn
  from maze.core.annotations import override
  from maze.perception.blocks.base import PerceptionBlock
label_desc: Use image node_id 1 to create a new InferenceBlock object from inherited base classes: PerceptionBlock with example: obj = InferenceBlock(in_keys, out_keys, in_shapes, perception_blocks)