class_docstr: A simple out-of-graph Replay Buffer.
  
  Stores transitions, state, action, reward, next_state, terminal (and any
  extra contents specified) in a circular buffer and provides a uniform
  transition sampling function.
  
  When the states consist of stacks of observations storing the states is
  inefficient. This class writes observations and constructs the stacked states
  at sample time.
  
  Attributes:
    add_count: int, counter of how many transitions have been added (including
      the blank ones at the beginning of an episode).
    invalid_range: np.array, an array with the indices of cursor-related invalid
      transitions
    episode_end_indices: set[int], a set of indices corresponding to the
      end of an episode.
docstr: Returns a batch of valid indices sampled uniformly.
  
  Args:
    batch_size: int, number of indices returned.
  
  Returns:
    list of ints, a batch of valid indices sampled uniformly.
  
  Raises:
    RuntimeError: If the batch was not constructed after maximum number of
      tries.
docstr_tok: ["Returns","a","batch","of","valid","indices","sampled","uniformly",".","Args",":","batch_size",":","int",",","number","of","indices","returned",".","Returns",":","list","of","ints",",","a","batch","of","valid","indices","sampled","uniformly",".","Raises",":","RuntimeError",":","If","the","batch","was","not","constructed","after","maximum","number","of","tries","."]
code: def sample_index_batch(self, batch_size):
      """Returns a batch of valid indices sampled uniformly.
  
      Args:
        batch_size: int, number of indices returned.
  
      Returns:
        list of ints, a batch of valid indices sampled uniformly.
  
      Raises:
        RuntimeError: If the batch was not constructed after maximum number of
          tries.
      """
      if self.is_full():
          # add_count >= self._replay_capacity > self._stack_size
          min_id = (
              self.cursor()
              - self._replay_capacity
              + self._stack_size
              - 1
          )
          max_id = self.cursor() - self._update_horizon
      else:
          # add_count < self._replay_capacity
          min_id = self._stack_size - 1
          max_id = self.cursor() - self._update_horizon
          if max_id <= min_id:
              raise RuntimeError(
                  "Cannot sample a batch with fewer than stack size "
                  "({}) + update_horizon ({}) transitions.".format(
                      self._stack_size, self._update_horizon
                  )
              )
  
      indices = []
      attempt_count = 0
      while (
          len(indices) < batch_size
          and attempt_count < self._max_sample_attempts
      ):
          index = (
              np.random.randint(min_id, max_id) % self._replay_capacity
          )
          if self.is_valid_transition(index):
              indices.append(index)
          else:
              attempt_count += 1
      if len(indices) != batch_size:
          raise RuntimeError(
              "Max sample attempts: Tried {} times but only sampled {}"
              " valid indices. Batch size is {}".format(
                  self._max_sample_attempts, len(indices), batch_size
              )
          )
  
      return indices
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import collections
  import gzip
  import math
  import os
  import pickle
  from absl import logging
  import gin.tf
  import numpy
  import tensorflow
label_desc: Use image node_id 21 for calling the OutOfGraphReplayBuffer obj's underlying member method code with example usage: obj.sample_index_batch(batch_size) and returns: indices