class_docstr: Defines how the inputs, hidden layers, and outputs are connected.
  
  This represents the architecture of a NN by a sequence with this pattern:
  
  (Layer 0)_______
  |InputSelector  |
  |Combiner       | (Node 0)
  |Block__________|
  (Layer 1)_______
  |InputSelector  |
  |Combiner       | (Node 1)
  |Block__________|
  (Layer 2)_______
  |InputSelector  |
  |Combiner       | (Node 2)
  |Block__________|
  (Layer 3)
  .
  .
  .
  (Layer d)
  
  Since every InputSelector has access to all preceding layers, we can represent
  any arbitrary DAG. To obtain the adjacency matrix, we can traverse the
  sequence and concatentate the InputSelectors' outputs.
class_docstr_tok: ["Defines","how","the","inputs",",","hidden","layers",",","and","outputs","are","connected",".","This","represents","the","architecture","of","a","NN","by","a","sequence","with","this","pattern",":","(","Layer","0",")","_______","|InputSelector","|","|Combiner","|","(","Node","0",")","|Block__________|","(","Layer","1",")","_______","|InputSelector","|","|Combiner","|","(","Node","1",")","|Block__________|","(","Layer","2",")","_______","|InputSelector","|","|Combiner","|","(","Node","2",")","|Block__________|","(","Layer","3",")",".",".",".","(","Layer","d",")","Since","every","InputSelector","has","access","to","all","preceding","layers",",","we","can","represent","any","arbitrary","DAG",".","To","obtain","the","adjacency","matrix",",","we","can","traverse","the","sequence","and","concatentate","the","InputSelectors","'","outputs","."]
docstr: Creates a tower.
  
  Forked from architecture_utils.construct_tower.
  
  Args:
    phoenix_spec: The trial's `phoenix_spec_pb2.PhoenixSpec` proto.
    input_tensor: An input `tf.Tensor` or a Dict[str, tf.Tensor] to build the
      network on top of.
    is_training: a boolean indicating if we are in training.
    lengths: A `tf.Tensor` with the lengths (dimenions: [batch_size]) holding
      the length of each sequence for sequential problems. Keep as None, for
      non sequential problems.
    logits_dimension: The last axis dimension of the logits.
    is_frozen: Is the tower frozen - integer and not boolean.
    dropout_rate: a float indicating the rate of dropouts to apply between
      blocks. Applied only if the value is above zero.
  
  Returns:
    The output `tf.Tensor` of the last layer in the built neural network.
code: def construct_tower(
      self,
      phoenix_spec,
      input_tensor,
      is_training,
      lengths,
      logits_dimension,
      is_frozen,
      dropout_rate=None,
  ):
      """Creates a tower.
  
      Forked from architecture_utils.construct_tower.
  
      Args:
        phoenix_spec: The trial's `phoenix_spec_pb2.PhoenixSpec` proto.
        input_tensor: An input `tf.Tensor` or a Dict[str, tf.Tensor] to build the
          network on top of.
        is_training: a boolean indicating if we are in training.
        lengths: A `tf.Tensor` with the lengths (dimenions: [batch_size]) holding
          the length of each sequence for sequential problems. Keep as None, for
          non sequential problems.
        logits_dimension: The last axis dimension of the logits.
        is_frozen: Is the tower frozen - integer and not boolean.
        dropout_rate: a float indicating the rate of dropouts to apply between
          blocks. Applied only if the value is above zero.
  
      Returns:
        The output `tf.Tensor` of the last layer in the built neural network.
      """
  
      if self.input_keys is None and not isinstance(input_tensor, dict):
          all_layers = [input_tensor]
      elif self.input_keys and isinstance(input_tensor, dict):
          all_layers = [input_tensor[k] for k in self.input_keys]
      else:
          raise ValueError(
              "If Architecture's input_keys is None, input_tensor must be a "
              "Tensor. Otherwise, input_tensor must be a dict."
          )
      block_index = 1
      str_signature = ""
      with tf.compat.v1.variable_scope(
          "Phoenix/{}".format(self.tower_name)
      ):
          for node in self.node_list:
              str_signature += str(node.block_type)
              # TODO(b/172564129): Should block_index also be ignored when uniform
              # average transfer learning? How would we handle repeated blocks, e.g.
              # two FC layers stacked on top of each other.
              scope = "{0}_{1}_{2}".format(
                  str(block_index), node.block_name, str_signature
              )
              scope = architecture_utils.strip_scope(
                  scope,
                  phoenix_spec.transfer_learning_spec.transfer_learning_type,
                  str_signature,
              )
              with tf.compat.v1.variable_scope(scope):
                  with architecture_utils.arg_scope(
                      architecture_utils.DATA_FORMAT_OPS,
                      data_format=phoenix_spec.cnn_data_format,
                  ):
                      selected_layers = node.input_selector(all_layers)
                      combined_layers = node.combiner(selected_layers)
                      output = node.block_builder.block_build(
                          input_tensors=combined_layers,
                          is_training=is_training,
                          lengths=lengths,
                      )
                      if dropout_rate and dropout_rate > 0:
                          output[-1] = tf.compat.v1.layers.dropout(
                              output[-1],
                              rate=dropout_rate,
                              training=is_training,
                          )
                      all_layers.append(output[-1])
                      block_index += 1
  
          # Create the logits.
          scope = "last_dense_{}".format(str_signature)
          scope = architecture_utils.strip_scope(
              scope,
              phoenix_spec.transfer_learning_spec.transfer_learning_type,
              str_signature,
          )
          with tf.compat.v1.variable_scope(scope):
              logits_spec = self.create_logits_spec(
                  phoenix_spec,
                  all_layers[-1],
                  logits_dimension,
                  is_frozen,
                  lengths,
              )
  
      self.save_to_graph()
      architecture_utils.set_parameter(
          self.tower_name,
          architecture_utils.DROPOUTS,
          (-1.0 if dropout_rate is None else dropout_rate),
          tf.float32,
      )
      architecture_utils.set_parameter(
          self.tower_name, architecture_utils.IS_FROZEN, int(is_frozen)
      )
      return logits_spec
  
imports: import abc
  import collections
  import enum
  import time
  from absl import logging
  from model_search import block_builder
  from model_search import utils
  from model_search.architecture import architecture_utils
  from model_search.proto import phoenix_spec_pb2
  import tensorflow.compat.v2
label_desc: Use image node_id 3 for calling the Architecture obj's underlying member method code with example usage: obj.construct_tower(phoenix_spec, input_tensor, is_training, lengths, logits_dimension, is_frozen, dropout_rate) and returns: logits_spec