code: def build_train(
      q_func,
      ob_space,
      ac_space,
      optimizer,
      sess,
      grad_norm_clipping=None,
      gamma=1.0,
      double_q=True,
      scope="deepq",
      reuse=None,
      param_noise=False,
      param_noise_filter_func=None,
      full_tensorboard_log=False,
  ):
      """
      Creates the train function:
  
      :param q_func: (DQNPolicy) the policy
      :param ob_space: (Gym Space) The observation space of the environment
      :param ac_space: (Gym Space) The action space of the environment
      :param reuse: (bool) whether or not to reuse the graph variables
      :param optimizer: (tf.train.Optimizer) optimizer to use for the Q-learning objective.
      :param sess: (TensorFlow session) The current TensorFlow session
      :param grad_norm_clipping: (float) clip gradient norms to this value. If None no clipping is performed.
      :param gamma: (float) discount rate.
      :param double_q: (bool) if true will use Double Q Learning (https://arxiv.org/abs/1509.06461). In general it is a
          good idea to keep it enabled.
      :param scope: (str or VariableScope) optional scope for variable_scope.
      :param reuse: (bool) whether or not the variables should be reused. To be able to reuse the scope must be given.
      :param param_noise: (bool) whether or not to use parameter space noise (https://arxiv.org/abs/1706.01905)
      :param param_noise_filter_func: (function (TensorFlow Tensor): bool) function that decides whether or not a
          variable should be perturbed. Only applicable if param_noise is True. If set to None, default_param_noise_filter
          is used by default.
      :param full_tensorboard_log: (bool) enable additional logging when using tensorboard
          WARNING: this logging can take a lot of space quickly
  
      :return: (tuple)
  
          act: (function (TensorFlow Tensor, bool, float): TensorFlow Tensor) function to select and action given
              observation. See the top of the file for details.
          train: (function (Any, numpy float, numpy float, Any, numpy bool, numpy float): numpy float)
              optimize the error in Bellman's equation. See the top of the file for details.
          update_target: (function) copy the parameters from optimized Q function to the target Q function.
              See the top of the file for details.
          step_model: (DQNPolicy) Policy for evaluation
      """
      n_actions = (
          ac_space.nvec
          if isinstance(ac_space, MultiDiscrete)
          else ac_space.n
      )
      with tf.variable_scope("input", reuse=reuse):
          stochastic_ph = tf.placeholder(tf.bool, (), name="stochastic")
          update_eps_ph = tf.placeholder(
              tf.float32, (), name="update_eps"
          )
  
      with tf.variable_scope(scope, reuse=reuse):
          if param_noise:
              act_f, obs_phs = build_act_with_param_noise(
                  q_func,
                  ob_space,
                  ac_space,
                  stochastic_ph,
                  update_eps_ph,
                  sess,
                  param_noise_filter_func=param_noise_filter_func,
              )
          else:
              act_f, obs_phs = build_act(
                  q_func,
                  ob_space,
                  ac_space,
                  stochastic_ph,
                  update_eps_ph,
                  sess,
              )
  
          # q network evaluation
          with tf.variable_scope(
              "step_model",
              reuse=True,
              custom_getter=tf_util.outer_scope_getter("step_model"),
          ):
              step_model = q_func(
                  sess,
                  ob_space,
                  ac_space,
                  1,
                  1,
                  None,
                  reuse=True,
                  obs_phs=obs_phs,
              )
          q_func_vars = tf.get_collection(
              tf.GraphKeys.GLOBAL_VARIABLES,
              scope=tf.get_variable_scope().name + "/model",
          )
          # target q network evaluation
  
          with tf.variable_scope("target_q_func", reuse=False):
              target_policy = q_func(
                  sess, ob_space, ac_space, 1, 1, None, reuse=False
              )
          target_q_func_vars = tf.get_collection(
              tf.GraphKeys.GLOBAL_VARIABLES,
              scope=tf.get_variable_scope().name + "/target_q_func",
          )
  
          # compute estimate of best possible value starting from state at t + 1
          double_q_values = None
          double_obs_ph = target_policy.obs_ph
          if double_q:
              with tf.variable_scope(
                  "double_q",
                  reuse=True,
                  custom_getter=tf_util.outer_scope_getter("double_q"),
              ):
                  double_policy = q_func(
                      sess, ob_space, ac_space, 1, 1, None, reuse=True
                  )
                  double_q_values = double_policy.q_values
                  double_obs_ph = double_policy.obs_ph
  
      with tf.variable_scope("loss", reuse=reuse):
          # set up placeholders
          act_t_ph = tf.placeholder(tf.int32, [None], name="action")
          rew_t_ph = tf.placeholder(tf.float32, [None], name="reward")
          done_mask_ph = tf.placeholder(tf.float32, [None], name="done")
          importance_weights_ph = tf.placeholder(
              tf.float32, [None], name="weight"
          )
  
          # q scores for actions which we know were selected in the given state.
          q_t_selected = tf.reduce_sum(
              step_model.q_values * tf.one_hot(act_t_ph, n_actions),
              axis=1,
          )
  
          # compute estimate of best possible value starting from state at t + 1
          if double_q:
              q_tp1_best_using_online_net = tf.argmax(
                  double_q_values, axis=1
              )
              q_tp1_best = tf.reduce_sum(
                  target_policy.q_values
                  * tf.one_hot(q_tp1_best_using_online_net, n_actions),
                  axis=1,
              )
          else:
              q_tp1_best = tf.reduce_max(target_policy.q_values, axis=1)
          q_tp1_best_masked = (1.0 - done_mask_ph) * q_tp1_best
  
          # compute RHS of bellman equation
          q_t_selected_target = rew_t_ph + gamma * q_tp1_best_masked
  
          # compute the error (potentially clipped)
          td_error = q_t_selected - tf.stop_gradient(
              q_t_selected_target
          )
          errors = tf_util.huber_loss(td_error)
          weighted_error = tf.reduce_mean(
              importance_weights_ph * errors
          )
  
          tf.summary.scalar("td_error", tf.reduce_mean(td_error))
          tf.summary.scalar("loss", weighted_error)
  
          if full_tensorboard_log:
              tf.summary.histogram("td_error", td_error)
  
          # update_target_fn will be called periodically to copy Q network to target Q network
          update_target_expr = []
          for var, var_target in zip(
              sorted(q_func_vars, key=lambda v: v.name),
              sorted(target_q_func_vars, key=lambda v: v.name),
          ):
              update_target_expr.append(var_target.assign(var))
          update_target_expr = tf.group(*update_target_expr)
  
          # compute optimization op (potentially with gradient clipping)
          gradients = optimizer.compute_gradients(
              weighted_error, var_list=q_func_vars
          )
          if grad_norm_clipping is not None:
              for i, (grad, var) in enumerate(gradients):
                  if grad is not None:
                      gradients[i] = (
                          tf.clip_by_norm(grad, grad_norm_clipping),
                          var,
                      )
  
      with tf.variable_scope("input_info", reuse=False):
          tf.summary.scalar("rewards", tf.reduce_mean(rew_t_ph))
          tf.summary.scalar(
              "importance_weights",
              tf.reduce_mean(importance_weights_ph),
          )
  
          if full_tensorboard_log:
              tf.summary.histogram("rewards", rew_t_ph)
              tf.summary.histogram(
                  "importance_weights", importance_weights_ph
              )
              if tf_util.is_image(obs_phs[0]):
                  tf.summary.image("observation", obs_phs[0])
              elif len(obs_phs[0].shape) == 1:
                  tf.summary.histogram("observation", obs_phs[0])
  
      optimize_expr = optimizer.apply_gradients(gradients)
  
      summary = tf.summary.merge_all()
  
      # Create callable functions
      train = tf_util.function(
          inputs=[
              obs_phs[0],
              act_t_ph,
              rew_t_ph,
              target_policy.obs_ph,
              double_obs_ph,
              done_mask_ph,
              importance_weights_ph,
          ],
          outputs=[summary, td_error],
          updates=[optimize_expr],
      )
      update_target = tf_util.function(
          [], [], updates=[update_target_expr]
      )
  
      return act_f, train, update_target, step_model
  
file_path: stable-baselines/stable_baselines/deepq/build_graph.py
imports: import tensorflow
  from gym.spaces import MultiDiscrete
  from stable_baselines.common import tf_util
label_desc: Use image node_id 7 for calling a global function with example usage: build_train(q_func, ob_space, ac_space, optimizer, sess, grad_norm_clipping, gamma, double_q, scope, reuse, param_noise, param_noise_filter_func, full_tensorboard_log) and returns: act_f,  train,  update_target,  step_model