def sample(self, batch_size, beta=0):
        """
        WARNING: This function does not make deep copies of the tuple experiences.
                 This means that if any objects in the experiences are modified,
                 the contents of the replay buffer memory will also be modified,
                 so be careful!!!
        Sample a batch of experiences, along with their importance weights, and the 
        indices of the sampled experiences in the buffer.
        :param batch_size: int
            How many transitions to sample.
        :param beta: float
            To what degree to use importance weights
            (0 - no corrections, 1 - full correction)
        :return experience_batch: List
            List of tuples, length batch_size, corresponding to the experiences sampled.
        :return weights: np.array
            Array of shape (batch_size,) and dtype np.float32
            denoting importance weight of each sampled transition
        :return indices: np.array
            Array of shape (batch_size,) and dtype np.int32
            indices in buffer of sampled experiences
        """
        with self.sample_time.time():
            assert beta >= 0

            # sample the experiences proportional to their priorities
            indices = self._sample_proportional(batch_size)
            response = [self._storage[idx] for idx in indices]

            # compute importance weights for the experiences to correct for distribution shift
            weights = []
            p_min = self._it_min.min() / self._it_sum.sum()
            max_weight = (p_min * len(self._storage)) ** (-beta)

            for idx in indices:
                p_sample = self._it_sum[idx] / self._it_sum.sum()
                weight = (p_sample * len(self._storage)) ** (-beta)
                weights.append(weight / max_weight)
            weights = np.array(weights)

        # return response, weights, indices
        return response

        WARNING: This function does not make deep copies of the tuple experiences.
                 This means that if any objects in the experiences are modified,
                 the contents of the replay buffer memory will also be modified,
                 so be careful!!!
        Sample a batch of experiences, along with their importance weights, and the 
        indices of the sampled experiences in the buffer.
        :param batch_size: int
            How many transitions to sample.
        :param beta: float
            To what degree to use importance weights
            (0 - no corrections, 1 - full correction)
        :return experience_batch: List
            List of tuples, length batch_size, corresponding to the experiences sampled.
        :return weights: np.array
            Array of shape (batch_size,) and dtype np.float32
            denoting importance weight of each sampled transition
        :return indices: np.array
            Array of shape (batch_size,) and dtype np.int32
            indices in buffer of sampled experiences
        
surreal/replay/prioritized_replay.py