code: def hypercube_scenario(filename, datafile=None, **kwds):
      """
      generate scenario support plots from file written with ``write_support_file``;
      and generate legacy data and cones from a dataset file, if provided
  
      Available from the command shell as::
  
          support_hypercube_scenario filename (datafile) [options]
  
      or as a function call::
  
          mystic.support.hypercube_scenario(filename, datafile=None, **options)
  
      Args:
          filename (str): name of the convergence logfile (e.g. ``paramlog.py``)
          datafile (str, default=None): name of the dataset file (e.g. ``data.txt``)
  
      Returns:
          None
  
      Notes:
          - The option *out* takes a string of the filepath for the generated plot.
            If ``out = True``, return the Figure object instead of generating a plot.
          - The options *bounds*, *dim*, and *iters* all take indicator strings.
            The bounds should be given as comma-separated slices. For example, using
            ``bounds = ".062:.125, 0:30, 2300:3200"`` will set lower and upper bounds
            for x to be (.062,.125), y to be (0,30), and z to be (2300,3200). If all
            bounds are to not be strictly enforced, append an asterisk ``*`` to the
            string. The dim (dimensions of the scenario) should comma-separated ints.      For example, ``dim = "1, 1, 2"`` will convert the params to a two-member
            3-D dataset. Iters accepts a string built from comma-separated array
            slices. Thus, ``iters = ":"`` will plot all iters in a single plot.
            Alternatively, ``iters = ":2, 2:"`` will split the iters into two plots,
            while ``iters = "0"`` will only plot the first iteration.
          - The option *label* takes comma-separated strings. Thus ``label = "x,y,"``
            will place 'x' on the x-axis, 'y' on the y-axis, and nothing on the
            z-axis. LaTeX, such as ``label = "$ h $, $ a$, $ v$"`` will label the
            axes with standard LaTeX math formatting. Note that the leading space is
            required, while a trailing space aligns the text with the axis instead of      the plot frame.
          - The option "filter" is used to select datapoints from a given dataset,
            and takes comma-separated ints.
          - A "mask" is given as comma-separated ints. When the mask has more than
            one int, the plot will be 2D.
          - The option "vertical" will plot the dataset values on the vertical axis;
            for 2D plots, cones are always plotted on the vertical axis.
          - The option *nid* takes an integer of the nth simultaneous points to plot.
          - The option *scale* takes an integer as a grayscale contrast multiplier.
          - The option *gap* takes an integer distance from cone center to vertex.
          - The option *data* takes a boolean, to plot legacy data, if provided.
          - The option *cones* takes a boolean, to plot cones, if provided.
          - The option *flat* takes a boolean, to plot results in a single plot.
      """
      import shlex
      from io import StringIO
  
      global __quit
      __quit = False
      _out = False
  
      instance = None
      # handle the special case where list is provided by sys.argv
      if isinstance(filename, (list, tuple)) and not kwds:
          cmdargs = filename  # (above is used by script to parse command line)
      elif isinstance(filename, str) and not kwds:
          cmdargs = shlex.split(filename)
      # 'everything else' is essentially the functional interface
      else:
          cmdargs = kwds.get("kwds", "")
          if not cmdargs:
              out = kwds.get("out", None)
              bounds = kwds.get("bounds", None)
              iters = kwds.get("iters", None)
              label = kwds.get("label", None)
              dim = kwds.get("dim", None)
              filter = kwds.get("filter", None)
              mask = kwds.get("mask", None)
              nid = kwds.get("nid", None)
              scale = kwds.get("scale", None)
              gap = kwds.get("gap", None)
              data = kwds.get("data", False)
              cones = kwds.get("cones", False)
              vertical = kwds.get("vertical", False)
              flat = kwds.get("flat", False)
  
              if isinstance(out, bool):
                  _out, out = out, None
  
              # process "commandline" arguments
              cmdargs = ""
              cmdargs += "" if out is None else "--out={} ".format(out)
              cmdargs += (
                  ""
                  if bounds is None
                  else '--bounds="{}" '.format(bounds)
              )
              cmdargs += (
                  "" if iters is None else '--iters="{}" '.format(iters)
              )
              cmdargs += (
                  "" if label is None else '--label="{}" '.format(label)
              )
              cmdargs += (
                  "" if dim is None else '--dim="{}" '.format(dim)
              )
              cmdargs += (
                  ""
                  if filter is None
                  else '--filter="{}" '.format(filter)
              )
              cmdargs += (
                  "" if mask is None else "--mask={} ".format(mask)
              )
              cmdargs += "" if nid is None else "--nid={} ".format(nid)
              cmdargs += (
                  "" if scale is None else "--scale={} ".format(scale)
              )
              cmdargs += "" if gap is None else "--gap={} ".format(gap)
              cmdargs += "" if data == False else "--data "
              cmdargs += "" if cones == False else "--cones "
              cmdargs += "" if vertical == False else "--vertical "
              cmdargs += "" if flat == False else "--flat "
          else:
              cmdargs = " " + cmdargs
          if isinstance(filename, str):
              cmdargs = filename.split() + shlex.split(cmdargs)
          else:  # special case of passing in monitor instance
              instance = filename
              cmdargs = shlex.split(cmdargs)
  
      # XXX: replace with 'argparse'?
      from optparse import OptionParser
  
      def _exit(self, errno=None, msg=None):
          global __quit
          __quit = True
          if errno or msg:
              msg = msg.split(": error: ")[-1].strip()
              raise IOError(msg)
  
      OptionParser.exit = _exit
  
      parser = OptionParser(
          usage=hypercube_scenario.__doc__.split(NL2 + "Options:")[0]
      )
      parser.add_option(
          "-u",
          "--out",
          action="store",
          dest="out",
          metavar="STR",
          default=None,
          help="filepath to save generated plot",
      )
      parser.add_option(
          "-b",
          "--bounds",
          action="store",
          dest="bounds",
          metavar="STR",
          default="0:1, 0:1, 0:1",
          help="indicator string to set hypercube bounds",
      )
      parser.add_option(
          "-i",
          "--iters",
          action="store",
          dest="iters",
          metavar="STR",
          default="-1",
          help="indicator string to select iterations to plot",
      )
      parser.add_option(
          "-l",
          "--label",
          action="store",
          dest="label",
          metavar="STR",
          default=",,",
          help="string to assign label to axis",
      )
      parser.add_option(
          "-d",
          "--dim",
          action="store",
          dest="dim",
          metavar="STR",
          default="None",
          help="indicator string set to scenario dimensions",
      )
      parser.add_option(
          "-p",
          "--filter",
          action="store",
          dest="filter",
          metavar="STR",
          default="None",
          help="filter to select datapoints to plot",
      )
      parser.add_option(
          "-m",
          "--mask",
          action="store",
          dest="replace",
          metavar="INT",
          default=None,
          help="id # of the coordinate axis not to be plotted",
      )
      parser.add_option(
          "-n",
          "--nid",
          action="store",
          dest="id",
          metavar="INT",
          default=None,
          help="id # of the nth simultaneous points to plot",
      )
      parser.add_option(
          "-s",
          "--scale",
          action="store",
          dest="scale",
          metavar="INT",
          default=1.0,
          help="grayscale contrast multiplier for points in plot",
      )
      parser.add_option(
          "-g",
          "--gap",
          action="store",
          dest="gap",
          metavar="INT",
          default=0.0,
          help="distance from double cone center to cone vertex",
      )
      parser.add_option(
          "-o",
          "--data",
          action="store_true",
          dest="legacy",
          default=False,
          help="plot legacy data, if provided",
      )
      parser.add_option(
          "-v",
          "--cones",
          action="store_true",
          dest="cones",
          default=False,
          help="plot cones, if provided",
      )
      parser.add_option(
          "-z",
          "--vertical",
          action="store_true",
          dest="vertical",
          default=False,
          help="always plot values on vertical axis",
      )
      parser.add_option(
          "-f",
          "--flat",
          action="store_true",
          dest="flatten",
          default=False,
          help="show selected iterations in a single plot",
      )
  
      f = StringIO()
      parser.print_help(file=f)
      f.seek(0)
      if "Options:" not in hypercube_scenario.__doc__:
          hypercube_scenario.__doc__ += NL + "Options:{0}".format(
              f.read().split("Options:")[-1]
          )
      f.close()
  
      try:
          parsed_opts, parsed_args = parser.parse_args(cmdargs)
      except UnboundLocalError:
          pass
      if __quit:
          return
  
      # get the name of the parameter log file
      if instance is None:
          instance = parsed_args[0]
      from mystic.munge import read_history
  
      params, _cost = read_history(instance, iter=False)
      # would be nice to use meta = ['wx','wx2','x','x2','wy',...]
      # exec "from {file} import meta".format(file=file)
  
      from mystic.math.discrete import scenario
      from mystic.math.legacydata import dataset
  
      try:  # select whether to plot the cones
          cones = parsed_opts.cones
      except:
          cones = False
  
      try:  # select whether to plot the legacy data
          legacy = parsed_opts.legacy
      except:
          legacy = False
  
      try:  # path of plot output file
          out = parsed_opts.out  # e.g. 'myplot.png'
          if "None" == out:
              out = None
      except:
          out = None
  
      try:  # get dataset filter
          filter = parsed_opts.filter
          if "None" == filter:
              filter = None
          else:
              filter = [
                  int(i) for i in filter.split(",")
              ]  # format is "1,5,9"
      except:
          filter = None
  
      try:  # select the scenario dimensions
          npts = parsed_opts.dim
          if "None" == npts:  # npts may have been logged
              from mystic.munge import read_import
  
              npts = read_import(parsed_args[0], "npts")
          else:
              npts = tuple(
                  int(i) for i in dim.split(",")
              )  # format is "1,1,1"
      except:
          npts = (1, 1, 1)  # XXX: better in parsed_args ?
  
      try:  # get the name of the dataset file
          file = parsed_args[1]
          from mystic.math.legacydata import load_dataset
  
          data = load_dataset(file, filter)
      except:
          #       raise IOError("please provide dataset file name")
          data = dataset()
          cones = False
          legacy = False
  
      try:  # select the bounds
          _bounds = parsed_opts.bounds
          if (
              _bounds[-1] == "*"
          ):  # XXX: then bounds are NOT strictly enforced
              _bounds = _bounds[:-1]
              strict = False
          else:
              strict = True
          bounds = _bounds.split(
              ","
          )  # format is "60:105, 0:30, 2.1:2.8"
          bounds = [
              tuple(float(j) for j in i.split(":")) for i in bounds
          ]
      except:
          strict = True
          bounds = [(0, 1), (0, 1), (0, 1)]
  
      try:  # select labels for the axes
          label = parsed_opts.label.split(",")  # format is "x, y, z"
      except:
          label = ["", "", ""]
  
      x = params[-1]
      try:  # select which iterations to plot
          select = parsed_opts.iters.split(
              ","
          )  # format is ":2, 2:4, 5, 6:"
      except:
          select = ["-1"]
      # select = [':']
      # select = [':1']
      # select = [':2','2:']
      # select = [':1','1:2','2:3','3:']
      # select = ['0','1','2','3']
  
      try:  # collapse non-consecutive iterations into a single plot...
          flatten = parsed_opts.flatten
      except:
          flatten = False
  
      try:  # select which 'id' to plot results for
          id = int(parsed_opts.id)
      except:
          id = None  # i.e. 'all' **or** use id=0, which should be 'best' energy ?
  
      try:  # scale the color in plotting the weights
          scale = float(parsed_opts.scale)
      except:
          scale = 1.0  # color = color**scale
  
      try:  # gap between double cone center and cone vertex
          gap = float(parsed_opts.gap)
      except:
          gap = 0.0
  
      _2D = False  # if False, use 3D plots; if True, use 3D plots
      cs = None
      try:  # select which axis to plot 'values' on  (3D plot)
          xs = int(parsed_opts.replace)
      except:
          try:  # select which axes to mask (2D plot)
              xs = (
                  int(i) for i in parsed_opts.replace.split(",")
              )  # format is "1,2"
              xs = list(reversed(sorted(set(xs))))
              cs = int(xs[-1]) if xs[-1] != xs[0] else None
              xs = int(xs[0])
              xs, cs = (
                  cs,
                  xs,
              )  # cs will swap coord axes; xs will swap with value axis
              _2D = True  # NOTE: always apply cs swap before xs swap
          except:
              xs = None  # don't plot values; plot values on 'x' axis with xs = 0
  
      try:  # always plot cones on vertical axis
          vertical_cones = parsed_opts.vertical
      except:
          vertical_cones = False
      if _2D:  # always plot cones on vertical axis
          vertical_cones = True
  
      # ensure all terms of bounds are tuples
      for bound in bounds:
          if not isinstance(bound, tuple):
              raise TypeError(
                  "bounds should be tuples of (lower_bound,upper_bound)"
              )
  
      # ensure all terms of select are strings that have a ":"
      for i in range(len(select)):
          if isinstance(select[i], int):
              select[i] = str(select[i])
          if select[i] == "-1":
              select[i] = "len(x)-1:len(x)"
          elif not select[i].count(":"):
              select[i] += ":" + str(int(select[i]) + 1)
  
      # take only the selected 'id'
      if id != None:
          param = []
          for j in range(len(params)):
              param.append([p[id] for p in params[j]])
          params = param[:]
  
      # at this point, we should have:
      # bounds = [(60,105),(0,30),(2.1,2.8)] or [(None,None)]*3
      # select = ['-1:'] or [':'] or [':1','1:2','2:3','3:'] or similar
      # id = 0 or None
  
      # get dataset coords (and values) for selected axes
      if data:
          slope = _get_slope(data, xs, cs)
          coords = _get_coords(data, xs, cs)
          # print("bounds: {0}".format(bounds))
          # print("slope: {0}".format(slope))
          # print("coords: {0}".format(coords))
      # else:
      #    slope = []
      #    coords = []
  
      plots = len(select)
      if not flatten:
          dim1, dim2 = best_dimensions(plots)
      else:
          dim1, dim2 = 1, 1
  
      # use the default bounds where not specified
      bounds = [list(i) for i in bounds]
      for i in range(len(bounds)):
          if bounds[i][0] is None:
              bounds[i][0] = 0
          if bounds[i][1] is None:
              bounds[i][1] = 1
  
      # swap the axes appropriately when plotting cones (when replacing an axis)
      if _2D and xs == 0:
          if data:
              slope[0], slope[1] = slope[1], slope[0]
              coords = [
                  list(reversed(pt[:2])) + pt[2:] for pt in coords
              ]
          # bounds[0],bounds[1] = bounds[1],bounds[0]
          # label[0],label[1] = label[1],label[0]
          axis = xs  # None
      elif not _2D and vertical_cones and xs in range(len(bounds)):
          # adjust slope, bounds, and data so cone axis is last
          if data:
              slope = swap(slope, xs)
              coords = [swap(pt, xs) for pt in coords]
          bounds = swap(bounds, xs)
          label = swap(label, xs)
          axis = None
      else:
          axis = xs
  
      # correctly bound the first plot.  there must be at least one plot
      import matplotlib.pyplot as plt
  
      fig = plt.figure()
      try:
          Subplot3D = None
          if _2D:
              ax1 = fig.add_subplot(dim1, dim2, 1)
              newsubplotcode = (
                  "ax{0:d} = ax = fig.add_subplot(dim1,dim2,{0:d});"
              )
          else:
              ax1 = fig.add_subplot(dim1, dim2, 1, projection="3d")
              newsubplotcode = "ax{0:d} = ax = fig.add_subplot(dim1,dim2,{0:d}, projection='3d');"
      except:
          from mpl_toolkits.mplot3d import Axes3D as _Axes3D
          from matplotlib.axes import subplot_class_factory
  
          Subplot3D = subplot_class_factory(_Axes3D)
          ax1 = Subplot3D(fig, dim1, dim2, 1)
          newsubplotcode = (
              "ax{0:d} = ax = Subplot3D(fig, dim1,dim2,{0:d});"
          )
      if _2D:
          ax1.plot([bounds[0][0]], [bounds[1][0]])
          ax1.plot([bounds[0][1]], [bounds[1][1]])
      else:
          ax1.plot([bounds[0][0]], [bounds[1][0]], [bounds[2][0]])
          ax1.plot([bounds[0][1]], [bounds[1][1]], [bounds[2][1]])
      globals = {
          "plt": plt,
          "fig": fig,
          "dim1": dim1,
          "dim2": dim2,
          "Subplot3D": Subplot3D,
          "bounds": bounds,
          "label": label,
      }
      if not flatten:
          code = "plt.title('iterations[{0}]');".format(select[0])
      else:
          code = "plt.title('iterations[*]');"
      code = compile(code, "<string>", "exec")
      exec(code, globals)
      if cones and data and xs in range(len(bounds)):
          if _2D:
              _plot_bowtie(
                  ax1, coords, slope, bounds, axis=axis, tol=gap
              )
          else:
              _plot_cones(
                  ax1,
                  coords,
                  slope,
                  bounds,
                  axis=axis,
                  strict=strict,
                  tol=gap,
              )
      if legacy and data:
          _plot_data(ax1, coords, bounds, strict=strict, _2D=_2D)
      # _clip_axes(ax1,bounds)
      _label_axes(ax1, label, _2D=_2D)
      a = [ax1]
  
      # set up additional plots
      if not flatten:
          for i in range(2, plots + 1):
              code = newsubplotcode.format(i)
              if _2D:
                  code += "ax.plot([bounds[0][0]],[bounds[1][0]]);"
                  code += "ax.plot([bounds[0][1]],[bounds[1][1]]);"
              else:
                  code += "ax.plot([bounds[0][0]],[bounds[1][0]],[bounds[2][0]]);"
                  code += "ax.plot([bounds[0][1]],[bounds[1][1]],[bounds[2][1]]);"
              code += "plt.title('iterations[{0}]');".format(
                  select[i - 1]
              )
              code = compile(code, "<string>", "exec")
              exec(code, globals)
              ax = globals["ax"]
              if cones and data and xs in range(len(bounds)):
                  if _2D:
                      _plot_bowtie(
                          ax, coords, slope, bounds, axis=axis, tol=gap
                      )
                  else:
                      _plot_cones(
                          ax,
                          coords,
                          slope,
                          bounds,
                          axis=axis,
                          strict=strict,
                          tol=gap,
                      )
              if legacy and data:
                  _plot_data(ax, coords, bounds, strict=strict, _2D=_2D)
              # _clip_axes(ax,bounds)
              _label_axes(ax, label, _2D=_2D)
              a.append(ax)
  
      # turn each "n:m" in select to a list
      _select = []
      for sel in select:
          if sel[0] == ":":
              _select.append("0" + sel)
          else:
              _select.append(sel)
      for i in range(len(_select)):
          if _select[i][-1] == ":":
              select[i] = _select[i] + str(len(x))
          else:
              select[i] = _select[i]
      for i in range(len(select)):
          p = select[i].split(":")
          if p[0][0] == "-":
              p[0] = "len(x)" + p[0]
          if p[1][0] == "-":
              p[1] = "len(x)" + p[1]
          select[i] = p[0] + ":" + p[1]
      locals = dict(x=x, params=params)
      steps = [
          eval("range({0})".format(sel.replace(":", ",")), locals)
          for sel in select
      ]
  
      # at this point, we should have:
      # steps = [[0,1],[1,2],[2,3],[3,4,5,6,7,8]] or similar
      if flatten:
          from mystic.tools import flatten
  
          steps = [list(flatten(steps))]
  
      # plot all the scenario "data"
      from numpy import inf, e
  
      scale = e ** (scale - 1.0)
      for v in range(len(steps)):
          if len(steps[v]) > 1:
              qp = float(max(steps[v]))
          else:
              qp = inf
          for s in steps[v]:
              par = eval(
                  "[params[q][{0}][0] for q in range(len(params))]".format(
                      s
                  ),
                  locals,
              )
              pm = scenario()
              pm.load(par, npts)
              d = dataset()
              d.load(pm.coords, pm.values)
              # dot color determined by number of simultaneous iterations
              t = str((s / qp) ** scale)
              # get and plot dataset coords for selected axes
              _coords = _get_coords(d, xs, cs)
              # check if we are replacing an axis
              if _2D and xs == 0:
                  if data:  # adjust data so cone axis is last
                      _coords = [
                          list(reversed(pt[:2])) + pt[2:]
                          for pt in _coords
                      ]
              elif (
                  not _2D
                  and vertical_cones
                  and xs in range(len(bounds))
              ):
                  if data:  # adjust data so cone axis is last
                      _coords = [swap(pt, xs) for pt in _coords]
              _plot_data(
                  a[v], _coords, bounds, color=t, strict=strict, _2D=_2D
              )
  
      if _2D:  # strict = True
          plt.xlim((bounds[0][0], bounds[0][1]))
          plt.ylim((bounds[1][0], bounds[1][1]))
  
      # process inputs
      if _out:
          out = _out
      if out:
          out = _out or out
  
      if not out:
          plt.show()
      elif out is True:
          return (
              fig  # XXX: better?: fig.axes if len(fig.axes) > 1 else ax
          )
      else:
          fig.savefig(out)
  
file_path: mystic/mystic/support.py
label_desc: Use image node_id 14 for calling a global function with example usage: hypercube_scenario(filename, datafile) and returns: fig