code: def linear_dataset(
      beta,
      num_common_causes,
      num_samples,
      num_instruments=0,
      num_effect_modifiers=0,
      num_treatments=None,
      num_frontdoor_variables=0,
      treatment_is_binary=True,
      treatment_is_category=False,
      outcome_is_binary=False,
      stochastic_discretization=True,
      num_discrete_common_causes=0,
      num_discrete_instruments=0,
      num_discrete_effect_modifiers=0,
      stddev_treatment_noise=1,
      stddev_outcome_noise=0.01,
      one_hot_encode=False,
  ):
      """
      Generate a synthetic dataset with a known effect size.
  
      This function generates a pandas dataFrame with num_samples records. The variables follow a naming convention where the first letter indicates its role in the causality graph and then a sequence number.
  
      :param beta: coefficient of the treatment(s) ('v?') in the generating equation of the outcome ('y').
      :type beta: int or list/ndarray of length num_treatments of type int
      :param num_common_causes: Number of variables affecting both the treatment and the outcome [w -> v; w -> y]
      :type num_common_causes: int
      :param num_samples: Number of records to generate
      :type num_samples: int
      :param num_instruments: Number of instrumental variables  [z -> v], defaults to 0
      :type num_instruments: int
      :param num_effect_modifiers: Number of effect modifiers, variables affecting only the outcome [x -> y], defaults to 0
      :type num_effect_modifiers: int
      :param num_treatments: Number of treatment variables [v]. By default inferred from the beta argument. When provided, beta is recycled to match num_treatments.
      :type num_treatments : Union[None, int]
      :param num_frontdoor_variables : Number of frontdoor mediating variables [v -> FD -> y], defaults to  0
      :type num_frontdoor_variables: int
      :param treatment_is_binary: Cannot be True if treatment_is_category is True, defaults to True
      :type treatment_is_binary: bool
      :param treatment_is_category: Cannot be True if treatment_is_binary is True, defaults to False
      :type treatment_is_category: bool
      :param outcome_is_binary: defaults to False,
      :type outcome_is_binary: bool
      :param stochastic_discretization: if False, quartiles are used when discretised variables are specified. They can be hot encoded, defaults True
      :type stochastic_discretization: bool
      :param num_discrete_common_causes: Number of discrete common causes of the total num_common_causes, defaults to 0
      :type num_discrete_common_causes: int
      :param num_discrete_instruments: Number of discrete instrumental variables of the total num_instruments, defaults to 0
      :type num_discrete_instruments  : int
      :param num_discrete_effect_modifiers : Number of discrete effect modifiers of the total effect_modifiers, defaults to 0
      :type num_discrete_effect_modifiers: int
      :param stddev_treatment_noise : defaults to 1
      :type stddev_treatment_noise : float
      :param stddev_outcome_noise: defaults to 0.01
      :type stddev_outcome_noise: float
      :param one_hot_encode: defaults to False
      :type one_hot_encode: bool
  
      :returns: Dictionary with pandas dataFrame and few other metadata variables.
                          "df": pd.dataFrame
                          with num_samples records. The variables follow a naming convention were the first letter indicates its role in the causality graph and then a sequence number.
  
                      v variables - are the treatments. They can be binary or continuous. In the case of continuous abs(*beta*) defines thier magnitude;
  
                      y - is the outcome variable. The generating equation is,
                       y = normal(0, stddev_outcome_noise) + t @ beta [where @ is a numpy matrix multiplication allowing for beta be a vector]
  
                      W variables - commonly cause both the treatment and the outcome and are iid. if continuous, they are Norm(mu = Unif(-1,1), sigma = 1)
  
                      Z variables - Instrument variables. Each one affects all treatments. i.e. if there is one instrument and two treatments then z0->v0, z0->v1
  
                      X variables - effect modifiers. If continuous, they are Norm(mu = Unif(-1,1), sigma = 1)
  
                      FD variables - Front door variables, v0->FD0->y
  
              "treatment_name": str/list(str)
              "outcome_name": str
              "common_causes_names": str/list(str)
              "instrument_names": str/list(str)
              "effect_modifier_names": str/list(str)
              "frontdoor_variables_names": str/list(str)
              "dot_graph": dot_graph,
              "gml_graph": gml_graph,
              "ate": float, the true ate in the dataset
      :rtype: dict
  
      Examples
      ********
      .. code-block:: python
              import networkx as nx
              import matplotlib.pyplot as plt
              import pandas as pd
              import numpy as np
              import dowhy.datasets
  
              def plot_gml(gml_graph):
                      G = nx.parse_gml(gml_graph)
                      pos=nx.spring_layout(G)
                      nx.draw_networkx(G, pos, with_labels=True, node_size=1000, node_color="darkorange")
                      return(plt.show())
  
              def describe_synthetic_data(synthetic_data):
                      if (synthetic_data['gml_graph'] != None) :
                      plot_gml(synthetic_data["gml_graph"])
                      synthetic_data_df=synthetic_data["df"]
                      print('------- Variables --------')
                      print('Treatment vars:'      , synthetic_data['treatment_name'])
                      print('Outcome vars:'        , synthetic_data['outcome_name'])
                      print('Common causes vars:'  , synthetic_data['common_causes_names'])
                      print('Instrument vars:'     , synthetic_data['instrument_names'])
                      print('Effect Modifier vars:', synthetic_data['effect_modifier_names'])
                      print('Frontdoor vars:'      , synthetic_data['frontdoor_variables_names'])
                      print('Treatment vars:', synthetic_data['outcome_name'])
                      print('-------- Corr -------')
                      print(synthetic_data_df.corr())
                      print('------- Head --------')
                      return(synthetic_data_df)
  
              # create a dataset with 10 observations one binary treatment and a continuous outcome affected by one common cause
              synthetic_data = dowhy.datasets.linear_dataset(beta = 100,
                      num_common_causes = 1,
                      num_samples =10
                      )
              describe_synthetic_data(synthetic_data).head()
  
              # Two continuous treatments, no common cause, an instrumental variable and two effect modifiers - linearly added appropriately
              synthetic_data = dowhy.datasets.linear_dataset(
                      beta                          = 100,
                      num_common_causes             = 0,
                      num_samples                   = 20,
                      num_instruments               = 1,
                      num_effect_modifiers          = 2,
                      num_treatments                = 2,
                      num_frontdoor_variables       = 0,
                      treatment_is_binary           = False,
                      treatment_is_category         = False,
                      outcome_is_binary             = False,
                      stochastic_discretization     = True,
                      num_discrete_common_causes    = 0,
                      num_discrete_instruments      = 0,
                      num_discrete_effect_modifiers = 0,
                      stddev_treatment_noise        = 1,
                      stddev_outcome_noise          = 0.01,
                      one_hot_encode                = False
                      )
              describe_synthetic_data(synthetic_data).head()
  
              # One Hot Encoding
              synthetic_data = dowhy.datasets.linear_dataset(
                      beta                          = 100,
                      num_common_causes             =   2,
                      num_samples                   =  20,
                      num_instruments               =   1,
                      num_effect_modifiers          =   1,
                      num_treatments                =   1,
                      num_frontdoor_variables       =   1,
                      treatment_is_binary           = False,
                      treatment_is_category         = False,
                      outcome_is_binary             = False,
                      stochastic_discretization     = True,
                      num_discrete_common_causes    = 1, #of the total num_common_causes
                      num_discrete_instruments      = 1,
                      num_discrete_effect_modifiers = 1,
                      stddev_treatment_noise        = 1,
                      stddev_outcome_noise          = 0.01,
                      one_hot_encode                = True
                      )
                      describe_synthetic_data(synthetic_data).head()
      """
      assert not (treatment_is_binary and treatment_is_category)
      W, X, Z, FD, c1, c2, ce, cz, cfd1, cfd2 = [None] * 10
      W_with_dummy, X_with_categorical = (None, None)
      beta = np.array(beta, dtype=np.float64, ndmin=1)
      if num_treatments is None:
          num_treatments = beta.size
      else:
          beta = np.resize(beta, num_treatments)
      num_cont_common_causes = (
          num_common_causes - num_discrete_common_causes
      )
      num_cont_effect_modifiers = (
          num_effect_modifiers - num_discrete_effect_modifiers
      )
      if num_common_causes > 0:
          range_c1 = 0.5 + np.max(np.absolute(beta)) * 0.5
          range_c2 = 0.5 + np.max(np.absolute(beta)) * 0.5
          means = np.random.uniform(-1, 1, num_common_causes)
          cov_mat = np.diag(np.ones(num_common_causes))
          W = np.random.multivariate_normal(means, cov_mat, num_samples)
          W_with_dummy = convert_to_categorical(
              W,
              num_common_causes,
              num_discrete_common_causes,
              quantiles=[0.25, 0.5, 0.75],
              one_hot_encode=one_hot_encode,
          )
          c1 = np.random.uniform(
              0, range_c1, (W_with_dummy.shape[1], num_treatments)
          )
          c2 = np.random.uniform(0, range_c2, W_with_dummy.shape[1])
  
      if num_instruments > 0:
          range_cz = 1 + np.max(np.absolute(beta))
          p = np.random.uniform(0, 1, num_instruments)
          Z = np.zeros((num_samples, num_instruments))
          for i in range(num_instruments):
              if (i % 2) == 0:
                  Z[:, i] = np.random.binomial(
                      n=1, p=p[i], size=num_samples
                  )
              else:
                  Z[:, i] = np.random.uniform(0, 1, size=num_samples)
          # TODO Ensure that we do not generate weak instruments
          cz = np.random.uniform(
              range_cz - (range_cz * 0.05),
              range_cz + (range_cz * 0.05),
              (num_instruments, num_treatments),
          )
      if num_effect_modifiers > 0:
          range_ce = 0.5 + np.max(np.absolute(beta)) * 0.5
          means = np.random.uniform(-1, 1, num_effect_modifiers)
          cov_mat = np.diag(np.ones(num_effect_modifiers))
          X = np.random.multivariate_normal(means, cov_mat, num_samples)
          X_with_categorical = convert_to_categorical(
              X,
              num_effect_modifiers,
              num_discrete_effect_modifiers,
              quantiles=[0.25, 0.5, 0.75],
              one_hot_encode=one_hot_encode,
          )
          ce = np.random.uniform(
              0, range_ce, X_with_categorical.shape[1]
          )
      # TODO - test all our methods with random noise added to covariates (instead of the stochastic treatment assignment)
  
      t = np.random.normal(
          0, stddev_treatment_noise, (num_samples, num_treatments)
      )
      if num_common_causes > 0:
          t += W_with_dummy @ c1  # + np.random.normal(0, 0.01)
      if num_instruments > 0:
          t += Z @ cz
      # Converting treatment to binary if required
      if treatment_is_binary:
          t = np.vectorize(convert_to_binary)(t)
      elif treatment_is_category:
          t = np.vectorize(
              stochastically_convert_to_three_level_categorical
          )(t)
  
      # Generating frontdoor variables if asked for
      if num_frontdoor_variables > 0:
          range_cfd1 = np.max(np.absolute(beta)) * 0.5
          range_cfd2 = np.max(np.absolute(beta)) * 0.5
          cfd1 = np.random.uniform(
              0, range_cfd1, (num_treatments, num_frontdoor_variables)
          )
          cfd2 = np.random.uniform(
              0, range_cfd2, num_frontdoor_variables
          )
          FD_noise = np.random.normal(
              0, 1, (num_samples, num_frontdoor_variables)
          )
          FD = FD_noise
          FD += t @ cfd1
          if num_common_causes > 0:
              range_c1_frontdoor = range_c1 / 10.0
              c1_frontdoor = np.random.uniform(
                  0,
                  range_c1_frontdoor,
                  (W_with_dummy.shape[1], num_frontdoor_variables),
              )
              FD += W_with_dummy @ c1_frontdoor
  
      def _compute_y(
          t, W, X, FD, beta, c2, ce, cfd2, stddev_outcome_noise
      ):
          y = np.random.normal(0, stddev_outcome_noise, num_samples)
          if num_frontdoor_variables > 0:
              y += FD @ cfd2
          else:
              # NOTE: We are assuming a linear relationship *even when t is categorical* and integer coded.
              # For categorical t, this example dataset has the effect size for category 2 being exactly
              # double the effect for category 1
              # This could be changed at this stage by one-hot encoding t and using a custom beta that
              # sets a different effect for each category {0, 1, 2}
              y += t @ beta
          if num_common_causes > 0:
              y += W @ c2
          if num_effect_modifiers > 0:
              y += (X @ ce) * np.prod(t, axis=1)
          if outcome_is_binary:
              y = np.vectorize(convert_to_binary)(
                  y, stochastic_discretization
              )
          return y
  
      y = _compute_y(
          t,
          W_with_dummy,
          X_with_categorical,
          FD,
          beta,
          c2,
          ce,
          cfd2,
          stddev_outcome_noise,
      )
  
      data = np.column_stack((t, y))
      if num_common_causes > 0:
          data = np.column_stack((W_with_dummy, data))
      if num_instruments > 0:
          data = np.column_stack((Z, data))
      if num_effect_modifiers > 0:
          data = np.column_stack((X_with_categorical, data))
      if num_frontdoor_variables > 0:
          data = np.column_stack((FD, data))
  
      # Computing ATE
      FD_T1, FD_T0 = None, None
      T1 = np.ones((num_samples, num_treatments))
      T0 = np.zeros((num_samples, num_treatments))
      if num_frontdoor_variables > 0:
          FD_T1 = FD_noise + (T1 @ cfd1)
          FD_T0 = FD_noise + (T0 @ cfd1)
      ate = np.mean(
          _compute_y(
              T1,
              W_with_dummy,
              X_with_categorical,
              FD_T1,
              beta,
              c2,
              ce,
              cfd2,
              stddev_outcome_noise,
          )
          - _compute_y(
              T0,
              W_with_dummy,
              X_with_categorical,
              FD_T0,
              beta,
              c2,
              ce,
              cfd2,
              stddev_outcome_noise,
          )
      )
  
      treatments = [("v" + str(i)) for i in range(0, num_treatments)]
      outcome = "y"
      # constructing column names for one-hot encoded discrete features
      common_causes = construct_col_names(
          "W",
          num_common_causes,
          num_discrete_common_causes,
          num_discrete_levels=4,
          one_hot_encode=one_hot_encode,
      )
      instruments = [("Z" + str(i)) for i in range(0, num_instruments)]
      frontdoor_variables = [
          ("FD" + str(i)) for i in range(0, num_frontdoor_variables)
      ]
      effect_modifiers = construct_col_names(
          "X",
          num_effect_modifiers,
          num_discrete_effect_modifiers,
          num_discrete_levels=4,
          one_hot_encode=one_hot_encode,
      )
      other_variables = None
      col_names = (
          frontdoor_variables
          + effect_modifiers
          + instruments
          + common_causes
          + treatments
          + [outcome]
      )
      data = pd.DataFrame(data, columns=col_names)
      # Specifying the correct dtypes
      if treatment_is_binary:
          data = data.astype(
              {tname: "bool" for tname in treatments}, copy=False
          )
      elif treatment_is_category:
          data = data.astype(
              {tname: "category" for tname in treatments}, copy=False
          )
      if outcome_is_binary:
          data = data.astype({outcome: "bool"}, copy=False)
      if num_discrete_common_causes > 0 and not one_hot_encode:
          data = data.astype(
              {
                  wname: "int64"
                  for wname in common_causes[num_cont_common_causes:]
              },
              copy=False,
          )
          data = data.astype(
              {
                  wname: "category"
                  for wname in common_causes[num_cont_common_causes:]
              },
              copy=False,
          )
      if num_discrete_effect_modifiers > 0 and not one_hot_encode:
          data = data.astype(
              {
                  emodname: "int64"
                  for emodname in effect_modifiers[
                      num_cont_effect_modifiers:
                  ]
              },
              copy=False,
          )
          data = data.astype(
              {
                  emodname: "category"
                  for emodname in effect_modifiers[
                      num_cont_effect_modifiers:
                  ]
              },
              copy=False,
          )
  
      # Now specifying the corresponding graph strings
      dot_graph = create_dot_graph(
          treatments,
          outcome,
          common_causes,
          instruments,
          effect_modifiers,
          frontdoor_variables,
      )
      # Now writing the gml graph
      gml_graph = create_gml_graph(
          treatments,
          outcome,
          common_causes,
          instruments,
          effect_modifiers,
          frontdoor_variables,
      )
      ret_dict = {
          "df": data,
          "treatment_name": treatments,
          "outcome_name": outcome,
          "common_causes_names": common_causes,
          "instrument_names": instruments,
          "effect_modifier_names": effect_modifiers,
          "frontdoor_variables_names": frontdoor_variables,
          "dot_graph": dot_graph,
          "gml_graph": gml_graph,
          "ate": ate,
      }
      return ret_dict
  
imports: import math
  import string
  import networkx
  import numpy
  import pandas
  import scipy.stats
  from numpy.random import choice
  from scipy.stats import bernoulli, halfnorm, poisson, uniform
  from sklearn.neural_network import MLPRegressor
  from dowhy.utils.graph_operations import add_edge, del_edge, get_random_node_pair, get_simple_ordered_tree
label_desc: Use image node_id 6 for calling a global function with example usage: linear_dataset(beta, num_common_causes, num_samples, num_instruments, num_effect_modifiers, num_treatments, num_frontdoor_variables, treatment_is_binary, treatment_is_category, outcome_is_binary, stochastic_discretization, num_discrete_common_causes, num_discrete_instruments, num_discrete_effect_modifiers, stddev_treatment_noise, stddev_outcome_noise, one_hot_encode) and returns: ret_dict, y