class_docstr: References:
  ===========
      - https://towardsdatascience.com/deep-reinforcement-learning-build-a-deep-q-network-dqn-to-play-cartpole-with-tensorflow-2-and-gym-8e105744b998
      - https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html#dqn-algorithm
      - https://arxiv.org/abs/1802.00308
code: def _apply_gradient_descent(
      self,
      memory: ReplayMemory,
      batch_size: int,
      learning_rate: float,
      discount_factor: float,
  ):
      # Optimization strategy.
      optimizer = tf.keras.optimizers.Nadam(learning_rate=learning_rate)
  
      loss = tf.keras.losses.Huber()
  
      transitions = memory.sample(batch_size)
      batch = DQNTransition(*zip(*transitions))
  
      state_batch = tf.convert_to_tensor(batch.state)
      action_batch = tf.convert_to_tensor(batch.action)
      reward_batch = tf.convert_to_tensor(
          batch.reward, dtype=tf.float32
      )
      next_state_batch = tf.convert_to_tensor(batch.next_state)
      done_batch = tf.convert_to_tensor(batch.done)
  
      with tf.GradientTape() as tape:
          state_action_values = tf.math.reduce_sum(
              self.policy_network(state_batch)
              * tf.one_hot(action_batch, self.n_actions),
              axis=1,
          )
  
          next_state_values = tf.where(
              done_batch,
              tf.zeros(batch_size),
              tf.math.reduce_max(
                  self.target_network(next_state_batch), axis=1
              ),
          )
  
          expected_state_action_values = reward_batch + (
              discount_factor * next_state_values
          )
          loss_value = loss(
              expected_state_action_values, state_action_values
          )
  
      variables = self.policy_network.trainable_variables
      gradients = tape.gradient(loss_value, variables)
      optimizer.apply_gradients(zip(gradients, variables))
  
file_path: tensortrade/tensortrade/agents/dqn_agent.py
imports: from deprecated import deprecated
  import random
  import numpy
  import tensorflow
  from collections import namedtuple
  from tensortrade.agents import Agent, ReplayMemory
  from datetime import datetime
label_desc: Use image node_id 6 for calling the DQNAgent obj's underlying member method code with example usage: obj._apply_gradient_descent(memory, batch_size, learning_rate, discount_factor) without return types