def plot(
        probs,
        accepts,
        h_olds,
        h_news,
        exp_delta_hs,
        subtitle,
        labels,
        save,
        h_mdmc=None,
        mdmc_deltaH=None,  # for mdmc additions
):
    """Plots 3 stacked figures:
        1. the acceptance probability at each step
        2. the hamiltonian (old, new) at each step
        3. the exp{-delta H} at each step
    Overlayed with red bars is each instance in which a configuration was rejected
    by the Metropolis-Hastings accept/reject step
    
    Required Inputs
        probs           :: np.array :: acc. probs
        accepts         :: np.array :: array of boolean acceptances (True = accepted)
        h_olds          :: np.array :: old hamiltonian at each step
        h_news          :: np.array :: new hamiltonian at each step
        exp_delta_hs    :: np.array :: exp{-delta H} at each step
        h_mdmc          :: np.array :: 
        mdmc_deltaH     :: np.array ::
        subtitle        :: str      :: the subtitle to put in ax[0].set_title()
        save            :: bool :: True saves the plot, False prints to the screen
    """
    pp = Pretty_Plotter()
    pp._teXify()  # LaTeX
    # pp.params['text.latex.preamble'] = [r"\usepackage{amsmath}"]
    pp._updateRC()

    fig, ax = plt.subplots(3, sharex=True, figsize=(8, 8))
    # fig.suptitle(r'Data from {} Metropolis acceptance steps'.format(len(probs)),
    #     fontsize=pp.ttfont)

    fig.subplots_adjust(hspace=0.2)

    l = probs.size  # length of HMC trajectory - lots rely on this being at the top

    ### Add top pseudo-title and bottom shared x-axis label
    ax[0].set_title(subtitle, fontsize=pp.tfont)
    ax[-1].set_xlabel(r'Markov Time, $j$')

    ### add the rejection points in the background
    xrng = range(1, l + 1)  # calculate xrange to match length
    for a in ax:  # iterate over each axis
        for x, val in zip(xrng, accepts):  # iterate over all rejection points
            if val == False:
                a.axvline(x=x, linewidth=4, color='red', alpha=0.2)

    ### add the lines to the plots
    ax[0].set_ylabel(r'Hamiltonian, $H_j$')
    ax[0].plot(xrng,
               h_olds,
               linestyle='-',
               color='blue',
               linewidth=2.,
               alpha=1,
               label=r'$H_{j-1}$')
    # ax[0].plot(xrng, h_news, linestyle='-', color='green', linewidth=2., alpha=0.4,
    #     label=r'$H(t)$')

    ax[1].set_ylabel(r'$\exp{-\delta H_j}$')

    ax[1].plot(xrng,
               exp_delta_hs,
               linestyle='-',
               color='blue',
               linewidth=2.,
               alpha=1,
               label=r'$\exp{ -\delta H_t}$')

    ax[2].set_ylabel(r'Acceptance $\rho_j$')
    ax[2].plot(xrng, probs, linestyle='-', color='blue', linewidth=2., alpha=1)

    ### test to see if we will plot all the intermediate MDMC steps
    plot_mdmc = (h_mdmc is not None) & (mdmc_deltaH is not None)

    if plot_mdmc:  # all these functions rely on l being calculated!!
        # functions to calculate the staggering of y and x
        # for the MDMC. Staggering means calculating the start and
        # end points of each single trajectory consisting of 1 MDMC integration
        n = h_mdmc.size / l - 1  # this calculates n_steps for the MDMC (in a backwards way)
        staggered_xi = np.arange(0, l)
        staggered_xf = np.arange(1, l + 1)
        staggered_y = lambda arr, offset: arr[0 + offset::(n + 1)]
        remove_links = lambda arr: [
            None if (i) % (n + 1) == 0 else a for i, a in enumerate(arr)
        ]

        ## calculate a linear space as a fraction of the HMC trajectory
        mdmc_x = np.asarray([
            np.linspace(i, j, n + 1)
            for i, j in zip(range(0, l), range(1, l + 1))
        ])
        mdmc_x = mdmc_x.flatten()

        ax[0].plot(mdmc_x,
                   remove_links(h_mdmc),
                   linestyle='--',
                   color='green',
                   linewidth=1,
                   alpha=1,
                   label=r'MDMC: $H_{j+1}$')
        ax[0].scatter(staggered_xi,
                      staggered_y(h_mdmc, 0),
                      color='red',
                      marker='o',
                      alpha=1,
                      label=r'Start MDMC')
        ax[0].scatter(staggered_xf,
                      staggered_y(h_mdmc, n),
                      color='red',
                      marker='x',
                      alpha=1,
                      label=r'End MDMC')

        ax[1].plot(mdmc_x,
                   remove_links(mdmc_deltaH),
                   linestyle='--',
                   color='blue',
                   linewidth=1.,
                   alpha=1,
                   label=r'MDMC: $\exp{ -\delta H_{j+1}$')
        ax[1].scatter(staggered_xi,
                      staggered_y(mdmc_deltaH, 0),
                      color='red',
                      marker='o',
                      alpha=1,
                      label=r'Start MDMC')
        ax[1].scatter(staggered_xf,
                      staggered_y(mdmc_deltaH, n),
                      color='red',
                      marker='x',
                      alpha=1,
                      label=r'End MDMC')

    ### adds labels to the plots
    for i, text in labels.iteritems():
        pp.add_label(ax[i], text, fontsize=pp.tfont)

    ### place legends
    ax[0].legend(loc='upper left',
                 shadow=True,
                 fontsize=pp.ipfont,
                 fancybox=True)
    ax[1].legend(loc='upper left',
                 shadow=True,
                 fontsize=pp.ipfont,
                 fancybox=True)

    ### formatting
    for i in ax:
        i.grid(True)
    # ax[1].set_yscale("log", nonposy='clip') # set logarithmic y-scale

    pp.save_or_show(save, PLOT_LOC)
    pass
