class_docstr: Base class for actor critic trainers. Suitable for multi-step and multi-agent training.
  
  :param algorithm_config: Algorithm parameters.
  :param rollout_generator: The rollout generator to use. This object encapsulates the env.
  :param evaluator: The evaluator to use.
  :param model: Structured torch actor critic model.
  :param model_selection: Optional model selection class, receives model evaluation results.
docstr: Append logging statistics for policies and critic.
  
  :param policy_train_stats: List of policy training statistics.
  :param critic_train_stats: List of critic training statistics.
  :param policy_losses: List of policy losses.
  :param entropies: List of policy entropies.
  :param detached_values: List of detached values.
  :param value_losses: List of value losses.
docstr_tok: ["Append","logging","statistics","for","policies","and","critic",".",":","param","policy_train_stats",":","List","of","policy","training","statistics",".",":","param","critic_train_stats",":","List","of","critic","training","statistics",".",":","param","policy_losses",":","List","of","policy","losses",".",":","param","entropies",":","List","of","policy","entropies",".",":","param","detached_values",":","List","of","detached","values",".",":","param","value_losses",":","List","of","value","losses","."]
code: def _append_train_stats(
      self,
      policy_train_stats: List[Dict[str, List[float]]],
      critic_train_stats: List[Dict[str, List[float]]],
      actor_ids: List[ActorID],
      policy_losses: List[torch.Tensor],
      entropies: List[torch.Tensor],
      detached_values: List[torch.Tensor],
      value_losses: List[torch.Tensor],
  ) -> None:
      """Append logging statistics for policies and critic.
  
      :param policy_train_stats: List of policy training statistics.
      :param critic_train_stats: List of critic training statistics.
      :param policy_losses: List of policy losses.
      :param entropies: List of policy entropies.
      :param detached_values: List of detached values.
      :param value_losses: List of value losses.
      """
  
      # Policies
      for actor_id, substep_loss, substep_entropies in zip(
          actor_ids, policy_losses, entropies
      ):
          policy_train_stats[actor_id[0]]["policy_loss"].append(
              substep_loss.detach().item()
          )
          policy_train_stats[actor_id[0]]["policy_entropy"].append(
              substep_entropies.detach().item()
          )
  
          grad_norm = compute_gradient_norm(
              self.model.policy.network_for(actor_id).parameters()
          )
          policy_train_stats[actor_id[0]]["policy_grad_norm"].append(
              grad_norm
          )
  
      # Critic(s)
      #  - if there is just one critic, report only values from the first sub-step.
      #  - otherwise, use sub-step keys to identify the critics.
      first_critic_id = list(self.model.critic.networks.keys())[-1]
      critic_ids = (
          [first_critic_id]
          if self.model.critic.num_critics == 1
          else list(map(lambda x: x[0], actor_ids))
      )
      for critic_id, substep_detached_values, substep_losses in zip(
          critic_ids, detached_values, value_losses
      ):
          critic_train_stats[critic_id]["critic_value"].append(
              substep_detached_values.mean().item()
          )
          critic_train_stats[critic_id]["critic_value_loss"].append(
              substep_losses.detach().item()
          )
  
          grad_norm = compute_gradient_norm(
              self.model.critic.networks[critic_id].parameters()
          )
          critic_train_stats[critic_id]["critic_grad_norm"].append(
              grad_norm
          )
  
imports: import sys
  import time
  from abc import ABC, abstractmethod
  from typing import List, Union, Dict, Optional
  import numpy
  import torch
  import torch.nn
  from maze.core.agent.torch_actor_critic import TorchActorCritic
  from maze.core.annotations import override
  from maze.core.env.base_env_events import BaseEnvEvents
  from maze.core.env.structured_env import ActorID
  from maze.core.log_stats.log_stats import increment_log_step, LogStatsLevel
  from maze.core.rollout.rollout_generator import RolloutGenerator
  from maze.core.trajectory_recording.records.structured_spaces_record import StructuredSpacesRecord
  from maze.train.parallelization.distributed_actors.distributed_actors import DistributedActors
  from maze.train.trainers.a2c.a2c_algorithm_config import A2CAlgorithmConfig
  from maze.train.trainers.common.actor_critic.actor_critic_events import ActorCriticEvents
  from maze.train.trainers.common.evaluators.rollout_evaluator import RolloutEvaluator
  from maze.train.trainers.common.model_selection.best_model_selection import BestModelSelection
  from maze.train.trainers.common.trainer import Trainer
  from maze.train.trainers.impala.impala_algorithm_config import ImpalaAlgorithmConfig
  from maze.train.trainers.ppo.ppo_algorithm_config import PPOAlgorithmConfig
  from maze.train.utils.train_utils import compute_gradient_norm
  from maze.utils.bcolors import BColors
  from tqdm import tqdm
  from typing.io import BinaryIO
label_desc: Use image node_id 10 for calling the ActorCritic obj's underlying member method code with example usage: obj._append_train_stats(policy_train_stats, critic_train_stats, actor_ids, policy_losses, entropies, detached_values, value_losses) without return types