class_docstr: Simple Q critic for mixed action heads (that is not all discrete). As such it computes a single q_value output
  for all observations.
class_docstr_tok: ["Simple","Q","critic","for","mixed","action","heads","(","that","is","not","all","discrete",")",".","As","such","it","computes","a","single","q_value","output","for","all","observations","."]
code: def __init__(
      self,
      obs_shapes: Dict[str, Sequence[int]],
      action_spaces_dict: Dict[Union[str, int], spaces.Space],
      non_lin: Union[str, type(nn.Module)],
  ):
      super().__init__()
      self.obs_shapes = obs_shapes
      # build perception part
      self.perception_dict = OrderedDict()
      self.perception_dict["latent-obs"] = DenseBlock(
          in_keys="observation",
          out_keys="latent-obs",
          in_shapes=obs_shapes["observation"],
          hidden_units=[256],
          non_lin=non_lin,
      )
      self.perception_dict["latent-act"] = DenseBlock(
          in_keys="action",
          out_keys="latent-act",
          in_shapes=obs_shapes["action"],
          hidden_units=[256],
          non_lin=non_lin,
      )
  
      self.perception_dict["concat"] = ConcatenationBlock(
          in_keys=["latent-obs", "latent-act"],
          in_shapes=self.perception_dict["latent-obs"].out_shapes()
          + self.perception_dict["latent-act"].out_shapes(),
          concat_dim=-1,
          out_keys="concat",
      )
  
      self.perception_dict["latent"] = DenseBlock(
          in_keys="concat",
          out_keys="latent",
          in_shapes=self.perception_dict["concat"].out_shapes(),
          hidden_units=[256],
          non_lin=non_lin,
      )
  
      # build action head
      self.perception_dict["q_value"] = LinearOutputBlock(
          in_keys="latent",
          out_keys="q_value",
          in_shapes=self.perception_dict["latent"].out_shapes(),
          output_units=1,
      )
  
      self.perception_net = InferenceBlock(
          in_keys=["observation", "action"],
          out_keys="q_value",
          in_shapes=[
              self.obs_shapes["observation"],
              self.obs_shapes["action"],
          ],
          perception_blocks=self.perception_dict,
      )
  
      # initialize model weights
      self.perception_net.apply(make_module_init_normc(1.0))
      self.perception_dict["q_value"].apply(
          make_module_init_normc(0.01)
      )
  
imports: from collections import OrderedDict
  from typing import Dict, Union, Sequence
  import torch.nn
  from gym import spaces
  from maze.core.agent.torch_actor_critic import TorchActorCritic
  from maze.core.agent.torch_policy import TorchPolicy
  from maze.core.agent.torch_state_action_critic import TorchStepStateActionCritic
  from maze.core.wrappers.maze_gym_env_wrapper import GymMazeEnv
  from maze.distributions.distribution_mapper import DistributionMapper
  from maze.perception.blocks.feed_forward.dense import DenseBlock
  from maze.perception.blocks.general.concat import ConcatenationBlock
  from maze.perception.blocks.inference import InferenceBlock
  from maze.perception.blocks.output.linear import LinearOutputBlock
  from maze.perception.perception_utils import observation_spaces_to_in_shapes
  from maze.perception.weight_init import make_module_init_normc
  from maze.train.parallelization.vector_env.sequential_vector_env import SequentialVectorEnv
  from maze.train.trainers.common.evaluators.rollout_evaluator import RolloutEvaluator
  from maze.train.parallelization.distributed_actors.dummy_distributed_workers_with_buffer import DummyDistributedWorkersWithBuffer
  from maze.train.trainers.common.replay_buffer.uniform_replay_buffer import UniformReplayBuffer
  from maze.train.trainers.sac.sac_algorithm_config import SACAlgorithmConfig
  from maze.train.trainers.sac.sac_runners import SACRunner
  from maze.train.trainers.sac.sac_trainer import SAC
label_desc: Use image node_id 1 to create a new QCriticNetContinuous object from inherited base classes: nn with example: obj = QCriticNetContinuous(obs_shapes, action_spaces_dict, non_lin)