class_docstr: Base class for all adapter methods that support composition.
  
  Make sure the 'adapter_modules_name' and 'supported_compositions' attributes as well as all abstract methods are
  overriden in derived classes. 'allow_multi_parallelize' can be set to True to allow inputs to be parallelized
  independently multiple times. This is useful when there are multiple parallel input flows through an adapter layer
  (e.g. in LoRA).
class_docstr_tok: ["Base","class","for","all","adapter","methods","that","support","composition",".","Make","sure","the","'adapter_modules_name","'","and","'supported_compositions","'","attributes","as","well","as","all","abstract","methods","are","overriden","in","derived","classes",".","'allow_multi_parallelize","'","can","be","set","to","True","to","allow","inputs","to","be","parallelized","independently","multiple","times",".","This","is","useful","when","there","are","multiple","parallel","input","flows","through","an","adapter","layer","(","e.g",".","in","LoRA",")","."]
docstr: For parallel execution of the adapters on the same input. This means that the input is repeated N times before
  feeding it to the adapters (where N is the number of adapters).
docstr_tok: ["For","parallel","execution","of","the","adapters","on","the","same","input",".","This","means","that","the","input","is","repeated","N","times","before","feeding","it","to","the","adapters","(","where","N","is","the","number","of","adapters",")","."]
code: def compose_parallel(
      self, adapter_setup: Parallel, state: NamedTuple, lvl: int = 0
  ):
      """
      For parallel execution of the adapters on the same input. This means that the input is repeated N times before
      feeding it to the adapters (where N is the number of adapters).
      """
  
      context = ForwardContext.get_context()
      if not context.adapters_parallelized:
          orig_batch_size = self._bsz(state)
          state = self.repeat(state, adapter_setup.parallel_channels)
          context.adapters_parallelized = True
          context.original_batch_size = orig_batch_size
      else:
          bsz = self._bsz(state)
          # If the input was already parallelized, we can parallelize it again.
          # This is useful e.g. for LoRA, where attention matrices are parallelized independently.
          if self.allow_multi_parallelize and bsz == getattr(
              context, "original_batch_size", -1
          ):
              state = self.repeat(
                  state, adapter_setup.parallel_channels
              )
              orig_batch_size = bsz
          # The base model should handle replication of input.
          # Therefore, we assume the (replicated) input batch to be divisible by the number of parallel channels.
          elif bsz % adapter_setup.parallel_channels != 0:
              raise ValueError(
                  "The total input batch size in a Parallel adapter block must be divisible by the number of"
                  " parallel channels."
              )
          else:
              orig_batch_size = bsz // adapter_setup.parallel_channels
  
      state = self.pre_block(adapter_setup, state)
  
      # sequentially feed different parts of the blown-up batch into different adapters
      children_states = []
      for i, child in enumerate(adapter_setup):
          if isinstance(child, AdapterCompositionBlock):
              self.check_composition_valid(adapter_setup, child, lvl)
              composition_func = self.composition_to_func_map[
                  type(child)
              ]
              child_state = composition_func(
                  child,
                  self.vslice(
                      state,
                      slice(
                          i * orig_batch_size, (i + 1) * orig_batch_size
                      ),
                  ),
                  lvl=lvl + 1,
              )
              children_states.append(child_state)
          elif child in self.adapter_modules:
              child_state = self.compose_single(
                  child,
                  self.vslice(
                      state,
                      slice(
                          i * orig_batch_size, (i + 1) * orig_batch_size
                      ),
                  ),
                  lvl=lvl + 1,
              )
              children_states.append(child_state)
          else:
              children_states.append(
                  self.vslice(
                      state,
                      slice(
                          i * orig_batch_size, (i + 1) * orig_batch_size
                      ),
                  )
              )
  
      # concatenate all outputs and return
      state = self.pad_and_concat(children_states)
      return state
  
imports: from abc import ABCMeta, abstractmethod
  from typing import Collection, Dict, List, NamedTuple, Union
  import numpy
  import torch
  from torch import nn
  from ..composition import ALLOWED_NESTINGS, AdapterCompositionBlock, Average, BatchSplit, Fuse, Parallel, Split, Stack
  from ..context import AdapterSetup, ForwardContext
label_desc: Use image node_id 15 for calling the ComposableAdapterLayerBase obj's underlying member method code with example usage: obj.compose_parallel(adapter_setup, state, lvl) and returns: state