def _start_sampling(self): nestle.sample(loglikelihood=self._calc_function, prior_transform=self._loaded_function, ndim=self._ndim, npoints=self._npoints, method=self._method, update_interval=self._update_interval, npdim=self._npdim, maxiter=self._maxiter, maxcall=self._maxcall, dlogz=self._dlogz, decline_factor=self._decline_factor)
def run_flat(method): logl = lambda x: 0.0 prior = lambda x: x res = nestle.sample(logl, prior, 2, method=method, npoints=4, rstate=RandomState(0)) assert_allclose(res.logz, 0.0, atol=1.e-10) assert_allclose(res.h, 0.0, atol=1.e-10)
def nestle_multi2(): # Run nested sampling res = nestle.sample(loglike2, prior_transform2, 3, method='multi', npoints=2000) print(res.summary()) # weighted average and covariance: pm, covm = nestle.mean_and_cov(res.samples, res.weights) # re-scale weights to have a maximum of one nweights = res.weights / np.max(res.weights) # get the probability of keeping a sample from the weights keepidx = np.where(np.random.rand(len(nweights)) < nweights)[0] # get the posterior samples samples_nestle = res.samples[keepidx, :] print(np.mean(samples_nestle[:, 0])) # mean of A samples print(np.std(samples_nestle[:, 0])) # standard deviation of A samples print(np.mean(samples_nestle[:, 1])) # mean of w samples print(np.std(samples_nestle[:, 1])) # standard deviation of w samples print(np.mean(samples_nestle[:, 2])) # mean of t0 samples print(np.std(samples_nestle[:, 2])) # standard deviation of t0 samples print(len(samples_nestle)) # number of posterior samples return res.logz
def run_sampler(self): """ Runs Nestle sampler with given kwargs and returns the result Returns ======= bilby.core.result.Result: Packaged information about the result """ import nestle out = nestle.sample( loglikelihood=self.log_likelihood, prior_transform=self.prior_transform, ndim=self.ndim, **self.kwargs) print("") self.result.sampler_output = out self.result.samples = nestle.resample_equal(out.samples, out.weights) self.result.nested_samples = DataFrame( out.samples, columns=self.search_parameter_keys) self.result.nested_samples['weights'] = out.weights self.result.nested_samples['log_likelihood'] = out.logl self.result.log_likelihood_evaluations = self.reorder_loglikelihoods( unsorted_loglikelihoods=out.logl, unsorted_samples=out.samples, sorted_samples=self.result.samples) self.result.log_evidence = out.logz self.result.log_evidence_err = out.logzerr self.result.information_gain = out.h self.calc_likelihood_count() return self.result
def run_two_gaussians(method): sigma = 0.1 mu1 = np.ones(2) mu2 = -np.ones(2) sigma_inv = np.identity(2) / 0.1**2 def logl(x): dx1 = x - mu1 dx2 = x - mu2 return np.logaddexp(-np.dot(dx1, np.dot(sigma_inv, dx1)) / 2.0, -np.dot(dx2, np.dot(sigma_inv, dx2)) / 2.0) # Flat prior, over [-5, 5] in both dimensions def prior(x): return 10.0 * x - 5.0 #(Approximate) analytic evidence for two identical Gaussian blobs, # over a uniform prior [-5:5][-5:5] with density 1/100 in this domain: analytic_logz = np.log(2.0 * 2.0*np.pi*sigma*sigma / 100.) grid_logz = integrate_on_grid_refine(logl, [(-5., 5.), (-5., 5.)]) res = nestle.sample(logl, prior, 2, method=method, npoints=100, rstate=RandomState(0)) print() print("{}: logz = {:6.3f} +/- {:6.3f}" .format(method, res.logz, res.logzerr)) print(" grid_logz = {0:8.5f}".format(grid_logz)) print(" analytic_logz = {0:8.5f}".format(analytic_logz)) assert abs(res.logz - grid_logz) < 3.0 * res.logzerr assert abs(res.weights.sum() - 1.) < SQRTEPS
def test_eggbox(): tmax = 5.0 * np.pi constant = np.log(1.0 / tmax**2) def loglike(x): t = 2.0 * tmax * x - tmax return (2.0 + np.cos(t[0] / 2.0) * np.cos(t[1] / 2.0))**5.0 def prior(x): return x res = nestle.sample(loglike, prior, 2, npoints=500, method='multi', rstate=RandomState(0)) grid_logz = integrate_on_grid_refine(loglike, [(0., 1.), (0., 1.)]) print("\nEggbox") print("multi : logz = {:6.3f} +/- {:6.3f}".format( res.logz, res.logzerr)) print(" grid_logz = {0:8.5f}".format(grid_logz)) assert abs(res.logz - grid_logz) < 3.0 * res.logzerr
def run_two_gaussians(method): sigma = 0.1 mu1 = np.ones(2) mu2 = -np.ones(2) sigma_inv = np.identity(2) / 0.1**2 def logl(x): dx1 = x - mu1 dx2 = x - mu2 return np.logaddexp(-np.dot(dx1, np.dot(sigma_inv, dx1)) / 2.0, -np.dot(dx2, np.dot(sigma_inv, dx2)) / 2.0) # Flat prior, over [-5, 5] in both dimensions def prior(x): return 10.0 * x - 5.0 #(Approximate) analytic evidence for two identical Gaussian blobs, # over a uniform prior [-5:5][-5:5] with density 1/100 in this domain: analytic_logz = np.log(2.0 * 2.0 * np.pi * sigma * sigma / 100.) grid_logz = integrate_on_grid_refine(logl, [(-5., 5.), (-5., 5.)]) res = nestle.sample(logl, prior, 2, method=method, npoints=100, rstate=RandomState(0)) print() print("{}: logz = {:6.3f} +/- {:6.3f}".format( method, res.logz, res.logzerr)) print(" grid_logz = {0:8.5f}".format(grid_logz)) print(" analytic_logz = {0:8.5f}".format(analytic_logz)) assert abs(res.logz - grid_logz) < 3.0 * res.logzerr
def nested(self, sigma): """ @returns Nested sampling integration of shape parameters """ def loglike(shape): """ @returns Log-likelihood """ self.set_velocity_dist(MB(*shape)) return self.treatment.loglike(sigma) def prior(x): """ @returns Shape parameters sampled from Gaussians """ return (norm.ppf(x[0], *self.V_MODE), norm.ppf(x[1], *self.V_ESCAPE)) result = nestle.sample(loglike, prior, 2, maxcall=1000, dlogz=0.5, npoints=25) return result
def run_sampler(model, prior_transform, data, z=0.306, npts=100): print model.description() data_x, data_y, data_yerr = data Likelihood = model.Likelihood loglike = Likelihood(data_x, data_y, data_yerr, z) ndim = model.N_PARAMS begin_time = time() result = nestle.sample(loglike, prior_transform, ndim, npoints=npts, method='multi', callback=nestle.print_progress) end_time = time() run_time = end_time - begin_time print "Time elapsed = ", run_time print "Time per likelihood call = ", run_time / result.ncall print "log z = ", result.logz # log evidence print "log z err = ", result.logzerr # numerical (sampling) error on logz return result
def run_nestle_sampler(lnprobfn, model, verbose=True, callback=None, nestle_method='single', nestle_npoints=200, nestle_maxcall=int(1e6), **kwargs): result = nestle.sample(lnprobfn, model.prior_transform, model.ndim, method=nestle_method, npoints=nestle_npoints, callback=nestle.print_progress, maxcall=nestle_maxcall) return result
def show_results(loglike_NFW, prior_transform_NFW, n): # n is the dim of theta; for NFW model, n =2 result = nestle.sample(loglike_NFW, prior_transform_NFW, 2) print('log evidence') print(result.logz) print('numerical (sampling) error on logz') print(result.logzerr) print('array of sample parameters') print(result.samples) print('array of weights associated with each sample') print(result.weights) p, cov = nestle.mean_and_cov(result.samples, result.weights) print("core radius a = {0:5.2f} +/- {1:5.2f} kpc".format( p[0], np.sqrt(cov[0, 0]))) print("normalization factor = {0:5.2f} +/- {1:5.2f}".format( p[1], np.sqrt(cov[1, 1]))) print( "Halo density normalization constant = {0:5.2e} +/- {1:5.2e} Msun/kpc^3" .format(2.312E5 * p[1], 2.312E5 * np.sqrt(cov[1, 1]))) # Note: in order to convert the model to units of Msun/kpc^3 we multiply its value by 2.312E5. # See comments in the model definition for details. print("Halo density in our solor system = {0:5.2e} Msun/kpc^3.".format( 2.312E5 * model_NFW(p, 8))) # Note: 1 Msun/kpc^3 = 3.817E-2 (GeV/c^2)/m^3 = 3.817E-5 (GeV/c^2)/(dm^3) # 1 dm^3 = 1 liter. # 3 WIMPS/liter would be 300 GeV/c^2/liter print("Halo density in our solor system = {0:5.2e} GeV/c^2/liter.".format( 3.817E-5 * 2.312E5 * model_NFW(p, 8))) plt.figure() plt.errorbar(data_x, data_y, data_yerr, data_xerr, fmt='*') plt.xlabel("r (kpc)") plt.ylabel('V (km/s)') plt.title( "The measured rotational speed of the interstellar medium as a fucntion of the galactocentric radius" ) plt.plot([5., 200.], model_NFW(p, np.array([5., 200.]))) plt.show() fig = corner.corner(result.samples, weights=result.weights, labels=['a', 'rho0'], range=[0.99999, 0.99999], bins=30) plt.show() return 0
def run_nestle_sampler(lnprobfn, model, verbose=True, callback=None, nestle_method='multi', nestle_npoints=200, nestle_maxcall=int(1e6), nestle_update_interval=None, **kwargs): result = nestle.sample(lnprobfn, model.prior_transform, model.ndim, method=nestle_method, npoints=nestle_npoints, callback=callback, maxcall=nestle_maxcall, update_interval=nestle_update_interval) return result
def run(ndim): """Convenience function for running in any dimension""" c1 = np.zeros(ndim) c1[0] = -3.5 c2 = np.zeros(ndim) c2[0] = 3.5 f = lambda theta: loglike(theta, c1, c2) return nestle.sample(f, prior_transform, ndim, method='multi', npoints=npoints, rstate=rstate)
def run_nestle(self,outfile='TEST.dat'): # initalize outfile self.outfile = open(outfile,'w') self.outfile.write('ITER Teff Dist Rad NH arfsc1 arfsc2 log(z) \n') # Start sampler print('Start Nestle') result = nestle.sample(self.calllike,self.prior_trans,self.ndim,method='multi',npoints=1000,callback=self.nestle_callback) # generate posterior means and covariances p,cov = nestle.mean_and_cov(result.samples,result.weights) # close output file self.outfile.close() return result,p,cov
def run_nestle(logl, logp, ranges, npoints=1000, method='single'): def logprob(p): return logl(p) + logp(p) def prior_transform(p): return (ranges[:, 1] - ranges[:, 0]) * p + ranges[:, 0] return nestle.sample(logprob, prior_transform, len(ranges), method=method, npoints=npoints)
def _run_test(self): """ Runs to test whether the sampler is properly running with the given kwargs without actually running to the end Returns ------- bilby.core.result.Result: Dummy container for sampling results. """ import nestle kwargs = self.kwargs.copy() kwargs['maxiter'] = 2 nestle.sample(loglikelihood=self.log_likelihood, prior_transform=self.prior_transform, ndim=self.ndim, **kwargs) self.result.samples = np.random.uniform(0, 1, (100, self.ndim)) self.result.log_evidence = np.nan self.result.log_evidence_err = np.nan return self.result
def compute_fit(self): """ Computes the fit using nestle """ data = self._observed.spectrum datastd = self._observed.errorBar sqrtpi = np.sqrt(2 * np.pi) def nestle_loglike(params): # log-likelihood function called by multinest fit_params_container = np.array(params) chi_t = self.chisq_trans(fit_params_container, data, datastd) loglike = -np.sum(np.log(datastd * sqrtpi)) - 0.5 * chi_t return loglike def nestle_uniform_prior(theta): # prior distributions called by multinest. Implements a uniform prior # converting parameters from normalised grid to uniform prior cube = [] for idx, prior in enumerate(self.fitting_priors): cube.append(prior.sample(theta[idx])) return tuple(cube) ndim = len(self.fitting_parameters) self.warning('Beginning fit......') ndims = ndim # two parameters t0 = time.time() res = nestle.sample(nestle_loglike, nestle_uniform_prior, ndims, method='multi', npoints=self.numLivePoints, dlogz=self.tolerance, callback=nestle.print_progress) t1 = time.time() timenestle = (t1 - t0) print(res.summary()) self.warning("Time taken to run 'Nestle' is %s seconds", timenestle) self.warning('Fit complete.....') self._nestle_output = self.store_nestle_output(res)
def __start_sampling(self): self.__results = nestle.sample(loglikelihood=self.__calc_function, prior_transform=self.__prior, ndim=self.__ndim, npoints=self.__npoints, method=self.__method, update_interval=self.__update_interval, npdim=self.__npdim, maxiter=self.__maxiter, maxcall=self.__maxcall, dlogz=self.__dlogz, decline_factor=self.__decline_factor, callback=self.__callback_object)
def __start_sampling(self): self.__results = nestle.sample( loglikelihood=self.__calc_function, prior_transform=self.__prior, ndim=self.__ndim, npoints=self.__npoints, method=self.__method, update_interval=self.__update_interval, npdim=self.__npdim, maxiter=self.__maxiter, maxcall=self.__maxcall, dlogz=self.__dlogz, decline_factor=self.__decline_factor, callback=self.__callback_object )
def run(self, verbose=False): """Initiate the Nestle Nested Sampling run.""" callback = None if verbose: callback = nestle.print_progress output = nestle.sample(self.loglikelihood, self._prior_transform, self._nDims, npoints=self.population_size, callback=callback, **self.nestle_kwargs) if verbose: output.summary() self._output = output return self.log_evidence, self.log_evidence_error
def nestle_multi2(): # Run nested sampling res = nestle.sample(loglike2, prior_transform2, 2, method='multi', npoints=2000) # weighted average and covariance: pm, covm = nestle.mean_and_cov(res.samples, res.weights) # re-scale weights to have a maximum of one nweights = res.weights / np.max(res.weights) # get the probability of keeping a sample from the weights keepidx = np.where(np.random.rand(len(nweights)) < nweights)[0] # get the posterior samples samples_nestle = res.samples[keepidx, :] return res.logz
def test_eggbox(): tmax = 5.0 * np.pi constant = np.log(1.0 / tmax**2) def loglike(x): t = 2.0 * tmax * x - tmax return (2.0 + np.cos(t[0]/2.0)*np.cos(t[1]/2.0))**5.0 def prior(x): return x res = nestle.sample(loglike, prior, 2, npoints=500, method='multi', rstate=RandomState(0)) grid_logz = integrate_on_grid_refine(loglike, [(0., 1.), (0., 1.)]) print("\nEggbox") print("multi : logz = {:6.3f} +/- {:6.3f}" .format(res.logz, res.logzerr)) print(" grid_logz = {0:8.5f}".format(grid_logz)) assert abs(res.logz - grid_logz) < 3.0 * res.logzerr
def normalise_prior(self, galind, num_components, npoints=50, seed=False, method='multi'): #Setup random seeding if seed is False: rstate = np.random.RandomState() elif seed is True: rstate = np.random.RandomState(galind) else: rstate = np.random.RandomState(seed + galind) self.model._setMeasurementComponentMapping(num_components) results = nestle.sample( self._full_lnPrior, self._priorTransform, num_components * 2, method=method, npoints=npoints, rstate=rstate) #, callback=self._sampleProgressUpdate) self.prior_norm = results.logz
# import nestle import nestle from datetime import datetime print('Nestle version: {}'.format(nestle.__version__)) nlive = 1024 # number of live points method = 'multi' # use MutliNest algorithm ndims = 9 # two parameters tol = 0.1 # the stopping criterion t0 = datetime.now() res = nestle.sample(log_likelihood, prior_transform, ndims, method=method, npoints=nlive, dlogz=tol) t1 = datetime.now() # In[ ]: timeultranest = (t1 - t0) print("Time taken to run 'UltraNest' is {} seconds".format(timeultranest)) # In[ ]: logZultranest = result['logZ'] # value of logZ logZerrultranest = result[ 'logZerr'] # estimate of the statistcal uncertainty on logZ
def fit_model_with_nestle(uv_fits, model_file, components_priors, outdir=None, **nestle_kwargs): """ :param uv_fits: Path to uv-fits file with self-calibrated visibilities. :param model_file: Path to file with difmap model. :param components_priors: Components prior's ppf. Close to phase center component goes first. Iterable of dicts with keys - name of the parameter and values - (callable, args, kwargs,) where args & kwargs - additional arguments to callable. Each callable is called callable.ppf(p, *args, **kwargs). Thus callable should has ``ppf`` method. Example of prior on single component: {'flux': (scipy.stats.uniform.ppf, [0., 10.], dict(),), 'bmaj': (scipy.stats.uniform.ppf, [0, 5.], dict(),), 'e': (scipy.stats.beta.ppf, [alpha, beta], dict(),)} First key will result in calling: scipy.stats.uniform.ppf(u, 0, 10) as value from prior for ``flux`` parameter. :param outdir: (optional) Directory to output results. If ``None`` then use cwd. (default: ``None``) :param nestle_kwargs: (optional) Any arguments passed to ``nestle.sample`` function. :return Results of ``nestle.sample`` work on that model. """ if outdir is None: outdir = os.getcwd() mdl_file = model_file uv_data = UVData(uv_fits) mdl_dir, mdl_fname = os.path.split(mdl_file) comps = import_difmap_model(mdl_fname, mdl_dir) # Sort components by distance from phase center comps = sorted(comps, key=lambda x: np.sqrt(x.p[1]**2 + x.p[2]**2)) ppfs = list() labels = list() for component_prior in components_priors: for comp_name in ('flux', 'x', 'y', 'bmaj', 'e', 'bpa'): try: ppfs.append(_function_wrapper(*component_prior[comp_name])) labels.append(comp_name) except KeyError: pass for ppf in ppfs: print(ppf.args) hypercube = hypercube_partial(ppfs) # Create model mdl = Model(stokes=stokes) # Add components to model mdl.add_components(*comps) loglike = LnLikelihood(uv_data, mdl) time0 = time.time() result = nestle.sample(loglikelihood=loglike, prior_transform=hypercube, ndim=mdl.size, npoints=50, method='multi', callback=nestle.print_progress, **nestle_kwargs) print("Time spent : {}".format(time.time()-time0)) samples = nestle.resample_equal(result.samples, result.weights) # Save re-weighted samples from posterior to specified ``outdir`` # directory np.savetxt(os.path.join(outdir, 'samples.txt'), samples) fig = corner.corner(samples, show_titles=True, labels=labels, quantiles=[0.16, 0.5, 0.84], title_fmt='.3f') # Save corner plot os samples from posterior to specified ``outdir`` # directory fig.savefig(os.path.join(outdir, "corner.png"), bbox_inches='tight', dpi=200) return result
def run_multinest(self, wavelength_bins, depths, errors, fit_info, include_condensation=True, plot_best=False, **nestle_kwargs): '''Runs nested sampling to retrieve atmospheric parameters. Parameters ---------- wavelength_bins : array_like, shape (N,2) Wavelength bins, where wavelength_bins[i][0] is the start wavelength and wavelength_bins[i][1] is the end wavelength for bin i. depths : array_like, length N Measured transit depths for the specified wavelength bins errors : array_like, length N Errors on the aforementioned transit depths fit_info : :class:`.FitInfo` object Tells us what parameters to freely vary, and in what range those parameters can vary. Also sets default values for the fixed parameters. include_condensation : bool, optional When determining atmospheric abundances, whether to include condensation. plot_best : bool, optional If True, plots the best fit model with the data **nestle_kwargs : keyword arguments to pass to nestle's sample method Returns ------- result : Result object This returns the object returned by nestle.sample The object is dictionary-like and has many useful items. For example, result.samples (or alternatively, result["samples"]) are the parameter values of each sample, result.weights contains the weights, and result.logl contains the log likelihoods. result.logz is the natural logarithm of the evidence. ''' calculator = TransitDepthCalculator( include_condensation=include_condensation) calculator.change_wavelength_bins(wavelength_bins) self._validate_params(fit_info, calculator) def transform_prior(cube): new_cube = np.zeros(len(cube)) for i in range(len(cube)): new_cube[i] = fit_info._from_unit_interval(i, cube[i]) return new_cube def multinest_ln_prob(cube): return self._ln_prob(cube, calculator, fit_info, depths, errors) def callback(callback_info): print(callback_info["it"], callback_info["logz"], transform_prior(callback_info["active_u"][0])) result = nestle.sample( multinest_ln_prob, transform_prior, fit_info._get_num_fit_params(), callback=callback, method='multi', **nestle_kwargs) best_params_arr = result.samples[np.argmax(result.logl)] write_param_estimates_file( nestle.resample_equal(result.samples, result.weights), best_params_arr, np.max(result.logl), fit_info.fit_param_names) if plot_best: self._ln_prob(best_params_arr, calculator, fit_info, depths, errors, plot=True) return result
def prior_transform(x): return 7 * np.pi * x # - 5.0 x = np.arange(0, 7 * np.pi, 0.1)[np.newaxis] y = np.arange(0, 7 * np.pi, 0.1) z = np.arange(0, 3, 0.1) # plt.figure(1) # plt.imshow(Likelihood([x.T, y]), cmap='hot') # plt.show() # Run nested sampling. result = nestle.sample(Likelihood, prior_transform, 3, npoints=1000, method='multi') result.logz # log evidence result.logzerr # numerical (sampling) error on logz result.samples # array of sample parameters result.weights # array of weights associated with each sample plt.figure(2) plt.plot(result.samples[400:, 0], result.samples[400:, 2], '.') plt.xlabel('$\\theta_{1}$') plt.ylabel('$\\theta_{3}$') plt.ylim(0, 3) # # plt.hist2d(result.samples[:, 0], result.samples[:, 1], weights=result.weights, bins=20) plt.show()
def nest_lc(data, model, vparam_names, bounds, guess_amplitude_bound=False, minsnr=5., priors=None, ppfs=None, npoints=100, method='single', maxiter=None, maxcall=None, modelcov=False, rstate=None, verbose=False, **kwargs): """Run nested sampling algorithm to estimate model parameters and evidence. Parameters ---------- data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` The model to fit. vparam_names : list Model parameters to vary in the fit. bounds : `dict` Bounded range for each parameter. Bounds must be given for each parameter, with the exception of ``t0``: by default, the minimum bound is such that the latest phase of the model lines up with the earliest data point and the maximum bound is such that the earliest phase of the model lines up with the latest data point. guess_amplitude_bound : bool, optional If true, bounds for the model's amplitude parameter are determined automatically based on the data and do not need to be included in `bounds`. The lower limit is set to zero and the upper limit is 10 times the amplitude "guess" (which is based on the highest-flux data point in any band). Default is False. minsnr : float, optional Minimum signal-to-noise ratio of data points to use when guessing amplitude bound. Default is 5. priors : `dict`, optional Prior probability distribution function for each parameter. The keys should be parameter names and the values should be callables that accept a float. If a parameter is not in the dictionary, the prior defaults to a flat distribution between the bounds. ppfs : `dict`, optional Prior percent point function (inverse of the cumulative distribution function) for each parameter. If a parameter is in this dictionary, the ppf takes precedence over a prior pdf specified in ``priors``. npoints : int, optional Number of active samples to use. Increasing this value increases the accuracy (due to denser sampling) and also the time to solution. method : {'classic', 'single', 'multi'}, optional Method used to select new points. Choices are 'classic', single-ellipsoidal ('single'), multi-ellipsoidal ('multi'). Default is 'single'. maxiter : int, optional Maximum number of iterations. Iteration may stop earlier if termination condition is reached. Default is no limit. maxcall : int, optional Maximum number of likelihood evaluations. Iteration may stop earlier if termination condition is reached. Default is no limit. modelcov : bool, optional Include model covariance when calculating chisq. Default is False. rstate : `~numpy.random.RandomState`, optional RandomState instance. If not given, the global random state of the ``numpy.random`` module will be used. verbose : bool, optional Print running evidence sum on a single line. Returns ------- res : Result Attributes are: * ``niter``: total number of iterations * ``ncall``: total number of likelihood function calls * ``time``: time in seconds spent in iteration loop. * ``logz``: natural log of the Bayesian evidence Z. * ``logzerr``: estimate of uncertainty in logz (due to finite sampling) * ``h``: Bayesian information. * ``vparam_names``: list of parameter names varied. * ``samples``: 2-d `~numpy.ndarray`, shape is (nsamples, nparameters). Each row is the parameter values for a single sample. For example, ``samples[0, :]`` is the parameter values for the first sample. * ``logprior``: 1-d `~numpy.ndarray` (length=nsamples); log(prior volume) for each sample. * ``logl``: 1-d `~numpy.ndarray` (length=nsamples); log(likelihood) for each sample. * ``weights``: 1-d `~numpy.ndarray` (length=nsamples); Weight corresponding to each sample. The weight is proportional to the prior * likelihood for the sample. * ``parameters``: 1-d `~numpy.ndarray` of weighted-mean parameter values from samples (including fixed parameters). Order corresponds to ``model.param_names``. * ``covariance``: 2-d `~numpy.ndarray` of parameter covariance; indicies correspond to order of ``vparam_names``. Calculated from ``samples`` and ``weights``. * ``errors``: OrderedDict of varied parameter uncertainties. Corresponds to square root of diagonal entries in covariance matrix. * ``ndof``: Number of degrees of freedom (len(data) - len(vparam_names)). * ``bounds``: Dictionary of bounds on varied parameters (including any automatically determined bounds). estimated_model : `~sncosmo.Model` A copy of the model with parameters set to the values in ``res.parameters``. """ try: import nestle except ImportError: raise ImportError("nest_lc() requires the nestle package.") if "nobj" in kwargs: warn("The nobj keyword is deprecated and will be removed in a future " "sncosmo release. Use `npoints` instead.") npoints = kwargs.pop("nobj") # experimental parameters tied = kwargs.get("tied", None) data = photometric_data(data) data.sort_by_time() model = copy.copy(model) bounds = copy.copy(bounds) # need to copy this b/c we modify it below # Order vparam_names the same way it is ordered in the model: vparam_names = [s for s in model.param_names if s in vparam_names] # Drop data that the model doesn't cover. data = cut_bands(data, model, z_bounds=bounds.get('z', None)) if guess_amplitude_bound: if model.param_names[2] not in vparam_names: raise ValueError("Amplitude bounds guessing enabled but " "amplitude parameter {0!r} is not varied" .format(model.param_names[2])) if model.param_names[2] in bounds: raise ValueError("cannot supply bounds for parameter {0!r}" " when guess_amplitude_bound=True" .format(model.param_names[2])) # If redshift is bounded, set model redshift to midpoint of bounds # when doing the guess. if 'z' in bounds: model.set(z=sum(bounds['z']) / 2.) _, amplitude = guess_t0_and_amplitude(data, model, minsnr) bounds[model.param_names[2]] = (0., 10. * amplitude) # Find t0 bounds to use, if not explicitly given if 't0' in vparam_names and 't0' not in bounds: bounds['t0'] = t0_bounds(data, model) if ppfs is None: ppfs = {} if tied is None: tied = {} # Convert bounds/priors combinations into ppfs if bounds is not None: for key, val in six.iteritems(bounds): if key in ppfs: continue # ppfs take priority over bounds/priors a, b = val if priors is not None and key in priors: # solve ppf at discrete points and return interpolating # function x_samples = np.linspace(0., 1., 101) ppf_samples = ppf(priors[key], x_samples, a, b) f = Interp1D(0., 1., ppf_samples) else: f = Interp1D(0., 1., np.array([a, b])) ppfs[key] = f # NOTE: It is important that iparam_names is in the same order # every time, otherwise results will not be reproducible, even # with same random seed. This is because iparam_names[i] is # matched to u[i] below and u will be in a reproducible order, # so iparam_names must also be. iparam_names = [key for key in vparam_names if key in ppfs] ppflist = [ppfs[key] for key in iparam_names] npdim = len(iparam_names) # length of u ndim = len(vparam_names) # length of v # Check that all param_names either have a direct prior or are tied. for name in vparam_names: if name in iparam_names: continue if name in tied: continue raise ValueError("Must supply ppf or bounds or tied for parameter '{}'" .format(name)) def prior_transform(u): d = {} for i in range(npdim): d[iparam_names[i]] = ppflist[i](u[i]) v = np.empty(ndim, dtype=np.float) for i in range(ndim): key = vparam_names[i] if key in d: v[i] = d[key] else: v[i] = tied[key](d) return v # Indicies of the model parameters in vparam_names idx = np.array([model.param_names.index(name) for name in vparam_names]) def loglike(parameters): model.parameters[idx] = parameters return -0.5 * chisq(data, model, modelcov=modelcov) t0 = time.time() res = nestle.sample(loglike, prior_transform, ndim, npdim=npdim, npoints=npoints, method=method, maxiter=maxiter, maxcall=maxcall, rstate=rstate, callback=(nestle.print_progress if verbose else None)) elapsed = time.time() - t0 # estimate parameters and covariance from samples vparameters, cov = nestle.mean_and_cov(res.samples, res.weights) # update model parameters to estimated ones. model.set(**dict(zip(vparam_names, vparameters))) # `res` is a nestle.Result object. Collect result into a sncosmo.Result # object for consistency, and add more fields. res = Result(niter=res.niter, ncall=res.ncall, logz=res.logz, logzerr=res.logzerr, h=res.h, samples=res.samples, weights=res.weights, logvol=res.logvol, logl=res.logl, vparam_names=copy.copy(vparam_names), ndof=len(data) - len(vparam_names), bounds=bounds, time=elapsed, parameters=model.parameters.copy(), covariance=cov, errors=OrderedDict(zip(vparam_names, np.sqrt(np.diagonal(cov)))), param_dict=OrderedDict(zip(model.param_names, model.parameters))) # Deprecated result fields. depmsg = ("The `param_names` attribute is deprecated in sncosmo v1.0 " "and will be removed in a future release. " "Use `vparam_names` instead.") res.__dict__['deprecated']['param_names'] = (res.vparam_names, depmsg) depmsg = ("The `logprior` attribute is deprecated in sncosmo v1.2 " "and will be changed in a future release. " "Use `logvol` instead.") res.__dict__['deprecated']['logprior'] = (res.logvol, depmsg) return res, model
def sample(loglike_model, prior_transform_model, datafile, priorRange): """ The function runs nested sampling. The function can be run on 4 different models. The user just needs to specify the model's loglikelihood and prior. ---------- The following input parameters are required: loglike_model: function calculates likelihood. The name of the loglike_model needs to be specified. prior_transform_model: function calculates the prior. The name of the prior transformation function needs to be specified. datafile: datafile with format has been discussed in above. priorRange: an array which specifies the limits of unifrom prior for different parameters eg: priorRange =[rangeForTheta[0],rangeForTheta[1],...] ---------- Here are some example commands for running the fuction for 4 different models. model.sample (model.loglike_NFW, model.prior_transform_NFW, 'DMdataref1.txt',[10,10E10]) model.sample (model.loglike_ISO, model.prior_transform_ISO, 'DMdataref1.txt',[10,10E10]) model.sample (model.loglike_Einasto, model.prior_transform_Einasto, 'DMdataref1.txt',[10,10,10E10]) model.sample (model.loglike_GeneralizedHalo, model.prior_transform_GeneralizedHalo, 'DMdataref1.txt',[5.,10.,4.,1.5,5E8]) ----------- As shown above, different models take in different loglike_model, prior_transform_model and priorRange. For NFW model: loglike_model: it's named as 'model.loglike_NFW' prior_transform_model: it's named as 'model.prior_transform_NFW' datafile: any datafile that has the correct 4 columns format. priorRange: an arrange of 2 elements. Recommand to use [10,10e10] For ISO model: loglike_model: it's named as 'model.loglike_ISO' prior_transform_model: it's named as 'model.prior_transform_ISO' datafile: any datafile that has the correct 4 columns format. priorRange: an arrange of 2 elements. Recommand to use [10,10e10] For Einasto model: loglike_model: it's named as 'model.loglike_Einasto' prior_transform_model: it's named as 'model.prior_transform_Einasto' datafile: any datafile that has the correct 4 columns format. priorRange: an arrange of 3 elements. Recommand to use [10,10,10e10] For GeneralizedHalo model: loglike_model: it's named as 'model.loglike_GeneralizedHalo' prior_transform_model: it's named as 'model.prior_transform_GeneralizedHalo' datafile: any datafile that has the correct 4 columns format. priorRange: an arrange of 5 elements. Recommand to use [5.,10.,4.,1.5,5e8] """ data_file = io.get_data_file_path(datafile) data_x, data_xerr, data_y, data_yerr = io.load_data(data_file) #n: number of parameters, len(priorRange) n = len(priorRange) def new_loglike_model(theta): return loglike_model(theta, (data_x, data_xerr, data_y, data_yerr)) def new_prior_transform_model(theta): return prior_transform_model(theta, priorRange) result = nestle.sample(new_loglike_model, new_prior_transform_model, n) print('log evidence') print(result.logz) print('numerical (sampling) error on logz') print(result.logzerr) print('array of sample parameters') print(result.samples) print('array of weights associated with each sample') print(result.weights) return result
m, c = theta # unpack the parameters # normalisation norm = -0.5*M*LN2PI - M*LNSIGMA # chi-squared (data, sigma and x are global variables defined early on in this notebook) chisq = np.sum(((data-straight_line(x, m, c))/sigma)**2) return norm - 0.5*chisq nlive = 1024 # number of live points method = 'multi' # use MutliNest algorithm ndims = 2 # two parameters tol= 0.5 # the stopping criterion (this is the nestle default, so doesn't need to be set) res = nestle.sample(loglikelihood_nestle, prior_transform, ndims, method=method, npoints=nlive, dlogz=tol) logZnestle = res.logz # value of logZ infogainnestle = res.h # value of the information gain in nats logZerrnestle = np.sqrt(infogainnestle/nlive) # estimate of the statistcal uncertainty on logZ # output marginal likelihood print('Marginalised evidence is {} ± {}'.format(logZnestle, logZerrnestle)) # re-scale weights to have a maximum of one nweights = res.weights/np.max(res.weights) # get the probability of keeping a sample from the weights keepidx = np.where(np.random.rand(len(nweights)) < nweights)[0] # get the posterior samples
# It helps to visualize the surface in two dimensions. Here, we plot the # likelihood evaluated on a fine grid and the sample points from nested # sampling. # likelihood surface in 2-d xx, yy = np.meshgrid(np.linspace(-6., 6., 200), np.linspace(-6., 6., 200)) c1 = np.array([-3.5, 0.]) c2 = np.array([3.5, 0.]) Z = np.exp(loglike(np.dstack((xx, yy)), c1, c2)) # nested sampling result c1 = np.array([-3.5, 0.]) c2 = np.array([3.5, 0.]) f = lambda theta: loglike(theta, c1, c2) res = nestle.sample(f, prior_transform, 2, method='multi', npoints=1000, rstate=rstate) fig = plt.figure(figsize=(14., 6.)) ax = fig.add_subplot(121, projection='3d') ax.plot_surface(xx, yy, Z, rstride=1, cstride=1, linewidth=0, cmap='coolwarm') ax.set_xlim(-6., 6.) ax.set_ylim(-6., 6.) ax.set_zlim(0., 4.) ax.set_zlabel('L') ax.set_title('Likelihood evaluated on fine grid') ax = fig.add_subplot(122, projection='3d') ax.scatter(res.samples[:,0], res.samples[:, 1], np.exp(res.logl), marker='.', c=np.exp(res.logl), linewidths=(0.,), cmap='coolwarm') ax.set_xlim(-6., 6.) ax.set_ylim(-6., 6.)
yerr = 0.1 + 0.5 * np.random.rand(N) y += yerr * np.random.randn(N) # The likelihood function: def loglike(theta): return -0.5 * (np.sum((y - model(theta, x))**2 / yerr**2)) # Defines a flat prior in 0 < m < 1, 0 < b < 100: def prior_transform(theta): return np.array([1., 100.]) * theta # Run nested sampling res = nestle.sample(loglike, prior_transform, 2, method='single', npoints=1000) print(res.summary()) # weighted average and covariance: p, cov = nestle.mean_and_cov(res.samples, res.weights) print("m = {0:5.2f} +/- {1:5.2f}".format(p[0], np.sqrt(cov[0, 0]))) print("b = {0:5.2f} +/- {1:5.2f}".format(p[1], np.sqrt(cov[1, 1]))) plt.figure() plt.errorbar(x, y, yerr=yerr, capsize=0, fmt='k.', ecolor='.7') plt.plot([0., 10.], model(p, np.array([0., 10.])), c='k') plt.show() ############################################################################### # Plot samples to see the full posterior surface.
def nested_negfc_sampling(init, cube, angs, plsc, psf, fwhm, annulus_width=8, aperture_radius=1, ncomp=10, scaling=None, svd_mode='lapack', cube_ref=None, collapse='median', w=(5, 5, 200), method='single', npoints=100, dlogz=0.1, decline_factor=None, rstate=None, verbose=True): """ Runs a nested sampling algorithm in order to determine the position and the flux of the planet using the 'Negative Fake Companion' technique. The result of this procedure is a a ``nestle`` object containing the samples from the posterior distributions of each of the 3 parameters. It provides pretty good results (value plus error bars) compared to a more CPU intensive Monte Carlo approach with the affine invariant sampler (``emcee``). Parameters ---------- init: numpy ndarray or tuple of length 3 The first guess for the position and flux of the planet, respectively. It serves for generating the bounds of the log prior function (uniform in a bounded interval). cube: numpy ndarray Frame sequence of cube. angs: numpy ndarray The relative path to the parallactic angle fits image or the angs itself. plsc: float The platescale, in arcsec per pixel. psf: numpy ndarray The PSF template. It must be centered and the flux in a 1*FWHM aperture must equal 1. fwhm : float The FHWM in pixels. annulus_width: float, optional The width in pixel of the annulus on which the PCA is performed. aperture_radius: float, optional The radius of the circular aperture in FWHM. ncomp: int optional The number of principal components. scaling : {'temp-mean', 'temp-standard'} or None, optional With None, no scaling is performed on the input data before SVD. With "temp-mean" then temporal px-wise mean subtraction is done and with "temp-standard" temporal mean centering plus scaling to unit variance is done. svd_mode : {'lapack', 'randsvd', 'eigen', 'arpack'}, str optional Switch for different ways of computing the SVD and selected PCs. cube_ref: numpy ndarray, 3d, optional Reference library cube. For Reference Star Differential Imaging. collapse : {'median', 'mean', 'sum', 'trimmean', None}, str or None, optional Sets the way of collapsing the frames for producing a final image. If None then the cube of residuals is used when measuring the function of merit (instead of a single final frame). w : tuple of length 3 The size of the bounds (around the initial state ``init``) for each parameter. method : {"single", "multi", "classic"}, str optional Flavor of nested sampling. Single ellipsoid works well for the NEGFC and is the default. npoints : int optional Number of active points. At least ndim+1 (4 will produce bad results). For problems with just a few parameters (<=5) like the NEGFC, good results are obtained with 100 points (default). dlogz : Estimated remaining evidence Iterations will stop when the estimated contribution of the remaining prior volume to the total evidence falls below this threshold. Explicitly, the stopping criterion is log(z + z_est) - log(z) < dlogz where z is the current evidence from all saved samples, and z_est is the estimated contribution from the remaining volume. This option and decline_factor are mutually exclusive. If neither is specified, the default is dlogz=0.5. decline_factor : float, optional If supplied, iteration will stop when the weight (likelihood times prior volume) of newly saved samples has been declining for decline_factor * nsamples consecutive samples. A value of 1.0 seems to work pretty well. rstate : random instance, optional RandomState instance. If not given, the global random state of the numpy.random module will be used. Returns ------- res : nestle object ``Nestle`` object with the nested sampling results, including the posterior samples. Notes ----- Nested Sampling is a computational approach for integrating posterior probability in order to compare models in Bayesian statistics. It is similar to Markov Chain Monte Carlo (MCMC) in that it generates samples that can be used to estimate the posterior probability distribution. Unlike MCMC, the nature of the sampling also allows one to calculate the integral of the distribution. It also happens to be a pretty good method for robustly finding global maxima. Nestle documentation: http://kbarbary.github.io/nestle/ Convergence: http://kbarbary.github.io/nestle/stopping.html Nested sampling has no well-defined stopping point. As iterations continue, the active points sample a smaller and smaller region of prior space. This can continue indefinitely. Unlike typical MCMC methods, we don't gain any additional precision on the results by letting the algorithm run longer; the precision is determined at the outset by the number of active points. So, we want to stop iterations as soon as we think the active points are doing a pretty good job sampling the remaining prior volume - once we've converged to the highest-likelihood regions such that the likelihood is relatively flat within the remaining prior volume. Method: The trick in nested sampling is to, at each step in the algorithm, efficiently choose a new point in parameter space drawn with uniform probability from the parameter space with likelihood greater than the current likelihood constraint. The different methods all use the current set of active points as an indicator of where the target parameter space lies, but differ in how they select new points from it. "classic" is close to the method described in Skilling (2004). "single", Mukherjee, Parkinson & Liddle (2006), Determines a single ellipsoid that bounds all active points, enlarges the ellipsoid by a user-settable factor, and selects a new point at random from within the ellipsoid. "multiple", Shaw, Bridges & Hobson (2007) and Feroz, Hobson & Bridges 2009 (Multinest). In cases where the posterior is multi-modal, the single-ellipsoid method can be extremely inefficient: In such situations, there are clusters of active points on separate high-likelihood regions separated by regions of lower likelihood. Bounding all points in a single ellipsoid means that the ellipsoid includes the lower-likelihood regions we wish to avoid sampling from. The solution is to detect these clusters and bound them in separate ellipsoids. For this, we use a recursive process where we perform K-means clustering with K=2. If the resulting two ellipsoids have a significantly lower total volume than the parent ellipsoid (less than half), we accept the split and repeat the clustering and volume test on each of the two subset of points. This process continues recursively. Alternatively, if the total ellipse volume is significantly greater than expected (based on the expected density of points) this indicates that there may be more than two clusters and that K=2 was not an appropriate cluster division. We therefore still try to subdivide the clusters recursively. However, we still only accept the final split into N clusters if the total volume decrease is significant. """ def prior_transform(x): """ x:[0,1] The prior transform is dinamically created with these bound: [radius-w1:radius+w1], [theta-w2:theta+w2], [flux-w3:flux+w3] Notes ----- The prior transform function is used to specify the Bayesian prior for the problem, in a round-about way. It is a transformation from a space where variables are independently and uniformly distributed between 0 and 1 to the parameter space of interest. For independent parameters, this would be the product of the inverse cumulative distribution function (also known as the percent point function or quantile function) for each parameter. http://kbarbary.github.io/nestle/prior.html """ a1 = 2 * w[0] a2 = init[0] - w[0] b1 = 2 * w[1] b2 = init[1] - w[1] c1 = 2 * w[2] c2 = init[2] - w[2] return np.array([a1 * x[0] + a2, b1 * x[1] + b2, c1 * x[2] + c2]) def f(param): return lnlike(param=param, cube=cube, angs=angs, plsc=plsc, psf_norm=psf, fwhm=fwhm, annulus_width=annulus_width, aperture_radius=aperture_radius, initial_state=init, cube_ref=cube_ref, svd_mode=svd_mode, scaling=scaling, fmerit='sum', ncomp=ncomp, collapse=collapse) # ------------------------------------------------------------------------- if verbose: start = time_ini() if verbose: print('Prior bounds on parameters:') print('Radius [{},{}]'.format( init[0] - w[0], init[0] + w[0], )) print('Theta [{},{}]'.format(init[1] - w[1], init[1] + w[1])) print('Flux [{},{}]'.format(init[2] - w[2], init[2] + w[2])) print('\nUsing {} active points'.format(npoints)) res = nestle.sample(f, prior_transform, ndim=3, method=method, npoints=npoints, rstate=rstate, dlogz=dlogz, decline_factor=decline_factor) # if verbose: print; timing(start) if verbose: print('\nTotal running time:') timing(start) return res
def sample(self, num_components, galaxy=None, nresample=1000, seed=False, mc_map_matrix=None, npoints=150, print_interval=10, use_pymultinest=None, save_path=None, save_interval=None): """Sample the posterior for a particular number of components. Args: num_components (int): Sample the posterior defined for this number of components in the source. galaxy (int or None): Index of the galaxy to sample. If None, sample every galaxy in the photometry. Defaults to None. nresample (int): Number of non-weighted samples to draw from the weighted samples distribution from Nested Sampling. Defaults to 1000. seed (bool or int): Random seed for sampling to ensure deterministic results when ampling again. If False, do not seed. If True, seed with value derived from galaxy index. If int, seed with specific value. mc_map_matrix (None or list of tuples): If None, sample from the fully blended posterior. For a partially blended posterior, this should be a list of tuples (length = number of measurements), where each tuples contains the (zero-based) indices of the components that measurement contains. Defaults to None. npoints (int): Number of live points for the Nested Sampling algorithm. Defaults to 150. print_interval (int): Update the progress bar with number of posterior evaluations every print_interval calls. Defaults to 10. save_path (None or str): Filepath for saving the Photoz object for reloading with `Photoz.loadState`. If None, do not automatically save. If given, the Photoz object will be saved to this path after all galaxies are sampled. If save_interval is also not None, the Photoz object will be saved to this path every save_interval galaxies. Defaults to None. save_interval (None or int) If given and save_path is not None, the Photoz object will be saved to save_path every save_interval galaxies. Defaults to None. use_pymultinest (bool or None) If True, sample using the pyMultinest sampler. This requires PyMultiNest to be installed separately. If False, sample using the Nestle sampler, which is always installed when blendz is. If None, check whether pyMultinest is installed and use it if it is, otherwise use Nestle. Defaults to None. """ if use_pymultinest is None: use_pymultinest = PYMULTINEST_AVAILABLE if isinstance(num_components, int): num_components = [num_components] else: if mc_map_matrix is not None: #TODO: This is a time-saving hack to avoid dealing with multiple specifications #The solution would probably be to rethink the overall design raise ValueError( 'mc_map_matrix cannot be set when sampling multiple numbers of components in one call. Do the separate cases separately.' ) self.num_components_sampling = len(num_components) self.num_between_print = float(round(print_interval)) if galaxy is None: start = None stop = None self.num_galaxies_sampling = self.num_galaxies elif isinstance(galaxy, int): start = galaxy stop = galaxy + 1 self.num_galaxies_sampling = 1 else: raise TypeError( 'galaxy may be either None or an integer, but got {} instead'. format(type(galaxy))) with tqdm(total=self.num_galaxies_sampling, unit='galaxy') as self.pbar: self.gal_count = 1 for gal in self.photometry.iterate(start, stop): self.blend_count = 1 for nb in num_components: if seed is False: rstate = np.random.RandomState() elif seed is True: rstate = np.random.RandomState(gal.index) else: rstate = np.random.RandomState(seed + gal.index) num_param = 2 * nb self.model._setMeasurementComponentMapping(nb) self.normalise_prior(gal.index, nb) if use_pymultinest: if not os.path.exists('chains'): os.makedirs('chains') with Silence() as self.breakSilence: self.num_posterior_evals = 0 pymultinest.run(self._lnPosterior_multinest, self._priorTransform_multinest, num_param, resume=False, verbose=False, sampling_efficiency='model', n_live_points=npoints) #, #outputfiles_basename=os.path.join(blendz.CHAIN_PATH, 'chain_')) results = pymultinest.analyse.Analyzer( num_param ) #, outputfiles_basename=os.path.join(blendz.CHAIN_PATH, 'chain_')) self._samples[gal.index][ nb] = results.get_equal_weighted_posterior( )[:, :-1] self._logevd[gal.index][nb] = results.get_mode_stats( )['global evidence'] self._logevd_error[ gal.index][nb] = results.get_mode_stats( )['global evidence error'] else: results = nestle.sample( self._lnPosterior, self._priorTransform, num_param, method='multi', npoints=npoints, rstate=rstate, callback=self._sampleProgressUpdate) self._samples[gal.index][nb] = results.samples[ rstate.choice(len(results.weights), size=nresample, p=results.weights)] self._logevd[gal.index][nb] = results.logz self._logevd_error[gal.index][nb] = results.logzerr self.blend_count += 1 self.gal_count += 1 if MPI_RANK == 0: self.pbar.update() if (save_path is not None) and (save_interval is not None): if gal.index % save_interval == 0: self.saveState(save_path) if save_path is not None: self.saveState(save_path)
def sample(self): res = nestle.sample(self.logLikelihood, self.prior_transform, self.nNodes*36, method='single', npoints=1000)
def nested_negfc_sampling(init, cube, angs, plsc, psf, fwhm, annulus_width=2, aperture_radius=1, ncomp=10, scaling=None, svd_mode='lapack', cube_ref=None, collapse='median', w=(5, 5, 200), method='single', npoints=100, dlogz=0.1, decline_factor=None, rstate=None, verbose=True): """ Runs a nested sampling algorithm in order to determine the position and the flux of the planet using the 'Negative Fake Companion' technique. The result of this procedure is a a ``nestle`` object containing the samples from the posterior distributions of each of the 3 parameters. It provides pretty good results (value plus error bars) compared to a more CPU intensive Monte Carlo approach with the affine invariant sampler (``emcee``). Parameters ---------- init: array_like or tuple of length 3 The first guess for the position and flux of the planet, respectively. It serves for generating the bounds of the log prior function (uniform in a bounded interval). cube: array_like Frame sequence of cube. angs: array_like The relative path to the parallactic angle fits image or the angs itself. plsc: float The platescale, in arcsec per pixel. psf: array_like The PSF template. It must be centered and the flux in a 1*FWHM aperture must equal 1. fwhm : float The FHWM in pixels. annulus_width: float, optional The width in pixel of the annulus on which the PCA is performed. aperture_radius: float, optional The radius of the circular aperture. ncomp: int optional The number of principal components. scaling : {'temp-mean', 'temp-standard'} or None, optional With None, no scaling is performed on the input data before SVD. With "temp-mean" then temporal px-wise mean subtraction is done and with "temp-standard" temporal mean centering plus scaling to unit variance is done. svd_mode : {'lapack', 'randsvd', 'eigen', 'arpack'}, str optional Switch for different ways of computing the SVD and selected PCs. cube_ref: array_like, 3d, optional Reference library cube. For Reference Star Differential Imaging. collapse : {'median', 'mean', 'sum', 'trimmean', None}, str or None, optional Sets the way of collapsing the frames for producing a final image. If None then the cube of residuals is used when measuring the function of merit (instead of a single final frame). w : tuple of length 3 The size of the bounds (around the initial state ``init``) for each parameter. method : {"single", "multi", "classic"}, str optional Flavor of nested sampling. Single ellipsoid works well for the NEGFC and is the default. npoints : int optional Number of active points. At least ndim+1 (4 will produce bad results). For problems with just a few parameters (<=5) like the NEGFC, good results are obtained with 100 points (default). dlogz : Estimated remaining evidence Iterations will stop when the estimated contribution of the remaining prior volume to the total evidence falls below this threshold. Explicitly, the stopping criterion is log(z + z_est) - log(z) < dlogz where z is the current evidence from all saved samples, and z_est is the estimated contribution from the remaining volume. This option and decline_factor are mutually exclusive. If neither is specified, the default is dlogz=0.5. decline_factor : float, optional If supplied, iteration will stop when the weight (likelihood times prior volume) of newly saved samples has been declining for decline_factor * nsamples consecutive samples. A value of 1.0 seems to work pretty well. rstate : random instance, optional RandomState instance. If not given, the global random state of the numpy.random module will be used. Returns ------- res : nestle object ``Nestle`` object with the nested sampling results, including the posterior samples. Notes ----- Nested Sampling is a computational approach for integrating posterior probability in order to compare models in Bayesian statistics. It is similar to Markov Chain Monte Carlo (MCMC) in that it generates samples that can be used to estimate the posterior probability distribution. Unlike MCMC, the nature of the sampling also allows one to calculate the integral of the distribution. It also happens to be a pretty good method for robustly finding global maxima. Nestle documentation: http://kbarbary.github.io/nestle/ Convergence: http://kbarbary.github.io/nestle/stopping.html Nested sampling has no well-defined stopping point. As iterations continue, the active points sample a smaller and smaller region of prior space. This can continue indefinitely. Unlike typical MCMC methods, we don't gain any additional precision on the results by letting the algorithm run longer; the precision is determined at the outset by the number of active points. So, we want to stop iterations as soon as we think the active points are doing a pretty good job sampling the remaining prior volume - once we've converged to the highest-likelihood regions such that the likelihood is relatively flat within the remaining prior volume. Method: The trick in nested sampling is to, at each step in the algorithm, efficiently choose a new point in parameter space drawn with uniform probability from the parameter space with likelihood greater than the current likelihood constraint. The different methods all use the current set of active points as an indicator of where the target parameter space lies, but differ in how they select new points from it. "classic" is close to the method described in Skilling (2004). "single", Mukherjee, Parkinson & Liddle (2006), Determines a single ellipsoid that bounds all active points, enlarges the ellipsoid by a user-settable factor, and selects a new point at random from within the ellipsoid. "multiple", Shaw, Bridges & Hobson (2007) and Feroz, Hobson & Bridges 2009 (Multinest). In cases where the posterior is multi-modal, the single-ellipsoid method can be extremely inefficient: In such situations, there are clusters of active points on separate high-likelihood regions separated by regions of lower likelihood. Bounding all points in a single ellipsoid means that the ellipsoid includes the lower-likelihood regions we wish to avoid sampling from. The solution is to detect these clusters and bound them in separate ellipsoids. For this, we use a recursive process where we perform K-means clustering with K=2. If the resulting two ellipsoids have a significantly lower total volume than the parent ellipsoid (less than half), we accept the split and repeat the clustering and volume test on each of the two subset of points. This process continues recursively. Alternatively, if the total ellipse volume is significantly greater than expected (based on the expected density of points) this indicates that there may be more than two clusters and that K=2 was not an appropriate cluster division. We therefore still try to subdivide the clusters recursively. However, we still only accept the final split into N clusters if the total volume decrease is significant. """ def prior_transform(x): """ x:[0,1] The prior transform is dinamically created with these bound: [radius-w1:radius+w1], [theta-w2:theta+w2], [flux-w3:flux+w3] Notes ----- The prior transform function is used to specify the Bayesian prior for the problem, in a round-about way. It is a transformation from a space where variables are independently and uniformly distributed between 0 and 1 to the parameter space of interest. For independent parameters, this would be the product of the inverse cumulative distribution function (also known as the percent point function or quantile function) for each parameter. http://kbarbary.github.io/nestle/prior.html """ a1 = 2 * w[0] a2 = init[0] - w[0] b1 = 2 * w[1] b2 = init[1] - w[1] c1 = 2 * w[2] c2 = init[2] - w[2] return np.array([a1 * x[0] + a2, b1 * x[1] + b2, c1 * x[2] + c2]) def f(param): return lnlike(param=param, cube=cube, angs=angs, plsc=plsc, psf_norm=psf, fwhm=fwhm, annulus_width=annulus_width, aperture_radius=aperture_radius, initial_state=init, cube_ref=cube_ref, svd_mode=svd_mode, scaling=scaling, fmerit='sum', ncomp=ncomp, collapse=collapse) # ------------------------------------------------------------------------- if verbose: start = time_ini() if verbose: print('Prior bounds on parameters:') print('Radius [{},{}]'.format(init[0] - w[0], init[0] + w[0], )) print('Theta [{},{}]'.format(init[1] - w[1], init[1] + w[1])) print('Flux [{},{}]'.format(init[2] - w[2], init[2] + w[2])) print('\nUsing {} active points'.format(npoints)) res = nestle.sample(f, prior_transform, ndim=3, method=method, npoints=npoints, rstate=rstate, dlogz=dlogz, decline_factor=decline_factor) # if verbose: print; timing(start) if verbose: print('\nTotal running time:') timing(start) return res
def run_nestle(self,samplertype=None,npoints=None,restart=None,weightrestart=True,maxrejcall=None): if samplertype == None: samplertype = 'multi' if npoints == None: npoints = 100 if restart == None: # generate initial random sample within Nestle volume modind = np.array(range(0,len(self.MODPARS)),dtype=int) selind = modind[np.random.choice(len(modind),npoints,replace=False)] if ('Tycho_V' in self.bfpar.keys()) & ('Tycho_B' in self.bfpar.keys()): cond = ( (self.PHOT['Tycho_V']+self.DM(self.mindist+0.5*self.distran) > self.bfpar['Tycho_V']-1.0) & (self.PHOT['Tycho_V']+self.DM(self.mindist+0.5*self.distran) < self.bfpar['Tycho_V'] + 1.0) & (self.PHOT['Tycho_B']+self.DM(self.mindist+0.5*self.distran) > self.bfpar['Tycho_B']-1.0) & (self.PHOT['Tycho_B']+self.DM(self.mindist+0.5*self.distran) < self.bfpar['Tycho_B'] + 1.0) ) addind = modind[cond][np.random.choice(len(modind[cond]),int(npoints*0.25),replace=False)] finind = np.hstack([selind,addind]) finind = np.unique(finind) else: finind = selind initsample = self.MODPARS[finind] initsample_v = np.empty((len(initsample), self.ndim), dtype=np.float64) initsample_u = np.empty((len(initsample), self.ndim), dtype=np.float64) for i in range(len(initsample)): initsample_v_i = [float(initsample['EEP'][i]),10.0**(float(initsample['log_age'][i])-9.0),float(initsample['[Fe/H]in'][i])] if self.fitphotbool: # initsample_v_i.append(self.distran*np.random.rand()+self.mindist) # initsample_v_i.append(self.Avran*np.random.rand()+self.minAv) if 'Para' in self.priordict.keys(): distmean = 1000.0/self.priordict['Para'][0] parashift = self.priordict['Para'][0]-3.0*self.priordict['Para'][1] distsig = (1000.0/parashift)-distmean initsample_v_i.append(distsig*np.random.randn()+distmean) else: initsample_v_i.append(self.distran*np.random.rand()+self.mindist) initsample_v_i.append(self.Avran*np.random.rand()+self.minAv) initsample_u_i = self.prior_inversetrans(initsample_v_i) initsample_v[i,:] = initsample_v_i initsample_u[i,:] = initsample_u_i else: restart_from = Table.read(restart,format='ascii') if len(restart_from) > npoints: if weightrestart: restart_ind = np.random.choice(range(0,len(restart_from)),npoints,replace=False,p=np.exp(restart_from['logwt']-restart_from['logz'][-1])) else: restart_ind = np.random.choice(range(0,len(restart_from)),npoints,replace=False) restart_sel = restart_from[restart_ind] addind = np.random.choice(len(self.MODPARS),int(0.25*npoints),replace=False) addsel = self.MODPARS[addind] else: restart_sel = restart_from numbadd = 1.25*npoints-len(restart_sel) addind = np.random.choice(len(self.MODPARS),numbadd,replace=False) addsel = self.MODPARS[addind] initsample_v = np.empty((len(restart_sel)+len(addsel),self.ndim), dtype=np.float64) initsample_u = np.empty((len(restart_sel)+len(addsel),self.ndim), dtype=np.float64) for i in range(len(restart_sel)): initsample_v_i = [float(restart_sel['EEP'][i]),float(restart_sel['Age'][i]),float(restart_sel['[Fe/H]in'][i])] if self.fitphotbool: initsample_v_i.append(float(restart_sel['Dist'][i])) initsample_v_i.append(float(restart_sel['Av'][i])) initsample_u_i = self.prior_inversetrans(initsample_v_i) initsample_v[i,:] = initsample_v_i initsample_u[i,:] = initsample_u_i for i in range(len(addsel)): initsample_v_i = [float(addsel['EEP'][i]),10.0**(float(addsel['log_age'][i])-9.0),float(addsel['[Fe/H]in'][i])] if self.fitphotbool: # initsample_v_i.append(self.distran*np.random.rand()+self.mindist) # initsample_v_i.append(self.Avran*np.random.rand()+self.minAv) distmean = 1000.0/self.priordict['Para'][0] parashift = self.priordict['Para'][0]-3.0*self.priordict['Para'][1] distsig = (1000.0/parashift)-distmean initsample_v_i.append(distsig*np.random.randn()+distmean) initsample_v_i.append(self.Avran*np.random.rand()+self.minAv) initsample_u_i = self.prior_inversetrans(initsample_v_i) initsample_v[i+len(restart_sel),:] = initsample_v_i initsample_u[i+len(restart_sel),:] = initsample_u_i print 'Start Nestle w/ {0} number of samples'.format(len(initsample_v)) self.startmct = datetime.now() self.stept = datetime.now() self.ncallt = 0 self.maxcallnum = 0 sys.stdout.flush() result = nestle.sample( self.lnp_call_nestle,self.prior_trans,self.ndim,method=samplertype, npoints=len(initsample_v),callback=self.nestle_callback,user_sample=initsample_u, # dlogz=1.0, # update_interval=1, maxrejcall=maxrejcall, ) p,cov = nestle.mean_and_cov(result.samples,result.weights) return result,p,cov
t = 2.0 * tmax * x - tmax return (2.0 + np.cos(t[0]/2.0)*np.cos(t[1]/2.0))**5.0 def prior(x): return x # plot the surface plt.figure(figsize=(8., 8.)) ax = plt.axes(aspect=1) xx, yy = np.meshgrid(np.linspace(0., 1., 50), np.linspace(0., 1., 50)) Z = loglike(np.array([xx, yy])) ax.contourf(xx, yy, Z, 12, cmap=plt.cm.Blues_r) plt.title("True Log likelihood surface") ############################################################################### # Run nested sampling in multi-ellipsoid mode and print a summary of results: res = nestle.sample(loglike, prior, 2, npoints=200, method='multi', update_interval=20) print(res.summary()) ############################################################################### # Plot the samples. Note that this represents the *likelihood* rather than # its log, hence it is much more highly peaked. fig = corner.corner(res.samples, weights=res.weights, bins=500, range=[(0., 1.), (0., 1.)]) fig.set_size_inches(8., 8.)
yerr = 0.1+0.5*np.random.rand(N) y += yerr * np.random.randn(N) # The likelihood function: def loglike(theta): return -0.5*(np.sum((y-model(theta, x))**2/yerr**2)) # Defines a flat prior in 0 < m < 1, 0 < b < 100: def prior_transform(theta): return np.array([1., 100.]) * theta # Run nested sampling res = nestle.sample(loglike, prior_transform, 2, method='single', npoints=1000) print(res.summary()) # weighted average and covariance: p, cov = nestle.mean_and_cov(res.samples, res.weights) print("m = {0:5.2f} +/- {1:5.2f}".format(p[0], np.sqrt(cov[0, 0]))) print("b = {0:5.2f} +/- {1:5.2f}".format(p[1], np.sqrt(cov[1, 1]))) plt.figure() plt.errorbar(x, y, yerr=yerr, capsize=0, fmt='k.', ecolor='.7') plt.plot([0., 10.], model(p, np.array([0., 10.])), c='k') plt.show() ############################################################################### # Plot samples to see the full posterior surface.
def run_nestle(**config): ndim = config['ndim'] def priortransform(u): assert len(u) == ndim, u return u def dump_callback(info): sys.stderr.write("\r%d|%d|logz=%.4f|eff=%f%% " % (info['it'], info['ncall'], info['logz'], info['it'] * 100. / info['ncall'])) #if info['it'] % 50 != 0: return #print "Replacements: %d" % (info['ncall']) #print "Samples: %d" % (info['it']) #print "Efficiency: %f" % (info['ncall']/info['it']) #print "Nested Sampling ln(Z): %f" % (info['logz']) if 'seed' in config: numpy.random.seed(config['seed']) # can use directly loglikelihood = config['loglikelihood'] nlive_points = config['nlive_points'] method = config['method'] if config.get('unlimited_sampling', False): max_samples = None else: max_samples = 2000000 print() print('running nestle ...') options = dict() #if 'enlarge' in config: # options['enlarge'] = config['enlarge'] starttime = time.time() result = nestle.sample(loglikelihood=loglikelihood, prior_transform=priortransform, ndim=ndim, npoints=nlive_points, method=method, update_interval=None, maxcall=max_samples, dlogz=0.5, rstate=numpy.random, callback=dump_callback, **options) endtime = time.time() output_basename = config['output_basename'] print() print('nestle done lnZ = %(logz).1f +- %(logzerr).1f' % (result)) if config.get('seed', 0) == 0: import matplotlib.pyplot as plt x = result['samples'] y = exp(result['logl']) plt.plot(x[:, 0], y, 'x', color='blue', ms=1) plt.savefig(output_basename + 'nested_samples.pdf', bbox_inches='tight') plt.close() L = result['logl'] width = result['weights'] plt.plot(width, L, 'x-', color='blue', ms=1, label='Z=%.2f (%.2f)' % (result['logz'], log(exp(L + width).sum()))) fromleft = exp(L + width)[::-1].cumsum() fromleft /= fromleft.max() mask = (fromleft < 0.99)[::-1] if mask.any(): i = width[mask].argmax() plt.ylim(L.max() - log(1000), L.max()) plt.fill_between(width[mask], L[mask], L.max() - log(1000), color='grey', alpha=0.3) plt.xlabel('prior mass') plt.ylabel('likelihood') plt.legend(loc='best') plt.savefig(output_basename + 'nested_integral.pdf', bbox_inches='tight') plt.close() #posterioru, posteriorx = equal_weighted_posterior(result['weights']) #plt.figure(figsize=(ndim*2, ndim*2)) #marginal_plots(weights=result['weights'], ndim=ndim) #plt.savefig(output_basename + 'posterior.pdf', bbox_inches='tight') #plt.close() return dict( Z_computed=float(result['logz']), Z_computed_err=float(result['logzerr']), niterations=result['niter'], duration=endtime - starttime, )
def nest_lc(data, model, vparam_names, bounds, guess_amplitude_bound=False, minsnr=5., priors=None, ppfs=None, npoints=100, method='single', maxiter=None, maxcall=None, modelcov=False, rstate=None, verbose=False, warn=True, **kwargs): """Run nested sampling algorithm to estimate model parameters and evidence. Parameters ---------- data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` The model to fit. vparam_names : list Model parameters to vary in the fit. bounds : `dict` Bounded range for each parameter. Bounds must be given for each parameter, with the exception of ``t0``: by default, the minimum bound is such that the latest phase of the model lines up with the earliest data point and the maximum bound is such that the earliest phase of the model lines up with the latest data point. guess_amplitude_bound : bool, optional If true, bounds for the model's amplitude parameter are determined automatically based on the data and do not need to be included in `bounds`. The lower limit is set to zero and the upper limit is 10 times the amplitude "guess" (which is based on the highest-flux data point in any band). Default is False. minsnr : float, optional Minimum signal-to-noise ratio of data points to use when guessing amplitude bound. Default is 5. priors : `dict`, optional Prior probability distribution function for each parameter. The keys should be parameter names and the values should be callables that accept a float. If a parameter is not in the dictionary, the prior defaults to a flat distribution between the bounds. ppfs : `dict`, optional Prior percent point function (inverse of the cumulative distribution function) for each parameter. If a parameter is in this dictionary, the ppf takes precedence over a prior pdf specified in ``priors``. npoints : int, optional Number of active samples to use. Increasing this value increases the accuracy (due to denser sampling) and also the time to solution. method : {'classic', 'single', 'multi'}, optional Method used to select new points. Choices are 'classic', single-ellipsoidal ('single'), multi-ellipsoidal ('multi'). Default is 'single'. maxiter : int, optional Maximum number of iterations. Iteration may stop earlier if termination condition is reached. Default is no limit. maxcall : int, optional Maximum number of likelihood evaluations. Iteration may stop earlier if termination condition is reached. Default is no limit. modelcov : bool, optional Include model covariance when calculating chisq. Default is False. rstate : `~numpy.random.RandomState`, optional RandomState instance. If not given, the global random state of the ``numpy.random`` module will be used. verbose : bool, optional Print running evidence sum on a single line. warn : bool, optional Issue warning when dropping bands outside the model range. Default is True. *New in version 1.5.0* Returns ------- res : Result Attributes are: * ``niter``: total number of iterations * ``ncall``: total number of likelihood function calls * ``time``: time in seconds spent in iteration loop. * ``logz``: natural log of the Bayesian evidence Z. * ``logzerr``: estimate of uncertainty in logz (due to finite sampling) * ``h``: Bayesian information. * ``vparam_names``: list of parameter names varied. * ``samples``: 2-d `~numpy.ndarray`, shape is (nsamples, nparameters). Each row is the parameter values for a single sample. For example, ``samples[0, :]`` is the parameter values for the first sample. * ``logprior``: 1-d `~numpy.ndarray` (length=nsamples); log(prior volume) for each sample. * ``logl``: 1-d `~numpy.ndarray` (length=nsamples); log(likelihood) for each sample. * ``weights``: 1-d `~numpy.ndarray` (length=nsamples); Weight corresponding to each sample. The weight is proportional to the prior * likelihood for the sample. * ``parameters``: 1-d `~numpy.ndarray` of weighted-mean parameter values from samples (including fixed parameters). Order corresponds to ``model.param_names``. * ``covariance``: 2-d `~numpy.ndarray` of parameter covariance; indicies correspond to order of ``vparam_names``. Calculated from ``samples`` and ``weights``. * ``errors``: OrderedDict of varied parameter uncertainties. Corresponds to square root of diagonal entries in covariance matrix. * ``ndof``: Number of degrees of freedom (len(data) - len(vparam_names)). * ``bounds``: Dictionary of bounds on varied parameters (including any automatically determined bounds). * ``data_mask``: Boolean array the same length as data specifying whether each observation was used. *New in version 1.5.0.* estimated_model : `~sncosmo.Model` A copy of the model with parameters set to the values in ``res.parameters``. """ try: import nestle except ImportError: raise ImportError("nest_lc() requires the nestle package.") # warnings if "nobj" in kwargs: warnings.warn("The nobj keyword is deprecated and will be removed in " "sncosmo v2.0. Use `npoints` instead.") npoints = kwargs.pop("nobj") # experimental parameters tied = kwargs.get("tied", None) data = photometric_data(data) # sort by time if not np.all(np.ediff1d(data.time) >= 0.0): sortidx = np.argsort(data.time) data = data[sortidx] else: sortidx = None model = copy.copy(model) bounds = copy.copy(bounds) # need to copy this b/c we modify it below # Order vparam_names the same way it is ordered in the model: vparam_names = [s for s in model.param_names if s in vparam_names] # Drop data that the model doesn't cover. fitdata, data_mask = cut_bands(data, model, z_bounds=bounds.get('z', None), warn=warn) if guess_amplitude_bound: if model.param_names[2] not in vparam_names: raise ValueError("Amplitude bounds guessing enabled but " "amplitude parameter {0!r} is not varied".format( model.param_names[2])) if model.param_names[2] in bounds: raise ValueError("cannot supply bounds for parameter {0!r}" " when guess_amplitude_bound=True".format( model.param_names[2])) # If redshift is bounded, set model redshift to midpoint of bounds # when doing the guess. if 'z' in bounds: model.set(z=sum(bounds['z']) / 2.) _, amplitude = guess_t0_and_amplitude(fitdata, model, minsnr) bounds[model.param_names[2]] = (0., 10. * amplitude) # Find t0 bounds to use, if not explicitly given if 't0' in vparam_names and 't0' not in bounds: bounds['t0'] = t0_bounds(fitdata, model) if ppfs is None: ppfs = {} if tied is None: tied = {} # Convert bounds/priors combinations into ppfs if bounds is not None: for key, val in six.iteritems(bounds): if key in ppfs: continue # ppfs take priority over bounds/priors a, b = val if priors is not None and key in priors: # solve ppf at discrete points and return interpolating # function x_samples = np.linspace(0., 1., 101) ppf_samples = ppf(priors[key], x_samples, a, b) f = Interp1D(0., 1., ppf_samples) else: f = Interp1D(0., 1., np.array([a, b])) ppfs[key] = f # NOTE: It is important that iparam_names is in the same order # every time, otherwise results will not be reproducible, even # with same random seed. This is because iparam_names[i] is # matched to u[i] below and u will be in a reproducible order, # so iparam_names must also be. iparam_names = [key for key in vparam_names if key in ppfs] ppflist = [ppfs[key] for key in iparam_names] npdim = len(iparam_names) # length of u ndim = len(vparam_names) # length of v # Check that all param_names either have a direct prior or are tied. for name in vparam_names: if name in iparam_names: continue if name in tied: continue raise ValueError( "Must supply ppf or bounds or tied for parameter '{}'".format( name)) def prior_transform(u): d = {} for i in range(npdim): d[iparam_names[i]] = ppflist[i](u[i]) v = np.empty(ndim, dtype=np.float) for i in range(ndim): key = vparam_names[i] if key in d: v[i] = d[key] else: v[i] = tied[key](d) return v # Indicies of the model parameters in vparam_names idx = np.array([model.param_names.index(name) for name in vparam_names]) def loglike(parameters): model.parameters[idx] = parameters return -0.5 * chisq(fitdata, model, modelcov=modelcov) t0 = time.time() res = nestle.sample(loglike, prior_transform, ndim, npdim=npdim, npoints=npoints, method=method, maxiter=maxiter, maxcall=maxcall, rstate=rstate, callback=(nestle.print_progress if verbose else None)) elapsed = time.time() - t0 # estimate parameters and covariance from samples vparameters, cov = nestle.mean_and_cov(res.samples, res.weights) # update model parameters to estimated ones. model.set(**dict(zip(vparam_names, vparameters))) # If we need to, unsort the mask so mask applies to input data if sortidx is not None: unsort_idx = np.argsort(sortidx) # indicies that will unsort array data_mask = data_mask[unsort_idx] # `res` is a nestle.Result object. Collect result into a sncosmo.Result # object for consistency, and add more fields. res = Result( niter=res.niter, ncall=res.ncall, logz=res.logz, logzerr=res.logzerr, h=res.h, samples=res.samples, weights=res.weights, logvol=res.logvol, logl=res.logl, vparam_names=copy.copy(vparam_names), ndof=len(fitdata) - len(vparam_names), bounds=bounds, time=elapsed, parameters=model.parameters.copy(), covariance=cov, errors=OrderedDict(zip(vparam_names, np.sqrt(np.diagonal(cov)))), param_dict=OrderedDict(zip(model.param_names, model.parameters)), data_mask=data_mask) # Deprecated result fields. depmsg = ("The `param_names` attribute is deprecated in sncosmo v1.0 " "and will be removed in sncosmo v2.0." "Use `vparam_names` instead.") res.__dict__['deprecated']['param_names'] = (res.vparam_names, depmsg) depmsg = ("The `logprior` attribute is deprecated in sncosmo v1.2 " "and will be changed in sncosmo v2.0." "Use `logvol` instead.") res.__dict__['deprecated']['logprior'] = (res.logvol, depmsg) return res, model