code: def functional_container_conversion(
      config,
      name,
      verbose,
      nonlinear_mxts_mode,
      dense_mxts_mode,
      conv_mxts_mode,
      maxpool_deeplift_mode,
      layer_overrides,
      custom_conversion_funcs,
      outer_inbound_node_infos=None,
      node_id_to_deeplift_layers=None,
      node_id_to_input_node_info=None,
      name_to_deeplift_layer=None,
  ):
      if node_id_to_deeplift_layers is None:
          assert node_id_to_input_node_info is None
          assert name_to_deeplift_layer is None
  
          node_id_to_deeplift_layers = OrderedDict()
          node_id_to_input_node_info = OrderedDict()
          name_to_deeplift_layer = OrderedDict()
      name_prefix = name
  
      if (
          outer_inbound_node_infos is not None
          and len(outer_inbound_node_infos) > 0
      ):
          # they should all be 2-tuples of the input node id
          # and the output node index.
          assert all([len(x) == 2 for x in outer_inbound_node_infos])
  
          # Get the model config's input node ids
          # The entries in model_config["input_layers"] are 3-tuples of the format
          # (layer name, node_idx, output_tensor_idx). Once again, I am
          # assuming output_tensor_idx is always 0.
          assert all(
              [x[2] == 0 for x in config["input_layers"]]
          ), "Unsupported format for input_layers: " + str(
              config["input_layers"]
          )
          input_node_ids = [
              (name_prefix + "_" if name_prefix != "" else "")
              + x[0]
              + "_"
              + str(x[1])
              for x in config["input_layers"]
          ]
  
          assert len(input_node_ids) == len(outer_inbound_node_infos)
          input_node_id_to_outer_inbound_node = OrderedDict(
              zip(input_node_ids, outer_inbound_node_infos)
          )
      else:
          input_node_id_to_outer_inbound_node = {}
  
      output_node_ids = [
          (
              (name_prefix + "_" if name_prefix != "" else "")
              + x[0]
              + "_"
              + str(x[1]),
              x[2],
          )
          for x in config["output_layers"]
      ]
  
      for layer_config in config["layers"]:
          if layer_config["class_name"] in custom_conversion_funcs:
              conversion_function = custom_conversion_funcs[
                  layer_config["class_name"]
              ]
          else:
              conversion_function = layer_name_to_conversion_function(
                  layer_config["class_name"]
              )
  
          # We need to deal with the case of shared layers, i.e. the same
          # parameters are repeated across multiple layers. In the keras
          # functional models API, shared layers are represented
          # as a single "layer" object with multiple "nodes". Each node
          # has an associated entry in config["inbound_nodes"] that details
          # the node's input (where the input consists of node(s) of
          # other layer(s)).
          # Different nodes are only distinguished by
          # the fact that they act on different inputs since all their
          # parameters are the same. The exception are nodes corresponding to
          # keras InputLayers because those nodes take no inputs.
          # For each layer, we iterate over each of its nodes
          # and instantiate a separate deeplift layer object for each node.
          # Note that there is no need for deeplift to remember
          # which nodes have shared parameters as weight sharing is only
          # important during training. Once the model is trained, each node
          # of a shared layer is treated as an entirely separate deeplift
          # layer. In other words, a "layer" in deeplift terminology corresponds
          # to a "node" of a layer in keras terminology. The deeplift layer
          # name will be the keras layer name + _ + <idx of node>
  
          # To iterate over each of the nodes of the layer, we can just iterate
          # over each of the entries in layer_config["inbound_nodes"] as each
          # node of a layer is associated with different input node(s).
          # All layer types have at least one entry in
          # layer_config["inbound_nodes"], except for keras layers of
          # type InputLayer which have exactly one associated node that
          # takes no input (thus, layer_config["inbound_nodes"]
          # is an empty list).
          # To handle the case of InputLayer, we iterate up to
          # max(len(layer_config["inbound_nodes"]),1)
          for node_idx in range(
              max(len(layer_config["inbound_nodes"]), 1)
          ):
              if layer_config["class_name"] == "Model":
                  actual_node_idx += (
                      1  # I am not sure why, but this is what I
                  )
                  # observe for nested models...
              else:
                  actual_node_idx = node_idx
  
              # generate the base deeplift layer name, also called the node id
              node_id = (
                  (name_prefix + "_" if name_prefix != "" else "")
                  + layer_config["name"]
                  + "_"
                  + str(actual_node_idx)
              )
  
              # We also need to keep track of the input node id(s) for this
              # node as we will need that info when
              # we are linking up the whole graph
              # First, we deal with the case of InputLayer
              if len(layer_config["inbound_nodes"]) == 0:
                  # if there are no input nodes (i.e. this node is an
                  # instance of InputLayer), set the input node info to None,
                  # unless this is a nested model
                  if node_id in input_node_id_to_outer_inbound_node:
                      processed_inbound_node_info = (
                          input_node_id_to_outer_inbound_node[node_id]
                      )
                  else:
                      processed_inbound_node_info = None
              # Now we deal with nodes of a type other than InputLayer.
              # Most nodes are of a type that takes only one input node,
              # however there are some nodes (i.e. nodes of type Merge)
              # that take multiple input nodes.
              else:
                  inbound_node_info = layer_config["inbound_nodes"][
                      node_idx
                  ]
                  # If we are dealing with a node that takes a single
                  # input node (i.e. most layers), then
                  # inbound_node_info is a 4-tuple of the format
                  # (keras_input_layer_name, node_index_in_keras_input_layer,
                  #  output_tensor_index, other_kwargs).
                  # In all the examples I have dealt with, output_tensor_index
                  # is 0 and other_kwargs is an empty dict. I think those
                  # are there for edge cases where a single node outputs
                  # multiple tensors and only one of them are used later.
                  if isinstance(inbound_node_info[0], str):
                      # We are dealing with a node that takes a single input node
                      # Validate my assumptions about the format:
                      assert (
                          len(inbound_node_info) == 4
                          and isinstance(inbound_node_info[1], int)
                          and len(inbound_node_info[3]) == 0
                      ), (
                          "Unsupported format for inbound_node_info: "
                          + str(inbound_node_info)
                      )
                      inbound_node_id = (
                          (
                              name_prefix + "_"
                              if name_prefix != ""
                              else ""
                          )
                          + inbound_node_info[0]
                          + "_"
                          + str(inbound_node_info[1])
                      )
                      processed_inbound_node_info = (
                          inbound_node_id,
                          inbound_node_info[2],
                      )
                  # The other case I have in mind are merge layers that
                  # take multiple input nodes. In this case,
                  # inbound_node_info should be a list of 4-tuples
                  else:
                      assert isinstance(
                          inbound_node_info[0], list
                      ) and (
                          isinstance(inbound_node_info[0][0], str)
                          or isinstance(
                              inbound_node_info[0][0], unicode
                          )
                      ), (
                          "Unsupported format for inbound_node_info: "
                          + str(inbound_node_info)
                      )
                      for single_inbound_node_info in inbound_node_info:
                          assert (
                              len(single_inbound_node_info) == 4
                              and isinstance(
                                  single_inbound_node_info[1], int
                              )
                              and len(single_inbound_node_info[3]) == 0
                          ), (
                              "Unsupported format for inbound_node_info: "
                              + str(inbound_node_info)
                          )
                      inbound_node_ids = [
                          (
                              (
                                  (
                                      name_prefix + "_"
                                      if name_prefix != ""
                                      else ""
                                  )
                                  + x[0]
                                  + "_"
                                  + str(x[1])
                              ),
                              x[2],
                          )
                          for x in inbound_node_info
                      ]
                      processed_inbound_node_info = inbound_node_ids
  
              if node_id in input_node_id_to_outer_inbound_node:
                  conversion_function = noop_conversion
  
              modes_to_pass = {
                  "dense_mxts_mode": dense_mxts_mode,
                  "conv_mxts_mode": conv_mxts_mode,
                  "nonlinear_mxts_mode": nonlinear_mxts_mode,
                  "maxpool_deeplift_mode": maxpool_deeplift_mode,
              }
              if node_id in layer_overrides:
                  for mode in [
                      "dense_mxts_mode",
                      "conv_mxts_mode",
                      "nonlinear_mxts_mode",
                  ]:
                      if mode in layer_overrides[layer_idx]:
                          modes_to_pass[mode] = layer_overrides[
                              layer_idx
                          ][mode]
  
              # Instantiate the deeplift layers associated with the node_id.
              # Note that a single keras node can map to multiple deeplift
              # layers - e.g. if the keras node is of type Conv with
              # a ReLU activation, this will be turned into two deeplift
              # layers - one for the Conv and one for the ReLU. This is
              # why the return type of conversion_function is a list of layers.
              # In the aforementioned example, the ReLU layer will retain
              # the original deeplift layer name and the Conv layer will
              # have 'preact_' prefixed to the deeplift layer name
              converted_deeplift_layers = conversion_function(
                  config=layer_config["config"],
                  name=node_id,
                  verbose=verbose,
                  layer_overrides=layer_overrides,
                  outer_inbound_node_infos=processed_inbound_node_info,
                  node_id_to_deeplift_layers=node_id_to_deeplift_layers,
                  node_id_to_input_node_info=node_id_to_input_node_info,
                  name_to_deeplift_layer=name_to_deeplift_layer,
                  **modes_to_pass,
              )
  
              if (
                  type(converted_deeplift_layers).__name__
                  == "ConvertedModelContainer"
              ):
                  # the inputs for the model will be linked up internally
                  # as part of the call to the model container conversion
                  pass
              else:
                  assert isinstance(converted_deeplift_layers, list)
                  node_id_to_input_node_info[
                      node_id
                  ] = processed_inbound_node_info
  
              node_id_to_deeplift_layers[
                  node_id
              ] = converted_deeplift_layers
              if isinstance(converted_deeplift_layers, list):
                  for layer in converted_deeplift_layers:
                      name_to_deeplift_layer[layer.name] = layer
              else:
                  assert (
                      type(converted_deeplift_layers).__name__
                      == "ConvertedModelContainer"
                  )
                  for (
                      layer_name,
                      layer,
                  ) in (
                      converted_deeplift_layers.name_to_deeplift_layer.items()
                  ):
                      assert layer_name == layer.name
                      name_to_deeplift_layer[layer.name] = layer
  
      output_layers = []
      # compile all the output layers together
      for output_node_id, output_tensor_idx in output_node_ids:
          assert output_node_id in node_id_to_deeplift_layers
          converted_deeplift_layers = node_id_to_deeplift_layers[
              output_node_id
          ]
          if isinstance(converted_deeplift_layers, list):
              assert output_tensor_idx == 0
              output_layers.append(converted_deeplift_layers[-1])
          else:
              if (
                  type(converted_deeplift_layers).__name__
                  == "ConvertedModelContainer"
              ):
                  output_layers.append(
                      converted_deeplift_layers.output_layers[
                          output_tensor_idx
                      ]
                  )
  
      # Link up all the deeplift layers
      for node_id in node_id_to_input_node_info:
          input_node_info = node_id_to_input_node_info[node_id]
          if input_node_info is not None:
              if isinstance(input_node_info, list):
                  temp_inp = []
                  for (
                      input_node_id,
                      output_tensor_idx,
                  ) in input_node_info:
                      deeplift_layers = node_id_to_deeplift_layers[
                          input_node_id
                      ]
                      if isinstance(deeplift_layers, list):
                          assert output_tensor_idx == 0
                          temp_inp.append(deeplift_layers[-1])
                      elif (
                          type(deeplift_layers).__name__
                          == "ConvertedModelContainer"
                      ):
                          temp_inp.append(
                              deeplift_layers.output_layers[
                                  output_tensor_idx
                              ]
                          )
              else:
                  input_node_id, output_tensor_idx = input_node_info
                  deeplift_layers = node_id_to_deeplift_layers[
                      input_node_id
                  ]
                  if isinstance(deeplift_layers, list):
                      assert output_tensor_idx == 0
                      temp_inp = deeplift_layers[-1]
                  elif (
                      type(deeplift_layers).__name__
                      == "ConvertedModelContainer"
                  ):
                      temp_inp = deeplift_layers.output_layers[
                          output_tensor_idx
                      ]
              node_id_to_deeplift_layers[node_id][0].set_inputs(
                  temp_inp
              )
  
      # Get the model's input node ids
      # The entries in model_config["input_layers"] are 3-tuples of the format
      # (layer name, node_idx, output_tensor_idx). Once again, I am
      # assuming output_tensor_idx is always 0.
      assert all(
          [x[2] == 0 for x in config["input_layers"]]
      ), "Unsupported format for input_layers: " + str(
          config["input_layers"]
      )
      input_node_ids = [
          (name_prefix + "_" if name_prefix != "" else "")
          + x[0]
          + "_"
          + str(x[1])
          for x in config["input_layers"]
      ]
  
      return ConvertedModelContainer(
          node_id_to_deeplift_layers=node_id_to_deeplift_layers,
          node_id_to_input_node_info=node_id_to_input_node_info,
          name_to_deeplift_layer=name_to_deeplift_layer,
          input_layer_names=input_node_ids,
          output_layers=output_layers,
      )
  
imports: from __future__ import division
  from __future__ import print_function
  from __future__ import absolute_import
  import sys
  import os
  from collections import OrderedDict
  import deeplift
  from deeplift import models, layers
  from deeplift.layers.core import NonlinearMxtsMode, DenseMxtsMode, ConvMxtsMode, MaxPoolDeepLiftMode
  import deeplift.util
  import numpy
  import json
  import yaml
  import h5py
label_desc: Use image node_id 31 for calling a global function with example usage: functional_container_conversion(config, name, verbose, nonlinear_mxts_mode, dense_mxts_mode, conv_mxts_mode, maxpool_deeplift_mode, layer_overrides, custom_conversion_funcs, outer_inbound_node_infos, node_id_to_deeplift_layers, node_id_to_input_node_info, name_to_deeplift_layer) and returns: ConvertedModelContainer