class_docstr: Extension of the standard TFModelV2 for SAC.
  
  To customize, do one of the following:
  - sub-class SACTFModel and override one or more of its methods.
  - Use SAC's `q_model_config` and `policy_model` keys to tweak the default model
    behaviors (e.g. fcnet_hiddens, conv_filters, etc..).
  - Use SAC's `q_model_config->custom_model` and `policy_model->custom_model` keys
    to specify your own custom Q-model(s) and policy-models, which will be
    created within this SACTFModel (see `build_policy_model` and
    `build_q_model`.
  
  Note: It is not recommended to override the `forward` method for SAC. This
  would lead to shared weights (between policy and Q-nets), which will then
  not be optimized by either of the critic- or actor-optimizers!
  
  Data flow:
      `obs` -> forward() (should stay a noop method!) -> `model_out`
      `model_out` -> get_policy_output() -> pi(actions|obs)
      `model_out`, `actions` -> get_q_values() -> Q(s, a)
      `model_out`, `actions` -> get_twin_q_values() -> Q_twin(s, a)
docstr: Builds one of the (twin) Q-nets used by this SAC.
  
  Override this method in a sub-class of SACTFModel to implement your
  own Q-nets. Alternatively, simply set `custom_model` within the
  top level SAC `q_model_config` config key to make this default implementation
  of `build_q_model` use your custom Q-nets.
  
  Returns:
      TFModelV2: The TFModelV2 Q-net sub-model.
docstr_tok: ["Builds","one","of","the","(","twin",")","Q-nets","used","by","this","SAC",".","Override","this","method","in","a","sub-class","of","SACTFModel","to","implement","your","own","Q-nets",".","Alternatively",",","simply","set","`","custom_model","`","within","the","top","level","SAC","`","q_model_config","`","config","key","to","make","this","default","implementation","of","`","build_q_model","`","use","your","custom","Q-nets",".","Returns",":","TFModelV2",":","The","TFModelV2","Q-net","sub-model","."]
code: def build_q_model(
      self, obs_space, action_space, num_outputs, q_model_config, name
  ):
      """Builds one of the (twin) Q-nets used by this SAC.
  
      Override this method in a sub-class of SACTFModel to implement your
      own Q-nets. Alternatively, simply set `custom_model` within the
      top level SAC `q_model_config` config key to make this default implementation
      of `build_q_model` use your custom Q-nets.
  
      Returns:
          TFModelV2: The TFModelV2 Q-net sub-model.
      """
      self.concat_obs_and_actions = False
      if self.discrete:
          input_space = obs_space
      else:
          orig_space = getattr(obs_space, "original_space", obs_space)
          if isinstance(orig_space, Box) and len(orig_space.shape) == 1:
              input_space = Box(
                  float("-inf"),
                  float("inf"),
                  shape=(orig_space.shape[0] + action_space.shape[0],),
              )
              self.concat_obs_and_actions = True
          else:
              input_space = gym.spaces.Tuple([orig_space, action_space])
  
      model = ModelCatalog.get_model_v2(
          input_space,
          action_space,
          num_outputs,
          q_model_config,
          framework="tf",
          name=name,
      )
      return model
  
imports: import gymnasium
  from gymnasium.spaces import Box, Discrete
  import numpy
  import tree
  from typing import Dict, List, Optional
  from ray.rllib.models.catalog import ModelCatalog
  from ray.rllib.models.tf.tf_modelv2 import TFModelV2
  from ray.rllib.utils.annotations import override
  from ray.rllib.utils.framework import try_import_tf
  from ray.rllib.utils.spaces.simplex import Simplex
  from ray.rllib.utils.typing import ModelConfigDict, TensorType, TensorStructType
label_desc: Use image node_id 4 for calling the SACTFModel obj's underlying member method code with example usage: obj.build_q_model(obs_space, action_space, num_outputs, q_model_config, name) and returns: model