class_docstr: Circular experience replay buffer for `CounterfactualRL` (DDPG). When the buffer is filled, then the oldest
  experience is replaced by the new one (FIFO). The experience batch size is kept constant and inferred when
  the first batch of data is stored. Allowing flexible batch size can generate `tensorflow` warning due to
  the `tf.function` retracing, which can lead to a drop in performance.
class_docstr_tok: ["Circular","experience","replay","buffer","for","`","CounterfactualRL","`","(","DDPG",")",".","When","the","buffer","is","filled",",","then","the","oldest","experience","is","replaced","by","the","new","one","(","FIFO",")",".","The","experience","batch","size","is","kept","constant","and","inferred","when","the","first","batch","of","data","is","stored",".","Allowing","flexible","batch","size","can","generate","`","tensorflow","`","warning","due","to","the","`","tf.function","`","retracing",",","which","can","lead","to","a","drop","in","performance","."]
docstr: Adds experience to the replay buffer. When the buffer is filled, then the oldest experience is replaced
  by the new one (FIFO).
  
  Parameters
  ----------
  X
      Input array.
  Y_m
      Model's prediction class of `X`.
  Y_t
      Counterfactual target class.
  Z
      Input's embedding.
  Z_cf_tilde
      Noised counterfactual embedding.
  C
      Conditional array.
  R_tilde
      Noised counterfactual reward array.
  **kwargs
      Other arguments. Not used.
code: def append(
      self,
      X: np.ndarray,
      Y_m: np.ndarray,
      Y_t: np.ndarray,
      Z: np.ndarray,
      Z_cf_tilde: np.ndarray,
      C: Optional[np.ndarray],
      R_tilde: np.ndarray,
      **kwargs,
  ) -> None:
      """
      Adds experience to the replay buffer. When the buffer is filled, then the oldest experience is replaced
      by the new one (FIFO).
  
      Parameters
      ----------
      X
          Input array.
      Y_m
          Model's prediction class of `X`.
      Y_t
          Counterfactual target class.
      Z
          Input's embedding.
      Z_cf_tilde
          Noised counterfactual embedding.
      C
          Conditional array.
      R_tilde
          Noised counterfactual reward array.
      **kwargs
          Other arguments. Not used.
      """
      # Initialize the buffers.
      if not hasattr(self, "X"):
          self.batch_size = X.shape[0]
  
          # Allocate memory.
          self.X = np.zeros(
              (self.size * self.batch_size, *X.shape[1:]),
              dtype=np.float32,
          )
          self.Y_m = np.zeros(
              (self.size * self.batch_size, *Y_m.shape[1:]),
              dtype=np.float32,
          )
          self.Y_t = np.zeros(
              (self.size * self.batch_size, *Y_t.shape[1:]),
              dtype=np.float32,
          )
          self.Z = np.zeros(
              (self.size * self.batch_size, *Z.shape[1:]),
              dtype=np.float32,
          )
          self.Z_cf_tilde = np.zeros(
              (self.size * self.batch_size, *Z_cf_tilde.shape[1:]),
              dtype=np.float32,
          )
          self.R_tilde = np.zeros(
              (self.size * self.batch_size, *R_tilde.shape[1:]),
              dtype=np.float32,
          )
  
          # Conditional tensor can be `None` when no condition is included. If it is not `None`, allocate memory.
          if C is not None:
              self.C = np.zeros(
                  (self.size * self.batch_size, *C.shape[1:]),
                  dtype=np.float32,
              )
  
      # Increase the length of the buffer if not full.
      if self.len < self.size:
          self.len += 1
  
      # Compute the first position where to add most recent experience.
      start = self.batch_size * self.idx
  
      # Add new data / replace old experience (note that a full batch is added at once).
      self.X[start : start + self.batch_size] = X
      self.Y_m[start : start + self.batch_size] = Y_m
      self.Y_t[start : start + self.batch_size] = Y_t
      self.Z[start : start + self.batch_size] = Z
      self.Z_cf_tilde[start : start + self.batch_size] = Z_cf_tilde
      self.R_tilde[start : start + self.batch_size] = R_tilde
  
      if C is not None:
          self.C = cast(
              np.ndarray, self.C
          )  # helping mypy out as self.C cannot be None at this point
          self.C[start : start + self.batch_size] = C
  
      # Compute the next index. Not that if the buffer reached its maximum capacity, for the next iteration
      # we start replacing old batches.
      self.idx = (self.idx + 1) % self.size
  
imports: import logging
  import os
  from abc import ABC, abstractmethod
  from copy import deepcopy
  from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Union, cast
  import numpy
  from tqdm import tqdm
  from alibi.api.defaults import DEFAULT_DATA_CFRL, DEFAULT_META_CFRL
  from alibi.api.interfaces import Explainer, Explanation, FitMixin
  from alibi.explainers.backends.cfrl_base import generate_empty_condition, get_classification_reward, get_hard_distribution, identity_function
  from alibi.utils.frameworks import Framework, has_pytorch, has_tensorflow
label_desc: Use image node_id 2 for calling the ReplayBuffer obj's underlying member method code with example usage: obj.append(X, Y_m, Y_t, Z, Z_cf_tilde, C, R_tilde) without return types