code: def test_given_simple_causal_chain_with_linear_relationships_when_attribute_anomaly_scores_with_it_score_then_returns_qualitatively_correct_results():
      num_training_samples = 5000
      X0 = np.random.normal(0, 1, num_training_samples)
      X1 = X0 + np.random.normal(0, 1, num_training_samples)
      X2 = X1 + np.random.normal(0, 1, num_training_samples)
      X3 = X2 + np.random.normal(0, 1, num_training_samples)
      training_data = pd.DataFrame(
          {"X0": X0, "X1": X1, "X2": X2, "X3": X3}
      )
  
      causal_model = InvertibleStructuralCausalModel(
          nx.DiGraph([("X0", "X1"), ("X1", "X2"), ("X2", "X3")])
      )
      auto.assign_causal_mechanisms(
          causal_model, training_data, auto.AssignmentQuality.GOOD
      )
  
      fit(causal_model, training_data)
  
      # Three examples:
      # 1. X1 is the root cause (+ 10 to the noise)
      # 2. X0 is the root cause (+ 10 to the noise)
      # 3. X0 and X3 are both root causes (+ 10 to both noises)
      anomaly_data = pd.DataFrame(
          {
              "X0": np.array([0, 10, 10]),
              "X1": np.array([10, 10, 10]),
              "X2": np.array([10, 10, 10]),
              "X3": np.array([10, 10, 20]),
          }
      )
  
      scores = attribute_anomalies(
          causal_model,
          "X3",
          anomaly_data,
          anomaly_scorer=MedianCDFQuantileScorer(),
          num_distribution_samples=num_training_samples,
          attribute_mean_deviation=False,
      )
  
      def total_anomaly_score(training_data, anomaly_sample):
          distribution_samples = training_data["X3"].to_numpy()
          anomaly_scorer = MedianCDFQuantileScorer()
          anomaly_scorer.fit(distribution_samples)
          return -np.log(
              _relative_frequency(
                  anomaly_scorer.score(distribution_samples)
                  >= anomaly_scorer.score(anomaly_sample)
              )
          )
  
      assert scores["X0"][0] < 0.5  # Not anomalous.
      assert scores["X0"][1] > 8  # Anomalous, but the only root cause.
      assert scores["X0"][2] > 4  # Anomalous together with X1.
      assert scores["X1"][0] > 8  # Anomalous, but the only root cause.
      assert (
          scores["X1"][1] < 0.5
      )  # Not anomalous given upstream nodes.
      assert (
          scores["X1"][2] < 0.5
      )  # Not anomalous given upstream nodes.
      assert np.all(
          scores["X2"] < 0.5
      )  # Not anomalous given upstream nodes.
      assert np.all(
          scores["X3"][:2] < 0.5
      )  # Not anomalous given upstream nodes.
      assert scores["X3"][2] > 4  # Anomalous together with X0.
  
      # The sum of the scores should add up to the anomaly score of the target (here, X3).
      assert scores["X0"][0] + scores["X1"][0] + scores["X2"][
          0
      ] + scores["X3"][0] == approx(
          total_anomaly_score(
              training_data, anomaly_data["X3"].to_numpy()[0]
          ),
          abs=0.001,
      )
      assert scores["X0"][1] + scores["X1"][1] + scores["X2"][
          1
      ] + scores["X3"][1] == approx(
          total_anomaly_score(
              training_data, anomaly_data["X3"].to_numpy()[1]
          ),
          abs=0.001,
      )
      assert scores["X0"][2] + scores["X1"][2] + scores["X2"][
          2
      ] + scores["X3"][2] == approx(
          total_anomaly_score(
              training_data, anomaly_data["X3"].to_numpy()[2]
          ),
          abs=0.001,
      )
  
file_path: dowhy/tests/gcm/test_anomaly_attribution.py
imports: import networkx
  import numpy
  import pandas
  from flaky import flaky
  from pytest import approx
  from dowhy.gcm import AdditiveNoiseModel, InverseDensityScorer, InvertibleStructuralCausalModel, MedianCDFQuantileScorer, attribute_anomalies, auto, fit
  from dowhy.gcm.anomaly import _relative_frequency, attribute_anomaly_scores
  from dowhy.gcm.density_estimators import GaussianMixtureDensityEstimator
  from dowhy.gcm.ml import PredictionModel
label_desc: Use image node_id 3 for calling a global function with example usage: test_given_simple_causal_chain_with_linear_relationships_when_attribute_anomaly_scores_with_it_score_then_returns_qualitatively_correct_results() and returns: unknown