code: def kernel_based(
      X: np.ndarray,
      Y: np.ndarray,
      Z: Optional[np.ndarray] = None,
      use_bootstrap: bool = False,
      bootstrap_num_runs: int = 10,
      max_num_samples_run: int = 2000,
      bootstrap_n_jobs: Optional[int] = None,
      p_value_adjust_func: Callable[
          [Union[np.ndarray, List[float]]], float
      ] = quantile_based_fwer,
      **kwargs,
  ) -> float:
      """Prepares the data and uses kernel (conditional) independence test. The independence test estimates a p-value
      for the null hypothesis that X and Y are independent (given Z). Depending whether Z is given, a conditional or
      pairwise independence test is performed.
  
      Here, we utilize the implementations of the https://github.com/cmu-phil/causal-learn package.
  
      If Z is given: Using KCI as conditional independence test, i.e. we use https://github.com/cmu-phil/causal-learn/blob/main/causallearn/utils/KCI/KCI.py#L238.
      If Z is not given: Using KCI as pairwise independence test, i.e. we use https://github.com/cmu-phil/causal-learn/blob/main/causallearn/utils/KCI/KCI.py#L17.
  
      Note:
      - The data can be multivariate, i.e. the given input matrices can have multiple columns.
      - Categorical data need to be represented as strings.
  
      Based on the work:
      - K. Zhang, J. Peters, D. Janzing, B. Schölkopf. *Kernel-based Conditional Independence Test and Application in Causal Discovery*. UAI'11, Pages 804–813, 2011.
      - A. Gretton, K. Fukumizu, C.-H. Teo, L. Song, B. Schölkopf, A. Smola. *A Kernel Statistical Test of Independence*. NIPS 21, 2007.
  
      For more information about configuring the kernel independence test, see:
      - https://github.com/cmu-phil/causal-learn/blob/main/causallearn/utils/KCI/KCI.py#L17 (if Z is not given)
      - https://github.com/cmu-phil/causal-learn/blob/main/causallearn/utils/KCI/KCI.py#L238 (if Z is given)
  
      :param X: Data matrix for observations from X.
      :param Y: Data matrix for observations from Y.
      :param Z: Optional data matrix for observations from Z. This is the conditional variable.
      :param use_bootstrap: If True, the independence tests are performed on multiple subsets of the data and the final
                            p-value is constructed based on the provided p_value_adjust_func function.
      :param bootstrap_num_runs: Number of bootstrap runs (only relevant if use_bootstrap is True).
      :param max_num_samples_run: Maximum number of samples used in an evaluation. If use_bootstrap is True, then
                                  different samples but at most max_num_samples_run are used.
      :param bootstrap_n_jobs: Number of parallel jobs for the bootstrap runs.
      :param p_value_adjust_func: A callable that expects a numpy array of multiple p-values and returns one p-value. This
                                  is typically used a family wise error rate control method.
      :return: The p-value for the null hypothesis that X and Y are independent (given Z).
      """
      bootstrap_n_jobs = (
          config.default_n_jobs
          if bootstrap_n_jobs is None
          else bootstrap_n_jobs
      )
  
      if not use_bootstrap:
          bootstrap_num_runs = 1
  
      if "est_width" not in kwargs:
          kwargs["est_width"] = "median"
  
      def evaluate_kernel_test_on_samples(
          X: np.ndarray,
          Y: np.ndarray,
          Z: np.ndarray,
          parallel_random_seed: int,
      ) -> float:
          set_random_seed(parallel_random_seed)
  
          X = _remove_constant_columns(X)
          Y = _remove_constant_columns(Y)
  
          if X.shape[1] == 0 or Y.shape[1] == 0:
              # Either X and/or Y is constant.
              return 1.0
  
          if Z is not None:
              Z = _remove_constant_columns(Z)
              if Z.shape[1] == 0:
                  # If Z is empty, we are in the pairwise setting.
                  Z = None
  
          if Z is None:
              X, Y = _convert_to_numeric(*shape_into_2d(X, Y))
              return KCI_UInd(**kwargs).compute_pvalue(X, Y)[0]
          else:
              X, Y, Z = _convert_to_numeric(*shape_into_2d(X, Y, Z))
              return KCI_CInd(**kwargs).compute_pvalue(X, Y, Z)[0]
  
      random_indices = [
          np.random.choice(
              X.shape[0],
              min(X.shape[0], max_num_samples_run),
              replace=False,
          )
          for run in range(bootstrap_num_runs)
      ]
  
      random_seeds = np.random.randint(
          np.iinfo(np.int32).max, size=len(random_indices)
      )
      p_values = Parallel(n_jobs=bootstrap_n_jobs)(
          delayed(evaluate_kernel_test_on_samples)(
              X[indices],
              Y[indices],
              Z[indices] if Z is not None else None,
              int(random_seed),
          )
          for indices, random_seed in zip(random_indices, random_seeds)
      )
  
      return p_value_adjust_func(p_values)
  
file_path: dowhy/dowhy/gcm/independence_test/kernel.py
imports: from typing import Callable, List, Optional, Union
  import numpy
  import scipy
  from causallearn.utils.KCI.KCI import KCI_CInd, KCI_UInd
  from joblib import Parallel, delayed
  from sklearn.preprocessing import scale
  import dowhy.gcm.config
  from dowhy.gcm.independence_test.kernel_operation import approximate_rbf_kernel_features
  from dowhy.gcm.stats import quantile_based_fwer
  from dowhy.gcm.util.general import auto_apply_encoders, auto_fit_encoders, set_random_seed, shape_into_2d
label_desc: Use image node_id 1 for calling a global function with example usage: kernel_based(X, Y, Z, use_bootstrap, bootstrap_num_runs, max_num_samples_run, bootstrap_n_jobs, p_value_adjust_func) and returns: p_value_adjust_func, int, KCI_UInd, KCI_CInd