code: def construct_frontdoor_estimand(
      treatment_name: List[str],
      outcome_name: List[str],
      frontdoor_variables_names: List[str],
  ):
      # TODO: support multivariate treatments better.
      expr = None
      outcome_name = outcome_name[0]
      sym_outcome = spstats.Normal(outcome_name, 0, 1)
      sym_treatment_symbols = [
          spstats.Normal(t, 0, 1) for t in treatment_name
      ]
      sym_treatment = sp.Array(sym_treatment_symbols)
      sym_frontdoor_symbols = [
          sp.Symbol(inst) for inst in frontdoor_variables_names
      ]
      sym_frontdoor = sp.Array(
          sym_frontdoor_symbols
      )  # ",".join(instrument_names))
      sym_outcome_derivative = sp.Derivative(sym_outcome, sym_frontdoor)
      sym_treatment_derivative = sp.Derivative(
          sym_frontdoor, sym_treatment
      )
      sym_effect = spstats.Expectation(
          sym_treatment_derivative * sym_outcome_derivative
      )
      sym_assumptions = {
          "Full-mediation": (
              "{2} intercepts (blocks) all directed paths from {0} to {1}."
          ).format(
              ",".join(treatment_name),
              ",".join(outcome_name),
              ",".join(frontdoor_variables_names),
          ),
          "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(treatment_name),
              ",".join(frontdoor_variables_names),
          ),
          "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(treatment_name),
              outcome_name,
              ",".join(frontdoor_variables_names),
          ),
      }
  
      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 17 for calling a global function with example usage: construct_frontdoor_estimand(treatment_name, outcome_name, frontdoor_variables_names) and returns: estimand