code: def construct_mediation_estimand(
      estimand_type: EstimandType,
      action_nodes: List[str],
      outcome_nodes: List[str],
      mediator_nodes: List[str],
  ):
      # TODO: support multivariate treatments better.
      expr = None
      if estimand_type in (
          EstimandType.NONPARAMETRIC_NDE,
          EstimandType.NONPARAMETRIC_NIE,
      ):
          outcome_nodes = outcome_nodes[0]
          sym_outcome = spstats.Normal(outcome_nodes, 0, 1)
          sym_treatment_symbols = [
              spstats.Normal(t, 0, 1) for t in action_nodes
          ]
          sym_treatment = sp.Array(sym_treatment_symbols)
          sym_mediators_symbols = [
              sp.Symbol(inst) for inst in mediator_nodes
          ]
          sym_mediators = sp.Array(sym_mediators_symbols)
          sym_outcome_derivative = sp.Derivative(
              sym_outcome, sym_mediators
          )
          sym_treatment_derivative = sp.Derivative(
              sym_mediators, sym_treatment
          )
          # For direct effect
          num_expr_str = outcome_nodes
          if len(mediator_nodes) > 0:
              num_expr_str += "|" + ",".join(mediator_nodes)
          sym_mu = sp.Symbol("mu")
          sym_sigma = sp.Symbol("sigma", positive=True)
          sym_conditional_outcome = spstats.Normal(
              num_expr_str, sym_mu, sym_sigma
          )
          sym_directeffect_derivative = sp.Derivative(
              sym_conditional_outcome, sym_treatment
          )
          if estimand_type == EstimandType.NONPARAMETRIC_NIE:
              sym_effect = spstats.Expectation(
                  sym_treatment_derivative * sym_outcome_derivative
              )
          elif estimand_type == EstimandType.NONPARAMETRIC_NDE:
              sym_effect = spstats.Expectation(
                  sym_directeffect_derivative
              )
          sym_assumptions = {
              "Mediation": (
                  "{2} intercepts (blocks) all directed paths from {0} to {1} except the path {{{0}}}\N{RIGHTWARDS ARROW}{{{1}}}."
              ).format(
                  ",".join(action_nodes),
                  ",".join(outcome_nodes),
                  ",".join(mediator_nodes),
              ),
              "First-stage-unconfoundedness": (
                  "If U\N{RIGHTWARDS ARROW}{{{0}}} and U\N{RIGHTWARDS ARROW}{{{1}}}"
                  " then P({1}|{0},U) = P({1}|{0})"
              ).format(
                  ",".join(action_nodes), ",".join(mediator_nodes)
              ),
              "Second-stage-unconfoundedness": (
                  "If U\N{RIGHTWARDS ARROW}{{{2}}} and U\N{RIGHTWARDS ARROW}{1}"
                  " then P({1}|{2}, {0}, U) = P({1}|{2}, {0})"
              ).format(
                  ",".join(action_nodes),
                  outcome_nodes,
                  ",".join(mediator_nodes),
              ),
          }
      else:
          raise ValueError(
              "Estimand type not supported. Supported estimand types are {0} or {1}'.".format(
                  EstimandType.NONPARAMETRIC_NDE,
                  EstimandType.NONPARAMETRIC_NIE,
              )
          )
  
      estimand = {
          "estimand": sym_effect,
          "assumptions": sym_assumptions,
      }
      return estimand
  
imports: import itertools
  import logging
  from enum import Enum
  from typing import Dict, List, Optional, Union
  import networkx
  import sympy
  import sympy.stats
  from dowhy.causal_identifier.efficient_backdoor import EfficientBackdoor
  from dowhy.causal_identifier.identified_estimand import IdentifiedEstimand
  from dowhy.graph import check_dseparation, check_valid_backdoor_set, check_valid_frontdoor_set, check_valid_mediation_set, do_surgery, get_all_directed_paths, get_backdoor_paths, get_descendants, get_instruments, has_directed_path
  from dowhy.utils.api import parse_state
label_desc: Use image node_id 18 for calling a global function with example usage: construct_mediation_estimand(estimand_type, action_nodes, outcome_nodes, mediator_nodes) and returns: estimand