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","`","."]
code: def _add_partial_rewards(self, successor):
      # TODO (simon): This could be made simpler with a reward buffer that
      # is longer than 1 and does not add, but append. Then we just collect
      # the buffer.
      for (
          agent_id,
          agent_partial_rewards,
      ) in self.partial_rewards.items():
          # If a global timestep mapping exists for the agent use it.
          if self.global_t_to_local_t[agent_id]:
              # The successor episode only need the partial rewards that
              # have not yet recorded in the `SingleAgentEpisode`.
              indices_to_keep = self.partial_rewards_t[
                  agent_id
              ].find_indices_right(
                  self.global_t_to_local_t[agent_id][-1],
              )
          # Otherwise, use the partial rewards timestep mapping.
          else:
              # The partial rewards timestep mapping does only exist for agents that
              # have stepped or have not stepped and received at least a single
              # partial reward.
              if self.partial_rewards_t[agent_id]:
                  indices_to_keep = list(
                      range(len(self.partial_rewards_t[agent_id]))
                  )
              # Otherwise return an empty index list.
              else:
                  indices_to_keep = []
  
          successor.partial_rewards_t[agent_id] = _IndexMapping(
              map(
                  self.partial_rewards_t[agent_id].__getitem__,
                  indices_to_keep,
              )
          )
          successor.partial_rewards[agent_id] = list(
              map(agent_partial_rewards.__getitem__, indices_to_keep)
          )
          # Leave partial results after the last observation in the buffer
          # as long as the agent is not temrinated/truncated.
          # Note, we still need to consider the `partial_rewards` and
          # `partial_rewards_t` for two reasons:
          #   1. The agent is terminated in the last step before creating
          #       this successor.
          #   2. We might want to concatenate the successor to its
          #       predecessor and therefore need structs for each agent
          #       (dead or alive).
          if not self.agent_episodes[agent_id].is_done:
              successor.agent_buffers[agent_id]["rewards"].get_nowait()
              successor.agent_buffers[agent_id]["rewards"].put_nowait(
                  sum(successor.partial_rewards[agent_id])
              )
  
      return successor
  
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 20 for calling the MultiAgentEpisode obj's underlying member method code with example usage: obj._add_partial_rewards(successor) and returns: successor