code: def collapse_plotter(filename, **kwds):
      r"""
      generate convergence plots of | cost - cost_min | from convergence logfile
  
      Available from the command shell as::
  
          mystic_collapse_plotter filename [options]
  
      or as a function call::
  
          mystic.collapse_plotter(filename, **options)
  
      Args:
          filename (str): name of the convergence logfile (e.g ``paramlog.py``).
  
      Returns:
          None
  
      Notes:
          - The option *dots* takes a boolean, and will show data points in the plot.
          - The option *linear* takes a boolean, and will plot in a linear scale.
          - 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 option *iter* takes an integer of the largest iteration to plot.
          - The option *legend* takes a boolean, and will display the legend.
          - The option *nid* takes an integer of the nth simultaneous points to plot.
          - The option *label* takes a label string. For example, ``label = "y"``
            will label the plot with a 'y', while ``label = " log-cost,
            $ log_{10}(\hat{P} - \hat{P}_{max})$"`` will label the y-axis with
            standard LaTeX math formatting. Note that the leading space is required,
            and that the text is aligned along the axis.
          - The option *col* takes a string of comma-separated integers indicating
            iteration numbers where parameter collapse has occurred. If a second set
            of integers is provided (delineated by a semicolon), the additional set
            of integers will be plotted with a different linestyle (to indicate a
            different type of collapse)."""
      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)
              dots = kwds.get("dots", False)
              # line = kwds.get('line', False)
              linear = kwds.get("linear", False)
              iter = kwds.get("iter", None)
              legend = kwds.get("legend", False)
              nid = kwds.get("nid", None)
              label = kwds.get("label", None)
              col = kwds.get("col", None)
  
              if isinstance(out, bool):
                  _out, out = out, None
  
              # process "commandline" arguments
              cmdargs = ""
              cmdargs += "" if out is None else "--out={} ".format(out)
              cmdargs += "" if dots == False else "--dots "
              cmdargs += "" if linear == False else "--linear "
              # cmdargs += '' if line == False else '--line '
              cmdargs += (
                  "" if iter is None else "--iter={} ".format(iter)
              )
              cmdargs += "" if legend == False else "--legend "
              cmdargs += "" if nid is None else "--nid={} ".format(nid)
              cmdargs += (
                  "" if label == None else "--label={} ".format(label)
              )
              cmdargs += (
                  "" if col is None else '--col="{}" '.format(col)
              )
          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=collapse_plotter.__doc__.split("\n\nOptions:")[0]
      )
      parser.add_option(
          "-d",
          "--dots",
          action="store_true",
          dest="dots",
          default=False,
          help="show data points in plot",
      )
      # parser.add_option("-l","--line",action="store_true",dest="line",\
      #                  default=False,help="connect data points with a line")
      parser.add_option(
          "-y",
          "--linear",
          action="store_true",
          dest="linear",
          default=False,
          help="plot y-axis in linear scale",
      )
      parser.add_option(
          "-u",
          "--out",
          action="store",
          dest="out",
          metavar="STR",
          default=None,
          help="filepath to save generated plot",
      )
      parser.add_option(
          "-i",
          "--iter",
          action="store",
          dest="stop",
          metavar="STR",
          default=":",
          help="string for smallest:largest iterations to plot",
      )
      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(
          "-g",
          "--legend",
          action="store_true",
          dest="legend",
          default=False,
          help="show the legend",
      )
      parser.add_option(
          "-l",
          "--label",
          action="store",
          dest="label",
          metavar="STR",
          default="",
          help="string to assign label to y-axis",
      )
      parser.add_option(
          "-c",
          "--col",
          action="store",
          dest="collapse",
          metavar="STR",
          default="",
          help="string to indicate collapse indices",
      )
      #   import sys
      #   if 'mystic_collapse_plotter.py' not in sys.argv:
      f = StringIO()
      parser.print_help(file=f)
      f.seek(0)
      if "Options:" not in collapse_plotter.__doc__:
          collapse_plotter.__doc__ += (
              "\nOptions:%s" % f.read().split("Options:")[-1]
          )
      f.close()
  
      try:
          parsed_opts, parsed_args = parser.parse_args(cmdargs)
      except UnboundLocalError:
          pass
      if __quit:
          return
  
      style = "-"  # default linestyle
      if parsed_opts.dots:
          mark = "o"
          # when using 'dots', also turn off 'line'
          # if not parsed_opts.line:
          #  style = 'None'
      else:
          mark = ""
  
      try:  # select labels for the axes
          label = parsed_opts.label  # format is "x" or " $x$"
      except:
          label = "log-cost, $log_{10}(y - y_{min})$"
  
      try:  # get logfile name
          filename = parsed_args[0]
      except:
          raise IOError("please provide log file name")
  
      try:  # path of plot output file
          out = parsed_opts.out  # e.g. 'myplot.png'
          if "None" == out:
              out = None
      except:
          out = None
  
      try:  # select which iteration to stop plotting at
          stop = parsed_opts.stop  # format is "1:10:1"
          stop = (
              stop
              if ":" in stop
              else (stop + ":" if "-" in stop else ":" + stop)
          )
      except:
          stop = ":"
  
      try:  # select which 'id' to plot results for
          runs = (
              int(parsed_opts.id),
          )  # XXX: allow selecting more than one id ?
      except:
          runs = None  # i.e. 'all' **or** use id=0, which should be 'best' energy ?
  
      try:  # select collapse boundaries to plot
          collapse = parsed_opts.collapse.split(
              ";"
          )  # format is "2, 3; 4, 5, 6; 7"
          collapse = [
              eval("(%s,)" % i) if i.strip() else () for i in collapse
          ]
      except:
          collapse = []
  
      # read file
      from mystic.munge import read_history
  
      step, params, cost = read_history(filename, iter=True)
  
      # ignore everything after 'stop'
      locals = dict(step=step, cost=cost, params=params)
      step = eval("step[%s]" % stop, locals)
      cost = eval("cost[%s]" % stop, locals)
      params = eval("params[%s]" % stop, locals)
      del locals
  
      # split (i,id) into iteration and id
      if step:  # XXX: ignore non-intger ids
          multinode = (
              len(step[0]) - 1 if isinstance(step[0][-1], int) else 0
          )
      else:
          multinode = 0  # XXX: no step info, so give up
      iter = [i[0] for i in step]
      if multinode:
          id = [i[1] for i in step]
      else:
          id = [0 for i in step]
  
      if runs is not None:
          maxid = max(id) + 1
          runs = [maxid + i if i < 0 else i for i in runs]
          if not set(id).intersection(runs):
              raise IOError("please provide a valid id")
  
      results = [[] for i in range(max(id) + 1)]
  
      # populate results for each id with the corresponding (iter,cost)
      for i in range(len(id)):
          if (
              runs is None or id[i] in runs
          ):  # take only the selected 'id'
              results[id[i]].append((iter[i], cost[i]))
  
      # build list of cost convergences for each id
      cost_conv = []
      iter_conv = []
      for i in range(len(results)):
          if len(results[i]):
              cost_conv.append(
                  [results[i][j][1] for j in range(len(results[i]))]
              )
              iter_conv.append(
                  [results[i][j][0] for j in range(len(results[i]))]
              )
          else:
              cost_conv.append([])
              iter_conv.append([])
  
      # print("iter_conv = %s" % iter_conv)
      # print("cost_conv = %s" % cost_conv)
  
      # get the minimum cost
      import numpy as np
  
      cost_min = min(cost)  # XXX: scale min per id? or vs global min???
  
      colors = ["orange", "red", "brown", "pink"]
      linestyles = ["--", "-.", ":", "-"]
  
      import matplotlib.pyplot as plt
  
      fig = plt.figure()
  
      # NOTE: uses min(cost) of each nid
      for i in range(len(cost_conv)):
          if runs is None or i in runs:
              tag = "%d" % i
  
              # convert to log scale
              # x = np.arange(len(cost))
              x = iter_conv[i]
              settings = np.seterr(all="ignore")
              y = np.array(cost_conv[i])
              ymin = (
                  min(y) if y.size else cost_min
              )  # avoids error when empty
              if not parsed_opts.linear:
                  y = np.log10(np.abs(ymin - y))
              else:
                  y = np.abs(ymin - y)
              np.seterr(**settings)
  
              plt.plot(
                  x,
                  y,
                  label="%s" % tag,
                  linestyle=style,
                  marker=mark,
                  markersize=1,
              )
  
      for param, color, style in zip(collapse, colors, linestyles):
          for clps in set(param):
              plt.axvline(
                  x=clps,
                  ymin=-10,
                  ymax=1,
                  linestyle=style,
                  linewidth=param.count(clps),
                  color=color,
              )
  
      if label:
          # plt.title('convergence rate')
          plt.xlabel("iteration number, $n$")
          plt.ylabel(label)
          # plt.ylabel('$log-error,\; log_{10}(\hat{P} - \hat{P}_{max})$')
  
      if parsed_opts.legend:
          plt.legend()
  
      # 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)
  
imports: from collections.abc import Callable
label_desc: Use image node_id 27 for calling a global function with example usage: collapse_plotter(filename) and returns: fig