# Path: prospect/fitting/convergence.py
# def convergence_check(chain, convergence_check_interval=None, convergence_chunks=325,
#                       convergence_stable_points_criteria=3, convergence_nhist=50,
#                       convergence_kl_threshold=0.018, **kwargs):
#     """Performs a Kullback-Leibler divergence test for convergence.
# 
#     :param chain:
#         The chain to perform the test on.
# 
#     :param convergence_check_interval:
#         How often to assess convergence, in number of iterations.
# 
#     :param convergence_chunks:
#         The number of iterations to combine when creating the marginalized
#         parameter probability functions.
# 
#     :param convergence_stable_points_criteria:
#         The number of stable convergence checks that the chain must pass before
#         being declared stable.
# 
#     :param convergence_nhist:
#         Controls how finely the PDF is subsampled before comparison. This has a
#         strong effect on the normalization of the KL divergence. Larger -->
#         more noise but finer distinction between PDF shapes.
# 
#     :param convergence_kl_threshold:
#         The convergence criteria for the KL test. Suggest running multiple long
#         chains and plotting the KL divergence in each parameter to determine
#         how to set this.
# 
#     :returns convergence_flag:
#         True if converged. False if not.
# 
#     :returns outdict:
#         Contains the results of the KL test for each parameter (number of
#         checks, number of parameters) and the iteration where this was
#         calculated.
#     """
# 
#     nwalkers, niter, npars = chain.shape
# 
#     # Define some useful quantities
#     niter_check_start = 2*convergence_chunks # must run for at least 2 intervals before checking!
#     ncheck = np.floor((niter-niter_check_start)/float(convergence_check_interval)).astype(int)+1
# 
#     # Calculate the K-L divergence in each chunk for each parameter
#     kl = np.zeros(shape=(ncheck, npars))
#     xiter = np.arange(ncheck) * convergence_check_interval + niter_check_start
#     for n in range(ncheck):
#         for i in range(npars):
# 
#             # Define chains and calculate pdf
#             lo = (xiter[n] - 2*convergence_chunks)
#             hi = (xiter[n] - convergence_chunks)
#             early_test_chain = chain[:, lo:hi, i].flatten()
#             pdf_early, bins = make_kl_bins(early_test_chain, nbins=convergence_nhist)
# 
#             # clip test chain so that it's all contained in bins
#             # basically redefining first and last bin to have open edges
#             late_test_chain = np.clip(chain[:, hi:xiter[n], i].flatten(),
#                                       bins[0], bins[-1])
#             pdf_late, _ = np.histogram(late_test_chain, bins=bins)
#             kl[n, i] = kl_divergence(pdf_late, pdf_early)
# 
#     # Check for convergence
#     converged_idx = np.all(kl < convergence_kl_threshold, axis=1)
#     convergence_flag = find_subsequence([True]*convergence_stable_points_criteria,
#                                         converged_idx.tolist())
# 
#     outdict = {'iteration': xiter, 'kl_test': kl}
#     return convergence_flag, outdict


import sys
import numpy as np
    import emcee
from numpy.random import normal, multivariate_normal
from .convergence import convergence_check
        ndim = esampler.dim
        nwalkers = esampler.k
        mc_args = {"storechain": storechain,
                   "iterations": niter}

    # Set up hdf5 backend
    sdat = hdf5.create_group('sampling')
    # dynamic dataset
    conv_int = convergence_check_interval
    conv_crit = convergence_stable_points_criteria
    nfirstcheck = (2 * convergence_chunks + conv_int * (conv_crit - 1))
    chain   = sdat.create_dataset('chain', (nwalkers, nfirstcheck, ndim),
                                  maxshape=(nwalkers, None, ndim))
    lnpout  = sdat.create_dataset('lnprobability', (nwalkers, nfirstcheck),
                                  maxshape=(nwalkers, None))
    kl      = sdat.create_dataset('kl_divergence', (conv_crit, ndim),
                                  maxshape=(None, ndim))
    kl_iter = sdat.create_dataset('kl_iteration', (conv_crit,),
                                  maxshape=(None,))

    # Main loop over iterations of the MCMC sampler
    for i, result in enumerate(esampler.sample(initial, **mc_args)):
        chain[:, i, :] = result[0]
        lnpout[:, i] = result[1]
        do_convergence_check = ((convergence_check_interval is not None) and
                                (i+1 >= nfirstcheck) and
                                ((i+1 - nfirstcheck) % convergence_check_interval == 0))
        if do_convergence_check:
            if verbose:
                print('checking convergence after iteration {0}').format(i+1)

