code: def learn_from_batch(self, batch):
      network_keys = self.ap.network_wrappers[
          "main"
      ].input_embedders_parameters.keys()
  
      # next state values
      q_st_plus_1_target, q_st_plus_1_online = self.networks[
          "main"
      ].parallel_prediction(
          [
              (
                  self.networks["main"].target_network,
                  batch.next_states(network_keys),
              ),
              (
                  self.networks["main"].online_network,
                  batch.next_states(network_keys),
              ),
          ]
      )
      selected_actions = np.argmax(q_st_plus_1_online, 1)
      v_st_plus_1_target = np.max(q_st_plus_1_target, 1)
  
      # current state values
      q_st_target, q_st_online = self.networks[
          "main"
      ].parallel_prediction(
          [
              (
                  self.networks["main"].target_network,
                  batch.states(network_keys),
              ),
              (
                  self.networks["main"].online_network,
                  batch.states(network_keys),
              ),
          ]
      )
      v_st_target = np.max(q_st_target, 1)
  
      # calculate TD error
      TD_targets = np.copy(q_st_online)
      total_returns = batch.n_step_discounted_rewards()
      for i in range(batch.size):
          TD_targets[i, batch.actions()[i]] = (
              batch.rewards()[i]
              + (1.0 - batch.game_overs()[i])
              * self.ap.algorithm.discount
              * q_st_plus_1_target[i][selected_actions[i]]
          )
          advantage_learning_update = (
              v_st_target[i] - q_st_target[i, batch.actions()[i]]
          )
          next_advantage_learning_update = (
              v_st_plus_1_target[i]
              - q_st_plus_1_target[i, selected_actions[i]]
          )
          # Persistent Advantage Learning or Regular Advantage Learning
          if self.persistent:
              TD_targets[i, batch.actions()[i]] -= self.alpha * min(
                  advantage_learning_update,
                  next_advantage_learning_update,
              )
          else:
              TD_targets[i, batch.actions()[i]] -= (
                  self.alpha * advantage_learning_update
              )
  
          # mixing monte carlo updates
          monte_carlo_target = total_returns[i]
          TD_targets[i, batch.actions()[i]] = (
              1 - self.monte_carlo_mixing_rate
          ) * TD_targets[
              i, batch.actions()[i]
          ] + self.monte_carlo_mixing_rate * monte_carlo_target
  
      result = self.networks["main"].train_and_sync_networks(
          batch.states(network_keys), TD_targets
      )
      total_loss, losses, unclipped_grads = result[:3]
  
      return total_loss, losses, unclipped_grads
  
file_path: coach/rl_coach/agents/pal_agent.py
imports: from typing import Union
  import numpy
  from rl_coach.agents.dqn_agent import DQNAgentParameters, DQNAlgorithmParameters
  from rl_coach.agents.value_optimization_agent import ValueOptimizationAgent
  from rl_coach.memories.episodic.episodic_experience_replay import EpisodicExperienceReplayParameters
label_desc: Use image node_id 3 for calling the PALAgent obj's underlying member method code with example usage: obj.learn_from_batch(batch) and returns: total_loss,  losses,  unclipped_grads