class_docstr: Stores multi-agent episode data.
  
  The central attribute of the class is the timestep mapping
  `global_t_to_local_t` that maps the global (environment)
  timestep to the local (agent) timesteps.
  
  The `MultiAgentEpisode` is based on the `SingleAgentEpisode`s
  for each agent, stored in `MultiAgentEpisode.agent_episodes`.
class_docstr_tok: ["Stores","multi-agent","episode","data",".","The","central","attribute","of","the","class","is","the","timestep","mapping","`","global_t_to_local_t","`","that","maps","the","global","(","environment",")","timestep","to","the","local","(","agent",")","timesteps",".","The","`","MultiAgentEpisode","`","is","based","on","the","`","SingleAgentEpisode","`","s","for","each","agent",",","stored","in","`","MultiAgentEpisode.agent_episodes","`","."]
docstr: Gets rewards for all agents that stepped in the last timesteps.
  
  Note that rewards are only returned for agents that stepped
  during the given index range.
  
  Args:
      indices: Either a single index or a list of indices. The indices
          can be reversed (e.g. [-1, -2]) or absolute (e.g. [98, 99]).
          This defines the time indices for which the rewards
          should be returned.
      global_ts: Boolean that defines, if the indices should be considered
          environment (`True`) or agent (`False`) steps.
  
  Returns: A dictionary mapping agent ids to rewards (of different
      timesteps). Only for agents that have stepped (were ready) at a
      timestep, rewards are returned (i.e. not all agent ids are
      necessarily in the keys).
