{"action":"created","comment":{"url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/comments/80479763","id":80479763,"diff_hunk":"@@ -0,0 +1,994 @@\n+# Copyright (C) 2016  Collin Capano\n+# This program is free software; you can redistribute it and/or modify it\n+# under the terms of the GNU General Public License as published by the\n+# Free Software Foundation; either version 3 of the License, or (at your\n+# option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful, but\n+# WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General\n+# Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License along\n+# with this program; if not, write to the Free Software Foundation, Inc.,\n+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+\n+\n+#\n+# =============================================================================\n+#\n+#                                   Preamble\n+#\n+# =============================================================================\n+#\n+\"\"\"\n+This modules provides classes and functions for using different sampler\n+packages for parameter estimation.\n+\"\"\"\n+\n+import numpy\n+from pycbc.inference.sampler import _BaseMCMCSampler\n+from pycbc.io import WaveformArray, FieldArray\n+from pycbc.filter import autocorrelation\n+\n+#\n+# =============================================================================\n+#\n+#                                   Samplers\n+#\n+# =============================================================================\n+#\n+\n+\n+class EmceeEnsembleSampler(_BaseMCMCSampler):\n+    \"\"\"This class is used to construct an MCMC sampler from the emcee\n+    package's EnsembleSampler.\n+\n+    Parameters\n+    ----------\n+    likelihood_evaluator : likelihood class\n+        An instance of the likelihood class from the\n+        pycbc.inference.likelihood module.\n+    nwalkers : int\n+        Number of walkers to use in sampler.\n+    processes : {None, int}\n+        Number of processes to use with multiprocessing. If None, all available\n+        cores are used.\n+    burn_in_iterations : {None, int}\n+        Set the number of burn in iterations to use. If None,\n+        `burn_in_ieterations` will be initialized to 0.\n+    \"\"\"\n+    name = \"emcee\"\n+\n+    def __init__(self, likelihood_evaluator, nwalkers, processes=None,\n+                 burn_in_iterations=None):\n+\n+        try:\n+            import emcee\n+        except ImportError:\n+            raise ImportError(\"emcee is not installed.\")\n+\n+        # initialize the pool to use\n+        if processes == 1:\n+            pool = None\n+        else:\n+            pool = emcee.interruptible_pool.InterruptiblePool(\n+                processes=processes)\n+\n+        # construct the sampler\n+        ndim = len(likelihood_evaluator.waveform_generator.variable_args)\n+        sampler = emcee.EnsembleSampler(nwalkers, ndim, likelihood_evaluator,\n+            pool=pool)\n+        # initialize\n+        super(EmceeEnsembleSampler, self).__init__(sampler,\n+            likelihood_evaluator, min_burn_in=burn_in_iterations)\n+        self._nwalkers = nwalkers\n+\n+    @classmethod\n+    def from_cli(cls, opts, likelihood_evaluator):\n+        \"\"\"Create an instance of this sampler from the given command-line\n+        options.\n+\n+        Parameters\n+        ----------\n+        opts : ArgumentParser options\n+            The options to parse.\n+        likelihood_evaluator : LikelihoodEvaluator\n+            The likelihood evaluator to use with the sampler.\n+\n+        Returns\n+        -------\n+        EmceeEnsembleSampler\n+            An emcee sampler initialized based on the given arguments.\n+        \"\"\"\n+        # check that if not skipping burn in, more than one burn in iteration\n+        # has been specified\n+        if not opts.skip_burn_in and (\n+                opts.min_burn_in is None or opts.min_burn_in == 0):\n+            raise ValueError(\"{name} requires that you provide a \".format(\n+                name=cls.name) + \" non-zero --min-burn-in if not skipping \"\n+                \"burn-in\")\n+        return cls(likelihood_evaluator, opts.nwalkers,\n+                   processes=opts.nprocesses,\n+                   burn_in_iterations=opts.min_burn_in)\n+\n+    @property\n+    def lnpost(self):\n+        \"\"\"Get the natural logarithm of the likelihood as an \n+        nwalkers x niterations array.\n+        \"\"\"\n+        # emcee returns nwalkers x niterations\n+        return self._sampler.lnprobability\n+\n+    @property\n+    def chain(self):\n+        \"\"\"Get all past samples as an nwalker x niterations x ndim array.\"\"\"\n+        # emcee returns the chain as nwalker x niterations x ndim\n+        return self._sampler.chain\n+\n+    def run(self, niterations, **kwargs):\n+        \"\"\"Advance the ensemble for a number of samples.\n+\n+        Parameters\n+        ----------\n+        niterations : int\n+            Number of samples to get from sampler.\n+\n+        Returns\n+        -------\n+        p : numpy.array\n+            An array of current walker positions with shape (nwalkers, ndim).\n+        lnpost : numpy.array\n+            The list of log posterior probabilities for the walkers at\n+            positions p, with shape (nwalkers, ndim).\n+        rstate : \n+            The current state of the random number generator.\n+        \"\"\"\n+        pos = self._pos\n+        if pos is None:\n+            pos = self.p0\n+        res = self._sampler.run_mcmc(pos, niterations, **kwargs)\n+        p, lnpost, rstate = res[0], res[1], res[2] \n+        # update the positions\n+        self._pos = p\n+        return p, lnpost, rstate\n+\n+    def burn_in(self, **kwargs):\n+        \"\"\"Advance the ensemble by the number of the sampler's\n+        `burn_in_iterations`.\n+\n+        Returns\n+        -------\n+        p : numpy.array\n+            An array of current walker positions with shape (nwalkers, ndim).\n+        lnpost : {None, numpy.array}\n+            The list of log posterior probabilities for the walkers at\n+            positions p, with shape (nwalkers, ndim).\n+        rstate : \n+            The current state of the random number generator.\n+        \"\"\"\n+        if self.burn_in_iterations == 0:\n+            raise ValueError(\"must specify a non-zero number of iterations \"\n+                             \"to burn in\")\n+        return self.run(self.burn_in_iterations, **kwargs)\n+\n+    # Emcee defines acceptance fraction differently, so have to override\n+    # write functions\n+    def write_acceptance_fraction(self, fp):\n+        \"\"\"Write acceptance_fraction data to file. Results are written to\n+        `fp[acceptance_fraction]`.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        \"\"\"\n+        dataset_name = \"acceptance_fraction\"\n+        try:\n+            fp[dataset_name][:] = self.acceptance_fraction\n+        except KeyError:\n+            # dataset doesn't exist yet, create it\n+            fp[dataset_name] = self.acceptance_fraction\n+\n+    @staticmethod\n+    def read_acceptance_fraction(fp, walkers=None):\n+        \"\"\"Reads the acceptance fraction from the given file.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            An open file handler to read the samples from.\n+        walkers : {None, (list of) int}\n+            The walker index (or a list of indices) to retrieve. If None,\n+            samples from all walkers will be obtained.\n+\n+        Returns\n+        -------\n+        array\n+            Array of acceptance fractions with shape (requested walkers,).\n+        \"\"\"\n+        group = 'acceptance_fraction'\n+        if walkers is None:\n+            wmask = numpy.ones(fp.nwalkers, dtype=bool)\n+        else:\n+            wmask = numpy.zeros(fp.nwalkers, dtype=bool)\n+            wmask[walkers] = True\n+        return fp[group][wmask]\n+\n+    def write_results(self, fp, max_iterations=None):\n+        \"\"\"Writes metadata, samples, likelihood stats, and acceptance fraction\n+        to the given file. See the write function for each of those for\n+        details.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        max_iterations : {None, int}\n+            If results have not previously been written to the\n+            file, new datasets will be created. By default, the size of these\n+            datasets will be whatever the length of the sampler's chain is at\n+            this point. If you intend to run more iterations in the future,\n+            set this value to that size so that the array in the file will be\n+            large enough to accomodate future data.\n+        \"\"\"\n+        self.write_metadata(fp)\n+        self.write_chain(fp, max_iterations=max_iterations)\n+        self.write_likelihood_stats(fp, max_iterations=max_iterations)\n+        self.write_acceptance_fraction(fp)\n+\n+\n+class EmceePTSampler(_BaseMCMCSampler):\n+    \"\"\"This class is used to construct a parallel-tempered MCMC sampler from\n+    the emcee package's PTSampler.\n+\n+    Parameters\n+    ----------\n+    likelihood_evaluator : likelihood class\n+        An instance of the likelihood class from the\n+        pycbc.inference.likelihood module.\n+    ntemps : int\n+        Number of temeratures to use in the sampler.\n+    nwalkers : int\n+        Number of walkers to use in sampler.\n+    processes : {None, int}\n+        Number of processes to use with multiprocessing. If None, all available\n+        cores are used.\n+    burn_in_iterations : {None, int}\n+        Set the number of burn in iterations to use. If None,\n+        `burn_in_ieterations` will be initialized to 0.\n+    \"\"\"\n+    name = \"emcee_pt\"\n+\n+    def __init__(self, likelihood_evaluator, ntemps, nwalkers, processes=None,\n+                 burn_in_iterations=None):\n+\n+        try:\n+            import emcee\n+        except ImportError:\n+            raise ImportError(\"emcee is not installed.\")\n+\n+        # construct the sampler: PTSampler needs the likelihood and prior\n+        # functions separately\n+        likelihood_evaluator.set_callfunc('loglikelihood')\n+\n+        # initialize the pool to use\n+        if processes == 1:\n+            pool = None\n+        else:\n+            pool = emcee.interruptible_pool.InterruptiblePool(\n+                processes=processes)\n+\n+        ndim = len(likelihood_evaluator.waveform_generator.variable_args)\n+        sampler = emcee.PTSampler(ntemps, nwalkers, ndim,\n+            likelihood_evaluator, likelihood_evaluator._prior,\n+            pool=pool)\n+        # initialize\n+        super(EmceePTSampler, self).__init__(sampler,\n+            likelihood_evaluator, min_burn_in=burn_in_iterations)\n+        self._nwalkers = nwalkers\n+        self._ntemps = ntemps\n+\n+    @classmethod\n+    def from_cli(cls, opts, likelihood_evaluator):\n+        \"\"\"Create an instance of this sampler from the given command-line\n+        options.\n+\n+        Parameters\n+        ----------\n+        opts : ArgumentParser options\n+            The options to parse.\n+        likelihood_evaluator : LikelihoodEvaluator\n+            The likelihood evaluator to use with the sampler.\n+\n+        Returns\n+        -------\n+        EmceePTSampler\n+            An emcee sampler initialized based on the given arguments.\n+        \"\"\"\n+        # check that if not skipping burn in, more than one burn in iteration\n+        # has been specified\n+        if not opts.skip_burn_in and (\n+                opts.min_burn_in is None or opts.min_burn_in == 0):\n+            raise ValueError(\"%s requires that you provide a non-zero \"%(\n+                cls.name) + \"--min-burn-in if not skipping burn-in\")\n+        return cls(likelihood_evaluator, opts.ntemps, opts.nwalkers,\n+                   processes=opts.nprocesses,\n+                   burn_in_iterations=opts.min_burn_in)\n+\n+    @property\n+    def ntemps(self):\n+        return self._ntemps\n+\n+    @property\n+    def chain(self):\n+        \"\"\"Get all past samples as an ntemps x nwalker x niterations x ndim\n+        array.\n+        \"\"\"\n+        # emcee returns the chain as ntemps x nwalker x niterations x ndim\n+        return self._sampler.chain\n+\n+    @property\n+    def likelihood_stats(self):\n+        \"\"\"Returns the log likelihood ratio and log prior as a FieldArray.\n+        The returned array has shape ntemps x nwalkers x niterations.\n+        \"\"\"\n+        # likelihood has shape ntemps x nwalkers x niterations\n+        logl = self._sampler.lnlikelihood\n+        # get prior from posterior\n+        logp = self._sampler.lnprobability - logl\n+        # compute the likelihood ratio\n+        loglr = logl - self.likelihood_evaluator.lognl\n+        return FieldArray.from_kwargs(loglr=loglr, prior=logp)\n+\n+    @property\n+    def lnpost(self):\n+        \"\"\"Get the natural logarithm of the likelihood + the prior as an \n+        ntemps x nwalkers x niterations array.\n+        \"\"\"\n+        # emcee returns ntemps x nwalkers x niterations\n+        return self._sampler.lnprobability\n+\n+    def set_p0(self, prior_distributions):\n+        \"\"\"Sets the initial position of the walkers.\n+\n+        Parameters\n+        ----------\n+        prior_distributions : list \n+            A list of priors to retrieve random values from (the sort of\n+            thing returned by `prior.read_distributions_from_config`).\n+\n+        Returns\n+        -------\n+        p0 : array\n+            An ntemps x nwalkers x ndim array of the initial positions that\n+            were set.\n+        \"\"\"\n+        # loop over all walkers and then parameters\n+        # find the distribution that has that parameter in it and draw a\n+        # random value from the distribution\n+        ntemps = self.ntemps\n+        nwalkers = self.nwalkers\n+        ndim = len(self.variable_args)\n+        pmap = dict([[param,k] for k,param in enumerate(self.variable_args)])\n+        p0 = numpy.ones((ntemps, nwalkers, ndim))\n+        for dist in prior_distributions:\n+            ps = dist.rvs(size=ntemps*nwalkers).reshape(ntemps, nwalkers)\n+            for param in dist.params:\n+                p0[:,:,pmap[param]] = ps[param]\n+        self._p0 = p0\n+        return p0\n+\n+    def run(self, niterations, **kwargs):\n+        \"\"\"Advance the ensemble for a number of samples.\n+\n+        Parameters\n+        ----------\n+        niterations : int\n+            Number of samples to get from sampler.\n+\n+        Returns\n+        -------\n+        p : numpy.array\n+            An array of current walker positions with shape (nwalkers, ndim).\n+        lnpost : numpy.array\n+            The list of log posterior probabilities for the walkers at\n+            positions p, with shape (nwalkers, ndim).\n+        rstate : \n+            The current state of the random number generator.\n+        \"\"\"\n+        pos = self._pos\n+        if pos is None:\n+            pos = self.p0\n+        res = self._sampler.run_mcmc(pos, niterations, **kwargs)\n+        p, lnpost, rstate = res[0], res[1], res[2] \n+        # update the positions\n+        self._pos = p\n+        return p, lnpost, rstate\n+\n+    def burn_in(self, **kwargs):\n+        \"\"\"Advance the ensemble by the number of the sampler's\n+        `burn_in_iterations`.\n+\n+        Returns\n+        -------\n+        p : numpy.array\n+            An array of current walker positions with shape (nwalkers, ndim).\n+        lnpost : {None, numpy.array}\n+            The list of log posterior probabilities for the walkers at\n+            positions p, with shape (nwalkers, ndim).\n+        rstate : \n+            The current state of the random number generator.\n+        \"\"\"\n+        if self.burn_in_iterations == 0:\n+            raise ValueError(\"must specify a non-zero number of iterations \"\n+                             \"to burn in\")\n+        return self.run(self.burn_in_iterations, **kwargs)\n+\n+    # read/write functions\n+\n+    # add ntemps and betas to metadata\n+    def write_metadata(self, fp):\n+        \"\"\"Writes metadata about this sampler to the given file. Metadata is\n+        written to the file's `attrs`.\n+\n+        Parameters\n+        ----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        \"\"\"\n+        super(EmceePTSampler, self).write_metadata(fp)\n+        fp.attrs[\"ntemps\"] = self.ntemps\n+        fp.attrs[\"betas\"] = self._sampler.betas\n+\n+    def write_acceptance_fraction(self, fp):\n+        \"\"\"Write acceptance_fraction data to file. Results are written to\n+        `fp[acceptance_fraction/temp{k}]` where k is the temperature.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        \"\"\"\n+        group = \"acceptance_fraction/temp{tk}\"\n+        # acf has shape ntemps x nwalkers\n+        acf = self.acceptance_fraction\n+        for tk in range(fp.ntemps):\n+            try:\n+                fp[group.format(tk=tk)][:] = acf[tk,:]\n+            except KeyError:\n+                # dataset doesn't exist yet, create it\n+                fp[group.format(tk=tk)] = acf[tk,:]\n+\n+    @staticmethod\n+    def read_acceptance_fraction(fp, temps=None, walkers=None):\n+        \"\"\"Reads the acceptance fraction from the given file.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            An open file handler to read the samples from.\n+        temps : {None, (list of) int}\n+            The temperature index (or a list of indices) to retrieve. If None,\n+            acfs from all temperatures and all walkers will be retrieved.\n+        walkers : {None, (list of) int}\n+            The walker index (or a list of indices) to retrieve. If None,\n+            samples from all walkers will be obtained.\n+\n+        Returns\n+        -------\n+        array\n+            Array of acceptance fractions with shape (requested temps,\n+            requested walkers).\n+        \"\"\"\n+        group = 'acceptance_fraction/temp{tk}'\n+        if temps is None:\n+            temps = numpy.arange(fp.ntemps)\n+        if walkers is None:\n+            wmask = numpy.ones(fp.nwalkers, dtype=bool)\n+        else:\n+            wmask = numpy.zeros(fp.nwalkers, dtype=bool)\n+            wmask[walkers] = True\n+        arrays = []\n+        for tk in temps:\n+            arrays.append(fp[group.format(tk=tk)][wmask])\n+        return numpy.vstack(arrays)\n+\n+    def write_chain(self, fp, max_iterations=None):\n+        \"\"\"Writes the samples from the current chain to the given file. Results\n+        are written to: `fp[fp.samples_group/{vararg}/temp{k}/walker{i}]`,\n+        where `{vararg}` is the name of a variable arg, `{k}` is a temperature\n+        index (smaller = colder), and `{i}` is the index of a walker.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        max_iterations : {None, int}\n+            If samples have not previously been written to the file, a new\n+            dataset will be created. By default, the size of this dataset will\n+            be whatever the length of the sampler's chain is at this point. If\n+            you intend to run more iterations, set this value to that size so\n+            that the array in the file will be large enough to accomodate\n+            future data.\n+        \"\"\"\n+        # chain is ntemps x nwalkers x niterations x ndim\n+        samples = self.chain\n+        ntemps, nwalkers, niterations, _ = samples.shape\n+\n+        group = fp.samples_group + '/{name}/temp{tk}/walker{wi}'\n+\n+        # create an empty array if desired, in case this is the first time\n+        # writing\n+        if max_iterations is not None:\n+            if max_iterations < niterations:\n+                raise IndexError(\"The provided max size is less than the \"\n+                    \"number of iterations\")\n+            out = numpy.zeros(max_iterations, dtype=samples.dtype)\n+\n+        # create indices for faster sub-looping\n+        widx = numpy.arange(nwalkers)\n+        tidx = numpy.arange(ntemps)\n+\n+        # loop over number of dimensions\n+        for pi,param in enumerate(self.variable_args):\n+            # loop over number of temps\n+            for tk in tidx:\n+                # loop over number of walkers\n+                for wi in widx:\n+                    dataset_name = group.format(name=param, tk=tk, wi=wi)\n+                    try:\n+                        fp[dataset_name][:niterations] = samples[tk,wi,:,pi]\n+                    except KeyError:\n+                        # dataset doesn't exist yet, see if a larger array is\n+                        # desired\n+                        if max_iterations is not None:\n+                            out[:niterations] = samples[tk,wi,:,pi]\n+                            fp[dataset_name] = out\n+                        else:\n+                            fp[dataset_name] = samples[tk,wi,:,pi]\n+\n+    def write_likelihood_stats(self, fp, max_iterations=None):\n+        \"\"\"Writes the given likelihood array to the given file. Results are\n+        written to: `fp[fp.stats_group/{field}/temp{k}/walker{i}]`, where\n+        `{field}` is the name of stat (`loglr`, `prior`), `{k}` is a\n+        temperature index (smaller = colder) and `{i}` is the index of a\n+        walker.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        max_iterations : {None, int}\n+            See `write_chain` for details.\n+        \"\"\"\n+        lls = self.likelihood_stats\n+        ntemps, nwalkers, niterations = lls.shape\n+\n+        group = fp.stats_group + '/{field}/temp{tk}/walker{wi}'\n+\n+        if max_iterations is not None and max_iterations < niterations:\n+            raise IndexError(\"The provided max size is less than the \"\n+                \"number of iterations\")\n+\n+        # create indices for faster sub-looping\n+        widx = numpy.arange(nwalkers)\n+        tidx = numpy.arange(ntemps)\n+\n+        # loop over stats\n+        for stat in lls.fieldnames:\n+            arr = lls[stat]\n+            # create an empty array if desired, in case this is the first time\n+            # writing\n+            if max_iterations is not None:\n+                out = numpy.zeros(max_iterations, dtype=arr.dtype)\n+            # loop over temps\n+            for tk in tidx:\n+                # loop over number of walkers\n+                for wi in widx:\n+                    dataset_name = group.format(field=stat, tk=tk, wi=wi)\n+                    try:\n+                        fp[dataset_name][:niterations] = arr[tk,wi,:]\n+                    except KeyError:\n+                        # dataset doesn't exist yet, see if a larger array is\n+                        # desired\n+                        if max_iterations is not None:\n+                            out[:niterations] = arr[tk,wi,:]\n+                            fp[dataset_name] = out\n+                        else:\n+                            fp[dataset_name] = arr[tk,wi,:]\n+\n+    def write_results(self, fp, max_iterations=None):\n+        \"\"\"Writes metadata, samples, lnpost, lnprior,  and acceptance fraction\n+        to the given file. See the write function for each of those for\n+        details.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            A file handler to an open inference file.\n+        max_iterations : {None, int}\n+            If results have not previously been written to the\n+            file, new datasets will be created. By default, the size of these\n+            datasets will be whatever the length of the sampler's chain is at\n+            this point. If you intend to run more iterations in the future,\n+            set this value to that size so that the array in the file will be\n+            large enough to accomodate future data.\n+        \"\"\"\n+        self.write_metadata(fp)\n+        self.write_chain(fp, max_iterations=max_iterations)\n+        self.write_likelihood_stats(fp, max_iterations=max_iterations)\n+        self.write_acceptance_fraction(fp)\n+\n+    @staticmethod\n+    def _read_fields(fp, fields_group, fields, array_class,\n+            thin_start=None, thin_interval=None, thin_end=None, iteration=None,\n+            temps=None, walkers=None, flatten=True):\n+        \"\"\"Base function for reading samples and likelihood stats. See\n+        `read_samples` and `read_likelihood_stats` for details.\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            An open file handler to read the samples from.\n+        fields_group : str\n+            The name of the group to retrieve the desired fields.\n+        fields : list\n+            The list of field names to retrieve. Must be names of groups in\n+            `fp[fields_group/]`.\n+        array_class : FieldArray or similar\n+            The type of array to return. Must have a `from_kwargs` attribute.\n+\n+        For other details on keyword arguments, see `read_samples` and\n+        `read_likelihood_stats`.\n+\n+        Returns\n+        -------\n+        array_class\n+            An instance of the given array class populated with values\n+            retrieved from the fields.\n+        \"\"\"\n+        # walkers to load\n+        if walkers is None:\n+            walkers = range(fp.nwalkers)\n+        if isinstance(walkers, int):\n+            walkers = [walkers]\n+\n+        # temperatures to load\n+        if temps is None:\n+            temps = 0\n+        if temps == 'all':\n+            temps = range(fp.ntemps)\n+        if isinstance(temps, int):\n+            temps = [temps]\n+\n+        # get the slice to use\n+        if iteration is not None:\n+            get_index = iteration\n+        else:\n+            if thin_end is None:\n+                # use the number of current iterations\n+                thin_end = fp.niterations\n+            get_index = get_slice(fp, thin_start=thin_start, thin_end=thin_end,\n+                thin_interval=thin_interval)\n+\n+        # load\n+        arrays = {}\n+        group = fields_group + '/{name}/temp{tk}/walker{wi}'\n+        for name in fields:\n+            these_arrays = numpy.array([\n+                [fp[group.format(name=name, wi=wi, tk=tk)][get_index]\n+                for wi in walkers]\n+                for tk in temps])\n+            if flatten:\n+                these_arrays = these_arrays.flatten()\n+            arrays[name] = these_arrays\n+        return array_class.from_kwargs(**arrays)\n+\n+    @classmethod\n+    def read_samples(cls, fp, parameters,\n+            thin_start=None, thin_interval=None, thin_end=None, iteration=None,\n+            temps=0, walkers=None, flatten=True, samples_group=None,\n+            array_class=None):\n+        \"\"\"Reads samples for the given parameter(s).\n+\n+        Parameters\n+        -----------\n+        fp : InferenceFile\n+            An open file handler to read the samples from.\n+        parameters : (list of) strings\n+            The parameter(s) to retrieve. A parameter can be the name of any\n+            field in `fp[fp.samples_group]`, a virtual field or method of\n+            `WaveformArray` (as long as the file contains the necessary fields\n+            to derive the virtual field or method), and/or a function of\n+            these.\n+        thin_start : int\n+            Index of the sample to begin returning samples. Default is to read\n+            samples after burn in. To start from the beginning set thin_start\n+            to 0.\n+        thin_interval : int\n+            Interval to accept every i-th sample. Default is to use the\n+            `fp.acl`. If `fp.acl` is not set, then use all samples\n+            (set thin_interval to 1).\n+        thin_end : int\n+            Index of the last sample to read. If not given then\n+            `fp.niterations` is used.\n+        iteration : int\n+            Get a single iteration. If provided, will override the\n+            `thin_{start/interval/end}` arguments.\n+        walkers : {None, (list of) int}\n+            The walker index (or a list of indices) to retrieve. If None,\n+            samples from all walkers will be obtained.\n+        temps : {None, (list of) int, 'all'}\n+            The temperature index (or list of indices) to retrieve. If None,\n+            only samples from the coldest (= 0) temperature chain will be\n+            retrieved. To retrieve all temperates pass 'all', or a list of\n+            all of the temperatures.\n+        flatten : {True, bool}\n+            The returned array will be one dimensional, with all desired\n+            samples from all desired walkers concatenated together. If False,\n+            the returned array will have dimension requested temps x requested \n+            walkers x requested iterations.\n+        samples_group : {None, str}\n+            The group in `fp` from which to retrieve the parameter fields. If\n+            None, searches in `fp.samples_group`.\n+        array_class : {None, array class}\n+            The type of array to return. The class must have a `from_kwargs`\n+            class method and a `parse_parameters` method. If None, will return\n+            a WaveformArray.\n+\n+        Returns\n+        -------\n+        array_class\n+            Samples for the given parameters, as an instance of a the given\n+            `array_class` (`WaveformArray` if `array_class` is None).\n+        \"\"\"\n+        # get the group to load from\n+        if samples_group is None:\n+            samples_group = fp.samples_group\n+        # get the type of array class to use\n+        if array_class is None:\n+            array_class = WaveformArray\n+        # get the names of fields needed for the given parameters\n+        possible_fields = dict([[str(name), float]\n+            for name in fp[fp.samples_group].keys()])\n+        loadfields = array_class.parse_parameters(parameters,\n+            possible_fields=possible_fields)\n+        return cls._read_fields(fp, samples_group, loadfields, array_class,\n+                thin_start=thin_start, thin_interval=thin_interval,\n+                thin_end=thin_end, iteration=iteration, temps=temps,\n+                walkers=walkers, flatten=flatten)","path":"pycbc/inference/sampler_emcee.py","position":760,"original_position":760,"commit_id":"1df3c4ed8dcb624e4f1e6a38f31fc86ff7d1ab1f","original_commit_id":"1df3c4ed8dcb624e4f1e6a38f31fc86ff7d1ab1f","user":{"login":"ahnitz","id":2206534,"avatar_url":"https://avatars.githubusercontent.com/u/2206534?v=3","gravatar_id":"","url":"https://api.github.com/users/ahnitz","html_url":"https://github.com/ahnitz","followers_url":"https://api.github.com/users/ahnitz/followers","following_url":"https://api.github.com/users/ahnitz/following{/other_user}","gists_url":"https://api.github.com/users/ahnitz/gists{/gist_id}","starred_url":"https://api.github.com/users/ahnitz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ahnitz/subscriptions","organizations_url":"https://api.github.com/users/ahnitz/orgs","repos_url":"https://api.github.com/users/ahnitz/repos","events_url":"https://api.github.com/users/ahnitz/events{/privacy}","received_events_url":"https://api.github.com/users/ahnitz/received_events","type":"User","site_admin":false},"body":"__[flake8]__\n* [E128] continuation line under-indented for visual indent\n\n<!-- Comment by [SideCI](https://sideci.com) -->","created_at":"2016-09-26T13:55:55Z","updated_at":"2016-09-26T13:55:55Z","html_url":"https://github.com/ligo-cbc/pycbc/pull/1101#discussion_r80479763","pull_request_url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101","_links":{"self":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/comments/80479763"},"html":{"href":"https://github.com/ligo-cbc/pycbc/pull/1101#discussion_r80479763"},"pull_request":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101"}}},"pull_request":{"url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101","id":86633669,"html_url":"https://github.com/ligo-cbc/pycbc/pull/1101","diff_url":"https://github.com/ligo-cbc/pycbc/pull/1101.diff","patch_url":"https://github.com/ligo-cbc/pycbc/pull/1101.patch","issue_url":"https://api.github.com/repos/ligo-cbc/pycbc/issues/1101","number":1101,"state":"open","locked":false,"title":"Seperate samplers into different modules","user":{"login":"cmbiwer","id":10871764,"avatar_url":"https://avatars.githubusercontent.com/u/10871764?v=3","gravatar_id":"","url":"https://api.github.com/users/cmbiwer","html_url":"https://github.com/cmbiwer","followers_url":"https://api.github.com/users/cmbiwer/followers","following_url":"https://api.github.com/users/cmbiwer/following{/other_user}","gists_url":"https://api.github.com/users/cmbiwer/gists{/gist_id}","starred_url":"https://api.github.com/users/cmbiwer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmbiwer/subscriptions","organizations_url":"https://api.github.com/users/cmbiwer/orgs","repos_url":"https://api.github.com/users/cmbiwer/repos","events_url":"https://api.github.com/users/cmbiwer/events{/privacy}","received_events_url":"https://api.github.com/users/cmbiwer/received_events","type":"User","site_admin":false},"body":"This separates out the classes in ``pycbc/inference/sampler.py`` into different modules. This module was getting to be an unmanageable size and for the sake of readability I think these need to be separated out.\r\n\r\nThis creates ``sampler_kombine.py`` and puts the kombine sampler in there. And ``sampler_emcee.py`` and puts the two emcee samplers in there.\r\n\r\nIt also removes a deprecated function for reading labels from the configuration file since now we are using the ``Parameters`` class.\r\n\r\nIt also makes the ``_pregenerator`` functions in ``generator.py`` top-level so that they can be pickled and used by the MCMC samplers.\r\n\r\nAnd adds the whitespace that Alex wanted in a different PR.\r\n\r\n","created_at":"2016-09-24T23:05:23Z","updated_at":"2016-09-26T13:55:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7f4902613896e5639e3d41705e929eac29b6f401","assignee":{"login":"cdcapano","id":8258446,"avatar_url":"https://avatars.githubusercontent.com/u/8258446?v=3","gravatar_id":"","url":"https://api.github.com/users/cdcapano","html_url":"https://github.com/cdcapano","followers_url":"https://api.github.com/users/cdcapano/followers","following_url":"https://api.github.com/users/cdcapano/following{/other_user}","gists_url":"https://api.github.com/users/cdcapano/gists{/gist_id}","starred_url":"https://api.github.com/users/cdcapano/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdcapano/subscriptions","organizations_url":"https://api.github.com/users/cdcapano/orgs","repos_url":"https://api.github.com/users/cdcapano/repos","events_url":"https://api.github.com/users/cdcapano/events{/privacy}","received_events_url":"https://api.github.com/users/cdcapano/received_events","type":"User","site_admin":false},"assignees":[{"login":"cdcapano","id":8258446,"avatar_url":"https://avatars.githubusercontent.com/u/8258446?v=3","gravatar_id":"","url":"https://api.github.com/users/cdcapano","html_url":"https://github.com/cdcapano","followers_url":"https://api.github.com/users/cdcapano/followers","following_url":"https://api.github.com/users/cdcapano/following{/other_user}","gists_url":"https://api.github.com/users/cdcapano/gists{/gist_id}","starred_url":"https://api.github.com/users/cdcapano/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdcapano/subscriptions","organizations_url":"https://api.github.com/users/cdcapano/orgs","repos_url":"https://api.github.com/users/cdcapano/repos","events_url":"https://api.github.com/users/cdcapano/events{/privacy}","received_events_url":"https://api.github.com/users/cdcapano/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101/commits","review_comments_url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101/comments","review_comment_url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ligo-cbc/pycbc/issues/1101/comments","statuses_url":"https://api.github.com/repos/ligo-cbc/pycbc/statuses/1df3c4ed8dcb624e4f1e6a38f31fc86ff7d1ab1f","head":{"label":"cmbiwer:sep_pr","ref":"sep_pr","sha":"1df3c4ed8dcb624e4f1e6a38f31fc86ff7d1ab1f","user":{"login":"cmbiwer","id":10871764,"avatar_url":"https://avatars.githubusercontent.com/u/10871764?v=3","gravatar_id":"","url":"https://api.github.com/users/cmbiwer","html_url":"https://github.com/cmbiwer","followers_url":"https://api.github.com/users/cmbiwer/followers","following_url":"https://api.github.com/users/cmbiwer/following{/other_user}","gists_url":"https://api.github.com/users/cmbiwer/gists{/gist_id}","starred_url":"https://api.github.com/users/cmbiwer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmbiwer/subscriptions","organizations_url":"https://api.github.com/users/cmbiwer/orgs","repos_url":"https://api.github.com/users/cmbiwer/repos","events_url":"https://api.github.com/users/cmbiwer/events{/privacy}","received_events_url":"https://api.github.com/users/cmbiwer/received_events","type":"User","site_admin":false},"repo":{"id":35433992,"name":"pycbc","full_name":"cmbiwer/pycbc","owner":{"login":"cmbiwer","id":10871764,"avatar_url":"https://avatars.githubusercontent.com/u/10871764?v=3","gravatar_id":"","url":"https://api.github.com/users/cmbiwer","html_url":"https://github.com/cmbiwer","followers_url":"https://api.github.com/users/cmbiwer/followers","following_url":"https://api.github.com/users/cmbiwer/following{/other_user}","gists_url":"https://api.github.com/users/cmbiwer/gists{/gist_id}","starred_url":"https://api.github.com/users/cmbiwer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmbiwer/subscriptions","organizations_url":"https://api.github.com/users/cmbiwer/orgs","repos_url":"https://api.github.com/users/cmbiwer/repos","events_url":"https://api.github.com/users/cmbiwer/events{/privacy}","received_events_url":"https://api.github.com/users/cmbiwer/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/cmbiwer/pycbc","description":"Python CBC Tools","fork":true,"url":"https://api.github.com/repos/cmbiwer/pycbc","forks_url":"https://api.github.com/repos/cmbiwer/pycbc/forks","keys_url":"https://api.github.com/repos/cmbiwer/pycbc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cmbiwer/pycbc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cmbiwer/pycbc/teams","hooks_url":"https://api.github.com/repos/cmbiwer/pycbc/hooks","issue_events_url":"https://api.github.com/repos/cmbiwer/pycbc/issues/events{/number}","events_url":"https://api.github.com/repos/cmbiwer/pycbc/events","assignees_url":"https://api.github.com/repos/cmbiwer/pycbc/assignees{/user}","branches_url":"https://api.github.com/repos/cmbiwer/pycbc/branches{/branch}","tags_url":"https://api.github.com/repos/cmbiwer/pycbc/tags","blobs_url":"https://api.github.com/repos/cmbiwer/pycbc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cmbiwer/pycbc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cmbiwer/pycbc/git/refs{/sha}","trees_url":"https://api.github.com/repos/cmbiwer/pycbc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cmbiwer/pycbc/statuses/{sha}","languages_url":"https://api.github.com/repos/cmbiwer/pycbc/languages","stargazers_url":"https://api.github.com/repos/cmbiwer/pycbc/stargazers","contributors_url":"https://api.github.com/repos/cmbiwer/pycbc/contributors","subscribers_url":"https://api.github.com/repos/cmbiwer/pycbc/subscribers","subscription_url":"https://api.github.com/repos/cmbiwer/pycbc/subscription","commits_url":"https://api.github.com/repos/cmbiwer/pycbc/commits{/sha}","git_commits_url":"https://api.github.com/repos/cmbiwer/pycbc/git/commits{/sha}","comments_url":"https://api.github.com/repos/cmbiwer/pycbc/comments{/number}","issue_comment_url":"https://api.github.com/repos/cmbiwer/pycbc/issues/comments{/number}","contents_url":"https://api.github.com/repos/cmbiwer/pycbc/contents/{+path}","compare_url":"https://api.github.com/repos/cmbiwer/pycbc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cmbiwer/pycbc/merges","archive_url":"https://api.github.com/repos/cmbiwer/pycbc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cmbiwer/pycbc/downloads","issues_url":"https://api.github.com/repos/cmbiwer/pycbc/issues{/number}","pulls_url":"https://api.github.com/repos/cmbiwer/pycbc/pulls{/number}","milestones_url":"https://api.github.com/repos/cmbiwer/pycbc/milestones{/number}","notifications_url":"https://api.github.com/repos/cmbiwer/pycbc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cmbiwer/pycbc/labels{/name}","releases_url":"https://api.github.com/repos/cmbiwer/pycbc/releases{/id}","deployments_url":"https://api.github.com/repos/cmbiwer/pycbc/deployments","created_at":"2015-05-11T15:55:44Z","updated_at":"2016-01-28T20:54:13Z","pushed_at":"2016-09-26T13:55:33Z","git_url":"git://github.com/cmbiwer/pycbc.git","ssh_url":"git@github.com:cmbiwer/pycbc.git","clone_url":"https://github.com/cmbiwer/pycbc.git","svn_url":"https://github.com/cmbiwer/pycbc","homepage":"","size":59825,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"ligo-cbc:master","ref":"master","sha":"1246c23a0bdb96c91a574b939ee015ceaddf72ed","user":{"login":"ligo-cbc","id":11262917,"avatar_url":"https://avatars.githubusercontent.com/u/11262917?v=3","gravatar_id":"","url":"https://api.github.com/users/ligo-cbc","html_url":"https://github.com/ligo-cbc","followers_url":"https://api.github.com/users/ligo-cbc/followers","following_url":"https://api.github.com/users/ligo-cbc/following{/other_user}","gists_url":"https://api.github.com/users/ligo-cbc/gists{/gist_id}","starred_url":"https://api.github.com/users/ligo-cbc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ligo-cbc/subscriptions","organizations_url":"https://api.github.com/users/ligo-cbc/orgs","repos_url":"https://api.github.com/users/ligo-cbc/repos","events_url":"https://api.github.com/users/ligo-cbc/events{/privacy}","received_events_url":"https://api.github.com/users/ligo-cbc/received_events","type":"Organization","site_admin":false},"repo":{"id":31596861,"name":"pycbc","full_name":"ligo-cbc/pycbc","owner":{"login":"ligo-cbc","id":11262917,"avatar_url":"https://avatars.githubusercontent.com/u/11262917?v=3","gravatar_id":"","url":"https://api.github.com/users/ligo-cbc","html_url":"https://github.com/ligo-cbc","followers_url":"https://api.github.com/users/ligo-cbc/followers","following_url":"https://api.github.com/users/ligo-cbc/following{/other_user}","gists_url":"https://api.github.com/users/ligo-cbc/gists{/gist_id}","starred_url":"https://api.github.com/users/ligo-cbc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ligo-cbc/subscriptions","organizations_url":"https://api.github.com/users/ligo-cbc/orgs","repos_url":"https://api.github.com/users/ligo-cbc/repos","events_url":"https://api.github.com/users/ligo-cbc/events{/privacy}","received_events_url":"https://api.github.com/users/ligo-cbc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ligo-cbc/pycbc","description":"Python CBC Tools","fork":false,"url":"https://api.github.com/repos/ligo-cbc/pycbc","forks_url":"https://api.github.com/repos/ligo-cbc/pycbc/forks","keys_url":"https://api.github.com/repos/ligo-cbc/pycbc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ligo-cbc/pycbc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ligo-cbc/pycbc/teams","hooks_url":"https://api.github.com/repos/ligo-cbc/pycbc/hooks","issue_events_url":"https://api.github.com/repos/ligo-cbc/pycbc/issues/events{/number}","events_url":"https://api.github.com/repos/ligo-cbc/pycbc/events","assignees_url":"https://api.github.com/repos/ligo-cbc/pycbc/assignees{/user}","branches_url":"https://api.github.com/repos/ligo-cbc/pycbc/branches{/branch}","tags_url":"https://api.github.com/repos/ligo-cbc/pycbc/tags","blobs_url":"https://api.github.com/repos/ligo-cbc/pycbc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ligo-cbc/pycbc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ligo-cbc/pycbc/git/refs{/sha}","trees_url":"https://api.github.com/repos/ligo-cbc/pycbc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ligo-cbc/pycbc/statuses/{sha}","languages_url":"https://api.github.com/repos/ligo-cbc/pycbc/languages","stargazers_url":"https://api.github.com/repos/ligo-cbc/pycbc/stargazers","contributors_url":"https://api.github.com/repos/ligo-cbc/pycbc/contributors","subscribers_url":"https://api.github.com/repos/ligo-cbc/pycbc/subscribers","subscription_url":"https://api.github.com/repos/ligo-cbc/pycbc/subscription","commits_url":"https://api.github.com/repos/ligo-cbc/pycbc/commits{/sha}","git_commits_url":"https://api.github.com/repos/ligo-cbc/pycbc/git/commits{/sha}","comments_url":"https://api.github.com/repos/ligo-cbc/pycbc/comments{/number}","issue_comment_url":"https://api.github.com/repos/ligo-cbc/pycbc/issues/comments{/number}","contents_url":"https://api.github.com/repos/ligo-cbc/pycbc/contents/{+path}","compare_url":"https://api.github.com/repos/ligo-cbc/pycbc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ligo-cbc/pycbc/merges","archive_url":"https://api.github.com/repos/ligo-cbc/pycbc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ligo-cbc/pycbc/downloads","issues_url":"https://api.github.com/repos/ligo-cbc/pycbc/issues{/number}","pulls_url":"https://api.github.com/repos/ligo-cbc/pycbc/pulls{/number}","milestones_url":"https://api.github.com/repos/ligo-cbc/pycbc/milestones{/number}","notifications_url":"https://api.github.com/repos/ligo-cbc/pycbc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ligo-cbc/pycbc/labels{/name}","releases_url":"https://api.github.com/repos/ligo-cbc/pycbc/releases{/id}","deployments_url":"https://api.github.com/repos/ligo-cbc/pycbc/deployments","created_at":"2015-03-03T12:19:19Z","updated_at":"2016-08-18T14:09:58Z","pushed_at":"2016-09-26T13:55:33Z","git_url":"git://github.com/ligo-cbc/pycbc.git","ssh_url":"git@github.com:ligo-cbc/pycbc.git","clone_url":"https://github.com/ligo-cbc/pycbc.git","svn_url":"https://github.com/ligo-cbc/pycbc","homepage":null,"size":176557,"stargazers_count":44,"watchers_count":44,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":84,"mirror_url":null,"open_issues_count":28,"forks":84,"open_issues":28,"watchers":44,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101"},"html":{"href":"https://github.com/ligo-cbc/pycbc/pull/1101"},"issue":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/issues/1101"},"comments":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/issues/1101/comments"},"review_comments":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101/comments"},"review_comment":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/pulls/1101/commits"},"statuses":{"href":"https://api.github.com/repos/ligo-cbc/pycbc/statuses/1df3c4ed8dcb624e4f1e6a38f31fc86ff7d1ab1f"}}}}
{"actor":{"display_login":"ahnitz"}}