def plot(lines_d, x_lst, ws, subtitle, mcore, angle_labels, op_name, save):
    """Plots the two-point correlation function
    
    Required Inputs
        x_lst    :: list :: list of x_values for each angle that was run
        lines_d  :: {axis:[(y,label)]} :: plots (y,error,label) for each list item
        ws       :: list :: list of integration windows
        subtitle :: str  :: subtitle for the plot
        mcore    :: bool :: are there multicore operations? (>1 mixing angles)
        op_name  :: str  :: the name of the operator for the title
        angle_labels :: list :: the angle label text for legend plotting
        save     :: bool :: True saves the plot, False prints to the screen
    """

    pp = Pretty_Plotter()
    pp._teXify()  # LaTeX
    pp.params['text.latex.preamble'] = r"\usepackage{amsfonts}"
    pp.params['text.latex.preamble'] = r"\usepackage{amsmath}"
    pp._updateRC()

    fig = plt.figure(figsize=(8, 8))  # make plot
    ax = []

    fig, ax = plt.subplots(3, sharex=True, figsize=(8, 8))
    fig.suptitle(r"Autocorrelation and Errors for {}".format(op_name),
                 fontsize=pp.ttfont)

    fig.subplots_adjust(hspace=0.1)

    # Add top pseudo-title and bottom shared x-axis label
    ax[0].set_title(subtitle, fontsize=pp.tfont)
    ax[-1].set_xlabel(r'Window Length')

    if not mcore:  # don't want clutter in a multiple plot env.
        for a in range(1, len(ax)):  # Add the Window stop point as a red line
            # there is only one window item if not multiple lines
            ax[a].axvline(x=ws[0], linewidth=4, color='red', alpha=0.1)

    ax[0].set_ylabel(r'$g(w)$')
    ax[1].set_ylabel(r'$\tau_{\text{int}}(w)$')
    ax[2].set_ylabel(r'Autocorrelation, $\Gamma(t)$')

    #### plot for the 0th axis ####
    line_list = lines_d[0]
    axis = ax[0]
    theory_colours = iter(colour)
    measured_colours = iter(colour)
    for x, lines in zip(x_lst, line_list):
        m = next(marker)  # get next marker style
        c = next(measured_colours)  # get next colour
        th_c = next(theory_colours)
        # split into y function, errors in y and label
        y, e, l = lines  # in this case there are no errors or labels used

        # allow plots in diff colours for +/
        yp = y.copy()
        yp[yp < 0] = np.nan  # hide negative values
        ym = y.copy()
        ym[ym >= 0] = np.nan  # hide positive values

        if not mcore:
            axis.scatter(x,
                         yp,
                         marker='o',
                         color='g',
                         linewidth=2.,
                         alpha=0.6,
                         label=r'$g(t) \ge 0$')
            axis.scatter(x,
                         ym,
                         marker='x',
                         color='r',
                         linewidth=2.,
                         alpha=0.6,
                         label=r'$g(t) < 0$')
        else:
            axis.plot(x, yp, color=c, lw=1., alpha=0.6)  # label with the angle
            axis.plot(x, ym, color='r', lw=1., alpha=0.6)
            once_label = None  # set to blank so don't get multiple copies

    if not mcore: axis.legend(loc='best', shadow=True, fontsize=pp.axfont)

    #### plot the 1st axis ###
    # there is no angle label on the lines themselves on this axis
    # because the colours are synchronised across each plot
    # so the label on the bottom axis is enough
    line_list = lines_d[1]
    axis = ax[1]
    theory_colours = iter(colour)
    measured_colours = iter(colour)
    for x, lines in zip(x_lst, line_list):
        m = next(marker)  # get next marker style
        c = next(measured_colours)  # get next colour
        th_c = next(theory_colours)
        y, e, l, t = lines  # split into y function, errors, label and theory
        # try:
        #     axis.fill_between(x, y-e, y+e, color=c, alpha=0.5)
        # except:
        #     print "errors are dodgy"
        axis.errorbar(x,
                      y,
                      yerr=e,
                      markersize=3,
                      color=c,
                      fmt=m,
                      alpha=0.5,
                      ecolor='k')
        if t is not None:
            axis.axhline(y=t, linewidth=1, color=th_c, linestyle='--')

        # Only add informative label if there is only one line
        # adds a pretty text box above the middle plot with info
        # contained in the variable l - assigned in preparePlot()
        if not mcore: pp.add_label(axis, l, fontsize=pp.tfont)

    #### plot the 2nd axis ###
    # This plot explicitly list the labels for all the angles
    line_list = lines_d[2]
    axis = ax[2]
    theory_colours = iter(colour)
    measured_colours = iter(colour)
    for x, lines, a in zip(x_lst, line_list, angle_labels):
        m = next(marker)  # get next marker style
        c = next(measured_colours)  # get next colour
        th_c = next(theory_colours)
        y, e, l, t = lines  # split into y function, errors in y and label
        try:  # errors when there are low number of sims
            axis.fill_between(x, y - e, y + e, color=c, alpha=0.6, label=a)
            # axis.errorbar(x, y, yerr=e, label = a,