# Path: prospect/fitting/convergence.py
# def convergence_check(chain, convergence_check_interval=None, convergence_chunks=325,
#                       convergence_stable_points_criteria=3, convergence_nhist=50,
#                       convergence_kl_threshold=0.018, **kwargs):
#     """Performs a Kullback-Leibler divergence test for convergence.
# 
#     :param chain:
#         The chain to perform the test on.
# 
#     :param convergence_check_interval:
#         How often to assess convergence, in number of iterations.
# 
#     :param convergence_chunks:
#         The number of iterations to combine when creating the marginalized
#         parameter probability functions.
# 
#     :param convergence_stable_points_criteria:
#         The number of stable convergence checks that the chain must pass before
#         being declared stable.
# 
#     :param convergence_nhist:
#         Controls how finely the PDF is subsampled before comparison. This has a
#         strong effect on the normalization of the KL divergence. Larger -->
#         more noise but finer distinction between PDF shapes.
# 
#     :param convergence_kl_threshold:
#         The convergence criteria for the KL test. Suggest running multiple long
#         chains and plotting the KL divergence in each parameter to determine
#         how to set this.
# 
#     :returns convergence_flag:
#         True if converged. False if not.
# 
#     :returns outdict:
#         Contains the results of the KL test for each parameter (number of
#         checks, number of parameters) and the iteration where this was
#         calculated.
#     """
# 
#     nwalkers, niter, npars = chain.shape
# 
#     # Define some useful quantities
#     niter_check_start = 2*convergence_chunks # must run for at least 2 intervals before checking!
#     ncheck = np.floor((niter-niter_check_start)/float(convergence_check_interval)).astype(int)+1
# 
#     # Calculate the K-L divergence in each chunk for each parameter
#     kl = np.zeros(shape=(ncheck, npars))
#     xiter = np.arange(ncheck) * convergence_check_interval + niter_check_start
#     for n in range(ncheck):
#         for i in range(npars):
# 
#             # Define chains and calculate pdf
#             lo = (xiter[n] - 2*convergence_chunks)
#             hi = (xiter[n] - convergence_chunks)
#             early_test_chain = chain[:, lo:hi, i].flatten()
#             pdf_early, bins = make_kl_bins(early_test_chain, nbins=convergence_nhist)
# 
#             # clip test chain so that it's all contained in bins
#             # basically redefining first and last bin to have open edges
#             late_test_chain = np.clip(chain[:, hi:xiter[n], i].flatten(),
#                                       bins[0], bins[-1])
#             pdf_late, _ = np.histogram(late_test_chain, bins=bins)
#             kl[n, i] = kl_divergence(pdf_late, pdf_early)
# 
#     # Check for convergence
#     converged_idx = np.all(kl < convergence_kl_threshold, axis=1)
#     convergence_flag = find_subsequence([True]*convergence_stable_points_criteria,
#                                         converged_idx.tolist())
# 
#     outdict = {'iteration': xiter, 'kl_test': kl}
#     return convergence_flag, outdict


# Path: prospect/fitting/ensemble.py
import sys
import numpy as np
    import emcee
from numpy.random import normal, multivariate_normal
from .convergence import convergence_check

        ndim = esampler.dim
        nwalkers = esampler.k
        mc_args = {"storechain": storechain,
                   "iterations": niter}

    # Set up hdf5 backend
    sdat = hdf5.create_group('sampling')
    # dynamic dataset
    conv_int = convergence_check_interval
    conv_crit = convergence_stable_points_criteria
    nfirstcheck = (2 * convergence_chunks + conv_int * (conv_crit - 1))
    chain   = sdat.create_dataset('chain', (nwalkers, nfirstcheck, ndim),
                                  maxshape=(nwalkers, None, ndim))
    lnpout  = sdat.create_dataset('lnprobability', (nwalkers, nfirstcheck),
                                  maxshape=(nwalkers, None))
    kl      = sdat.create_dataset('kl_divergence', (conv_crit, ndim),
                                  maxshape=(None, ndim))
    kl_iter = sdat.create_dataset('kl_iteration', (conv_crit,),
                                  maxshape=(None,))

    # Main loop over iterations of the MCMC sampler
    for i, result in enumerate(esampler.sample(initial, **mc_args)):
        chain[:, i, :] = result[0]
        lnpout[:, i] = result[1]
        do_convergence_check = ((convergence_check_interval is not None) and
                                (i+1 >= nfirstcheck) and
                                ((i+1 - nfirstcheck) % convergence_check_interval == 0))
        if do_convergence_check:
            if verbose:
                print('checking convergence after iteration {0}').format(i+1)

            converged, info = convergence_check(chain,