code: def get_rewards(
      self,
      indices: Union[int, List[int]] = -1,
      global_ts: bool = True,
      as_list: bool = False,
      partial: bool = True,
      consider_buffer: bool = True,
  ) -> Union[MultiAgentDict, List[MultiAgentDict]]:
      """Gets rewards for all agents that stepped in the last timesteps.
  
      Note that rewards are only returned for agents that stepped
      during the given index range.
  
      Args:
          indices: Either a single index or a list of indices. The indices
              can be reversed (e.g. [-1, -2]) or absolute (e.g. [98, 99]).
              This defines the time indices for which the rewards
              should be returned.
          global_ts: Boolean that defines, if the indices should be considered
              environment (`True`) or agent (`False`) steps.
  
      Returns: A dictionary mapping agent ids to rewards (of different
          timesteps). Only for agents that have stepped (were ready) at a
          timestep, rewards are returned (i.e. not all agent ids are
          necessarily in the keys).
      """
  
      if global_ts:
          # Check, if the indices are iterable.
          if isinstance(indices, list):
              indices = [
                  (
                      self.t - self.ts_carriage_return + idx + 1
                      if idx < 0
                      else idx
                  )
                  for idx in indices
              ]
          # If not make them iterable.
          else:
              indices = (
                  [self.t - self.ts_carriage_return + indices + 1]
                  if indices < 0
                  else [indices]
              )
      else:
          if not isinstance(indices, list):
              indices = [indices]
  
      if not partial and consider_buffer:
          buffered_rewards = {}
          timestep_mapping = {}
  
          for (
              agent_id,
              agent_global_t_to_local_t,
          ) in self.global_t_to_local_t.items():
              # If the agent had an initial observation.
              if agent_global_t_to_local_t:
                  # If the agent received rewards after the last observation.
                  if (
                      self.partial_rewards_t[agent_id]
                      and self.partial_rewards_t[agent_id][-1]
                      > agent_global_t_to_local_t[-1]
                      and self.partial_rewards_t[agent_id][-1]
                      <= max(indices)
                  ):
                      indices_at_or_after_last_obs = [
                          agent_global_t_to_local_t[-1]
                      ] + sorted(
                          [
                              idx
                              for idx in indices
                              if idx > agent_global_t_to_local_t[-1]
                          ]
                      )
                      (
                          buffered_rewards[agent_id],
                          indices_wih_rewards,
                      ) = self.accumulate_partial_rewards(
                          agent_id,
                          indices_at_or_after_last_obs,
                          return_indices=True,
                      )
                      # Note, the global timestep mapping begins at zero
                      # with the initial observation. Rewards start at
                      # timestep 1.
                      timestep_mapping[agent_id] = _IndexMapping(
                          agent_global_t_to_local_t[1:]
                          + list(
                              range(
                                  agent_global_t_to_local_t[-1] + 1,
                                  agent_global_t_to_local_t[-1]
                                  + 1
                                  + len(indices_wih_rewards),
                              )
                          )
                      )
                  # There are no partial rewards in the range of requested indices.
                  else:
                      buffered_rewards[agent_id] = []
                      # Note, we need here at least the timestep mapping for the
                      # recorded timesteps as they have recorded any rewards before.
                      # TODO (simon): Allow slicing for _IndexMapping. ALso rename to
                      # TimestepMapping.
                      # TODO (simon): Check, if we can simply use the `
                      # has_initial_value`instead of slicing.
                      timestep_mapping[agent_id] = _IndexMapping(
                          agent_global_t_to_local_t[1:]
                      )
  
              # If the agent had no initial observation, yet.
              else:
                  # Has the agent received partial rewards, yet and if yes, has
                  # any one received before the last requested index.
                  if self.partial_rewards_t[
                      agent_id
                  ] and self.partial_rewards_t[agent_id][0] < max(
                      indices
                  ):
                      # Then assign the partial rewards to the requested indices.
                      # Note, the function accumulates from the second index on, so
                      # we add a zero.
                      # TODO (simon): Check, if we need here maybe a sifting for
                      # the first index in partial_rewards_t.
                      (
                          buffered_rewards[agent_id],
                          indices_with_rewards,
                      ) = self.accumulate_partial_rewards(
                          agent_id,
                          [0] + sorted(indices),
                          return_indices=True,
                      )
                      # TODO (simon): This should be all indices at or below which
                      # rewards existed.
                      timestep_mapping[agent_id] = _IndexMapping(
                          range(1, len(indices_with_rewards) + 1)
                      )
                      # timestep_mapping[agent_id] = _IndexMapping(
                      #     [
                      #         idx
                      #         for idx in sorted(indices)
                      #         if self.partial_rewards_t[
                      #             "agent_1"
                      #         ].find_indices_left_equal(idx)
                      #     ]
                      # )
                  # Else, no partial rewards have to be assigned.
                  else:
                      buffered_rewards[agent_id] = []
                      timestep_mapping[agent_id] = _IndexMapping()
  
      # Now request the rewards.
      if partial:
          # Here we simply apply the logic from `_getattr_by_index`, however
          # use simply the the `partial_rewards` dict. Check for indices
          # correspondingly.
          if as_list:
              return [
                  {
                      agent_id: self.partial_rewards[agent_id][
                          self.partial_rewards_t[agent_id].find_indices(
                              [idx], shift=0
                          )[0]
                      ]
                      for agent_id, agent_eps in self.agent_episodes.items()
                      if self.partial_rewards_t[agent_id].find_indices(
                          [idx], shift=0
                      )
                  }
                  for idx in indices
              ]
          else:
              return {
                  agent_id: list(
                      map(
                          self.partial_rewards[agent_id].__getitem__,
                          self.partial_rewards_t[agent_id].find_indices(
                              indices, shift=0
                          ),
                      )
                  )
                  for agent_id in self._agent_ids
                  if self.partial_rewards_t[agent_id].find_indices(
                      indices, shift=0
                  )
              }
  
      else:
          if consider_buffer:
              # Note, we do not consider initial values here as the indices are
              # already positive.
              return self._getattr_by_index(
                  "rewards",
                  indices,
                  global_ts=global_ts,
                  global_ts_mapping=timestep_mapping,
                  buffered_values=buffered_rewards,
                  as_list=as_list,
              )
          else:
              # Note, if we use the global timestep mapping (of observations), the
              # mapping starts at 0 (because observations do), therefore we have to
              # set `has_initial_value` to `True`.
              return self._getattr_by_index(
                  "rewards",
                  indices,
                  has_initial_value=True,
                  global_ts=global_ts,
                  as_list=as_list,
                  shift=-1,
              )
  
imports: from collections import defaultdict
  from queue import Queue
  from typing import Any, Dict, List, Optional, Set, Union
  import uuid
  import numpy
  from ray.rllib.env.single_agent_episode import SingleAgentEpisode
  from ray.rllib.policy.sample_batch import MultiAgentBatch
  from ray.rllib.utils.typing import MultiAgentDict
label_desc: Use image node_id 6 for calling the MultiAgentEpisode obj's underlying member method code with example usage: obj.get_rewards(indices, global_ts, as_list, partial, consider_buffer) and returns: unknown, unknown, self, self