#                 markersize=3, color=c, fmt=m, alpha=0.5, ecolor='k')
        except:  # avoid crashing
            print 'Too few MCMC simulations to plot autocorrelations for: {}'.format(
                a)

        if t is not None:
            axis.plot(x,
                      t,
                      linewidth=1.2,
                      alpha=0.9,
                      color=th_c,
                      linestyle='--',
                      label='Theoretical')

    axis.legend(loc='best', shadow=True, fontsize=pp.axfont)

    #### start outdated section ####
    ## this won't work after the changes but shows the general idea of fitting a curve
    #
    # for i in range(1, len(lines)):              # add best fit lines
    #     x, y = lines[i][:2]
    #     popt, pcov = curve_fit(expFit, x, y)    # approx A+Bexp(-t/C)
    #     if not mcore:
    #         l_th = r'Fit: $f(t) = {:.1f} + {:.1f}'.format(popt[0], popt[1]) \
    #         + r'e^{-t/' +'{:.2f}'.format(popt[2]) + r'}$'
    #     else:
    #         l_th = None
    #     ax[i].plot(x, expFit(x, *popt), label = l_th,
    #         linestyle = '-', color=c, linewidth=2., alpha=.5)
    #### end outdated section ####

    # fix the limits so the plots have nice room
    xi, xf = ax[2].get_xlim()
    ax[2].set_xlim(xmin=xi - .05 * (xf - xi))  # decent view of the first point
    for a in ax:  # 5% extra room at top & add legend
        yi, yf = a.get_ylim()
        a.set_ylim(ymax=yf + .05 * (yf - yi), ymin=yi - .05 * (yf - yi))
    ax[-1].set_ylim(ymax=2)
    pp.save_or_show(save, PLOT_LOC)
    pass