code: def compute_score_lira(
      stat_target: Union[np.ndarray, Sequence[float]],
      stat_in: Sequence[np.ndarray],
      stat_out: Sequence[np.ndarray],
      option: str = "both",
      fix_variance: bool = False,
      median_or_mean: str = "median",
  ) -> np.ndarray:
      """Computes score of each sample using Gaussian distribution fitting.
  
      Args:
        stat_target: a list or numpy array where stat_target[i] is the statistics of
          example i computed from the target model. stat_target[i] is an array of k
          scalars for k being the number of augmentations for each sample.
        stat_in: a list where stat_in[i] is the in-training statistics of example i.
          stat_in[i] is a m by k numpy array where m is the number of shadow models
          and k is the number of augmentations for each sample. m can be different
          for different examples.
        stat_out: a list where stat_out[i] is the out-training statistics of example
          i. stat_out[i] is a m by k numpy array where m is the number of shadow
          models and k is the number of augmentations for each sample.
        option: using stat_in ("in"), stat_out ("out"), or both ("both").
        fix_variance: whether to use the same variance for all examples.
        median_or_mean: use median or mean across shadow models.
  
      Returns:
        log(Pr(out)) - log(Pr(in)), log(Pr(out)), or -log(Pr(in)) depending on the
        option. In-training sample is expected to have small value.
        The idea is from https://arxiv.org/pdf/2112.03570.pdf.
      """
      # median of statistics across shadow models
      if option not in ["both", "in", "out"]:
          raise ValueError('option should be "both", "in", or "out".')
      if median_or_mean not in ["median", "mean"]:
          raise ValueError(
              'median_or_mean should be either "median" or "mean".'
          )
      if option in ["in", "both"]:
          if any([s.ndim != 2 for s in stat_in]):
              raise ValueError(
                  "Each element in stat_in should be a 2-d numpy array."
              )
          if any([s.shape[1] != stat_in[0].shape[1] for s in stat_in]):
              raise ValueError(
                  "Each element in stat_in should have the same size "
                  "in the second dimension."
              )
      if option in ["out", "both"]:
          if any([s.ndim != 2 for s in stat_out]):
              raise ValueError(
                  "Each element in stat_out should be a 2-d numpy array."
              )
          if any(
              [s.shape[1] != stat_out[0].shape[1] for s in stat_out]
          ):
              raise ValueError(
                  "Each element in stat_out should have the same size "
                  "in the second dimension."
              )
  
      func_avg = functools.partial(
          np.nanmedian if median_or_mean == "median" else np.nanmean,
          axis=0,
      )
      if option in ["in", "both"]:
          avg_in = np.array(
              list(map(func_avg, stat_in))
          )  # n by k array
          replace_nan_with_column_mean(
              avg_in
          )  # use column average in case of NaN
      if option in ["out", "both"]:
          avg_out = np.array(list(map(func_avg, stat_out)))
          replace_nan_with_column_mean(avg_out)
  
      if fix_variance:
          # standard deviation of statistics across shadow models and examples
          if option in ["in", "both"]:
              std_in = np.nanstd(
                  np.concatenate(
                      [
                          l - m[np.newaxis]
                          for l, m in zip(stat_in, avg_in)
                      ]
                  )
              )
          if option in ["out", "both"]:
              std_out = np.nanstd(
                  np.concatenate(
                      [
                          l - m[np.newaxis]
                          for l, m in zip(stat_out, avg_out)
                      ]
                  )
              )
      else:
          # standard deviation of statistics across shadow models
          func_std = functools.partial(np.nanstd, axis=0)
          if option in ["in", "both"]:
              std_in = np.array(list(map(func_std, stat_in)))
              replace_nan_with_column_mean(std_in)
          if option in ["out", "both"]:
              std_out = np.array(list(map(func_std, stat_out)))
              replace_nan_with_column_mean(std_out)
  
      stat_target = np.array(stat_target)
      if option in ["in", "both"]:
          log_pr_in = scipy.stats.norm.logpdf(
              stat_target, avg_in, std_in + 1e-30
          )
      if option in ["out", "both"]:
          log_pr_out = scipy.stats.norm.logpdf(
              stat_target, avg_out, std_out + 1e-30
          )
  
      if option == "both":
          scores = -(log_pr_in - log_pr_out).mean(axis=1)
      elif option == "in":
          scores = -log_pr_in.mean(axis=1)
      else:
          scores = log_pr_out.mean(axis=1)
      return scores
  
file_path: privacy/tensorflow_privacy/privacy/privacy_tests/membership_inference_attack/advanced_mia.py
imports: import functools
  from typing import Optional, Sequence, Union
  import numpy
  import scipy.stats
  from tensorflow_privacy.privacy.privacy_tests.utils import log_loss
label_desc: Use image node_id 3 for calling a global function with example usage: compute_score_lira(stat_target, stat_in, stat_out, option, fix_variance, median_or_mean) and returns: scores