From f1f52edbd5b8a9593342864dfd1d0f25ac0c8c49 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 19 May 2022 17:15:31 +0100 Subject: [PATCH 01/87] neighbor spelling (assuming we're sticking to American spelling!) --- src/polychord/clustering.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/polychord/clustering.f90 b/src/polychord/clustering.f90 index 9c49eb8c..4e805812 100644 --- a/src/polychord/clustering.f90 +++ b/src/polychord/clustering.f90 @@ -10,7 +10,7 @@ module KNN_clustering !! Points belong to the same cluster if they are in either of each others k !! nearest neighbor sets. !! - !! The algorithm computes the k nearest neihbor sets from the similarity + !! The algorithm computes the k nearest neighbor sets from the similarity !! matrix, and then tests recursive function NN_clustering(similarity_matrix,num_clusters) result(cluster_list) use utils_module, only: relabel @@ -113,7 +113,7 @@ function do_clustering_k(knn) result(c) ! If they're not in the same cluster already... if(c(i)/=c(j)) then - ! ... check to see if they are within each others k nearest neihbors... + ! ... check to see if they are within each others k nearest neighbors... if( neighbors( knn(:,i),knn(:,j) ) ) then ! If they are then relabel cluster_i and cluster_j to the smaller of the two From 6efc40cef2437caab073b0e48baa80a4c2828a27 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 18:52:03 +0100 Subject: [PATCH 02/87] updated run_poypolychord to use **kwargs rather than PolyChordSettings --- pypolychord/polychord.py | 151 ++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 580fba94..6e515bb4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -12,8 +12,8 @@ def default_dumper(live, dead, logweights, logZ, logZerr): pass -def run_polychord(loglikelihood, nDims, nDerived, settings, - prior=default_prior, dumper=default_dumper): +def run_pypolychord(loglikelihood, nDims, nDerived, + prior=default_prior, dumper=default_dumper, **kwargs): """ Runs PolyChord. @@ -56,12 +56,6 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, logL: float log-likelihood - nDims: int - Dimensionality of the model, i.e. the number of physical parameters. - - nDerived: int - The number of derived parameters (can be 0). - settings: settings.Settings Object containing polychord settings @@ -143,6 +137,9 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, .stats Final output evidence statistics + + + """ try: @@ -152,22 +149,25 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, except ImportError: rank = 0 + kwargs.setdefault("base_dir", "chains") + kwargs.setdefault("cluster_dir", "test") + kwargs.setdefault("read_resume", True) try: if rank == 0: - os.makedirs(settings.base_dir) + os.makedirs(kwargs["base_dir"]) except OSError: pass try: if rank == 0: - os.makedirs(settings.cluster_dir) + os.makedirs(os.path.join(kwargs["base_dir"], kwargs["cluster_dir"])) except OSError: pass - if settings.cube_samples is not None: - make_resume_file(settings, loglikelihood, prior) - read_resume = settings.read_resume - settings.read_resume=True + if "cube_samples" in kwargs: + make_resume_file(loglikelihood, prior, **kwargs) + read_resume = kwargs["read_resume"] + kwargs["read_resume"] = True def wrap_loglikelihood(theta, phi): logL, phi[:] = loglikelihood(theta) @@ -176,55 +176,58 @@ def wrap_loglikelihood(theta, phi): def wrap_prior(cube, theta): theta[:] = prior(cube) - settings.grade_dims = [int(d) for d in settings.grade_dims] - settings.nlives = {float(logL):int(nlive) for logL, nlive in settings.nlives.items()} + kwargs["grade_dims"] = [int(d) for d in kwargs.pop("grade_dims", [nDims])] + kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs.pop("nlives", {}).items()} # Run polychord from module library _pypolychord.run(wrap_loglikelihood, - wrap_prior, - dumper, - nDims, - nDerived, - settings.nlive, - settings.num_repeats, - settings.nprior, - settings.nfail, - settings.do_clustering, - settings.feedback, - settings.precision_criterion, - settings.logzero, - settings.max_ndead, - settings.boost_posterior, - settings.posteriors, - settings.equals, - settings.cluster_posteriors, - settings.write_resume, - settings.write_paramnames, - settings.read_resume, - settings.write_stats, - settings.write_live, - settings.write_dead, - settings.write_prior, - settings.maximise, - settings.compression_factor, - settings.synchronous, - settings.base_dir, - settings.file_root, - settings.grade_frac, - settings.grade_dims, - settings.nlives, - settings.seed) - - if settings.cube_samples is not None: - settings.read_resume = read_resume - - return PolyChordOutput(settings.base_dir, settings.file_root) - - -def make_resume_file(settings, loglikelihood, prior): + wrap_prior, + dumper, + nDims, + nDerived, + kwargs.pop('nlive', nDims*25), + kwargs.pop('num_repeats', nDims*5), + kwargs.pop('nprior', -1), + kwargs.pop('nfail', -1), + kwargs.pop('do_clustering', True), + kwargs.pop('feedback', 1), + kwargs.pop('precision_criterion', 0.001), + kwargs.pop('logzero', -1e30), + kwargs.pop('max_ndead', -1), + kwargs.pop('boost_posterior', 0.0), + kwargs.pop('posteriors', True), + kwargs.pop('equals', True), + kwargs.pop('cluster_posteriors', True), + kwargs.pop('write_resume', True), + kwargs.pop('write_paramnames', False), + kwargs.pop('read_resume', True), + kwargs.pop('write_stats', True), + kwargs.pop('write_live', True), + kwargs.pop('write_dead', True), + kwargs.pop('write_prior', True), + kwargs.pop('maximise', False), + kwargs.pop('compression_factor', np.exp(-1)), + kwargs.pop('synchronous', True), + kwargs.pop('base_dir', 'chains'), + kwargs.pop('file_root', 'test'), + kwargs.pop('seed', -1), + kwargs.pop("grade_frac", [1.0]*len(kwargs["grade_dims"])), + kwargs["grade_dims"], + kwargs["nlives"], + kwargs.pop("seed", -1), + ) + + if "cube_samples" in kwargs: + kwargs["read_resume"] = read_resume + + return PolyChordOutput(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + + + +def make_resume_file(loglikelihood, prior, **kwargs): import fortranformat as ff - resume_filename = os.path.join(settings.base_dir, - settings.file_root)+".resume" + resume_filename = os.path.join(kwargs["base_dir"], + kwargs["file_root"])+".resume" try: from mpi4py import MPI @@ -236,9 +239,9 @@ def make_resume_file(settings, loglikelihood, prior): size = 1 lives = [] - logL_birth = settings.logzero - for i in np.array_split(np.arange(len(settings.cube_samples)), size)[rank]: - cube = settings.cube_samples[i] + logL_birth = kwargs["logzero"] + for i in np.array_split(np.arange(len(kwargs["cube_samples"])), size)[rank]: + cube = kwargs["cube_samples"][i] theta = prior(cube) logL, derived = loglikelihood(theta) nDims = len(theta) @@ -254,7 +257,7 @@ def make_resume_file(settings, loglikelihood, prior): recvbuf = None comm.Gatherv(sendbuf=sendbuf, recvbuf=(recvbuf, sendcounts), root=root) - lives = np.reshape(sendbuf, (len(settings.cube_samples), len(lives[0]))) + lives = np.reshape(sendbuf, (len(kwargs["cube_samples"]), len(lives[0]))) except NameError: lives = np.array(lives) @@ -286,11 +289,11 @@ def write(var): write('=== Number of global equally weighted posterior points ===') write(0) write('=== Number of grades ===') - write(len(settings.grade_dims)) + write(len(kwargs["grade_dims"])) write('=== positions of grades ===') - write(settings.grade_dims) + write(kwargs["grade_dims"]) write('=== Number of repeats ===') - write(settings.num_repeats) + write(kwargs["num_repeats"]) write('=== Number of likelihood calls ===') write(len(lives)) write('=== Number of live points in each cluster ===') @@ -306,11 +309,11 @@ def write(var): write('=== Number of weighted posterior points in each dead cluster ===') write('=== Number of equally weighted posterior points in each dead cluster ===') write('=== global evidence -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== global evidence^2 -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== posterior thin factor ===') - write(settings.boost_posterior) + write(kwargs["boost_posterior"]) write('=== local loglikelihood bounds ===') write(lives[:,-1].min()) write('=== local volume -- log() ===') @@ -318,17 +321,17 @@ def write(var): write('=== last update volume ===') write(0.0) write('=== global evidence volume cross correlation -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== local evidence -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== local evidence^2 -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== local evidence volume cross correlation -- log() ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== local volume cross correlation -- log() ===') write(0.0) write('=== maximum log weights -- log(w_p) ===') - write(settings.logzero) + write(kwargs["logzero"]) write('=== local dead evidence -- log() ===') write('=== local dead evidence^2 -- log() ===') write('=== maximum dead log weights -- log(w_p) ===') From df7b3007b06d190165d8ad44839f4b6248fa38cb Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 18:52:30 +0100 Subject: [PATCH 03/87] return anesthetic.NestedSamples output if installed --- pypolychord/polychord.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 6e515bb4..706a3ef2 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -219,8 +219,11 @@ def wrap_prior(cube, theta): if "cube_samples" in kwargs: kwargs["read_resume"] = read_resume - - return PolyChordOutput(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + try: + import anesthetic + return anesthetic.NestedSamples(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + except ImportError: + return PolyChordOutput(os.path.join(kwargs["base_dir"], kwargs["file_root"])) From 3f21a7bf9be9e6ab5685fa37a2823dae28d0c24c Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 18:55:10 +0100 Subject: [PATCH 04/87] added kwargs to run_pypolychord() docstring --- pypolychord/polychord.py | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 706a3ef2..7a061dc3 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -55,6 +55,13 @@ def run_pypolychord(loglikelihood, nDims, nDerived, ------- logL: float log-likelihood + TODO: what about []? + + nDims: int + Dimensionality of the model, i.e. the number of physical parameters. + + nDerived: int + The number of derived parameters (can be 0). settings: settings.Settings Object containing polychord settings @@ -95,6 +102,160 @@ def run_pypolychord(loglikelihood, nDims, nDerived, The current log-evidence estimate logZerr: float The current log-evidence error estimate + + Keyword arguments + ----------------- + nlive: int + (Default: nDims*25) + The number of live points. + Increasing nlive increases the accuracy of posteriors and evidences, + and proportionally increases runtime ~ O(nlive). + + num_repeats : int + (Default: nDims*5) + The number of slice slice-sampling steps to generate a new point. + Increasing num_repeats increases the reliability of the algorithm. + Typically + * for reliable evidences need num_repeats ~ O(5*nDims). + * for reliable posteriors need num_repeats ~ O(nDims) + + nprior : int + (Default: nlive) + The number of prior samples to draw before starting compression. + + nfail : int + (Default: nlive) + The number of failed spawns before stopping nested sampling. + + do_clustering : boolean + (Default: True) + Whether or not to use clustering at run time. + + feedback : {0,1,2,3} + (Default: 1) + How much command line feedback to give + + precision_criterion : float + (Default: 0.001) + Termination criterion. Nested sampling terminates when the evidence + contained in the live points is precision_criterion fraction of the + total evidence. + + logzero : float + (Default: -1e30) + The loglikelihood value at which PolyChord considers points + 'unphysical', and excludes them at the prior level. + + max_ndead : int + (Default: -1) + Alternative termination criterion. Stop after max_ndead iterations. + Set negative to ignore (default). + + boost_posterior : float + (Default: 0.0) + Increase the number of posterior samples produced. This can be set + arbitrarily high, but you won't be able to boost by more than + num_repeats + Warning: in high dimensions PolyChord produces _a lot_ of posterior + samples. You probably don't need to change this + + posteriors : boolean + (Default: True) + Produce (weighted) posterior samples. Stored in .txt. + + equals : boolean + (Default: True) + Produce (equally weighted) posterior samples. Stored in + _equal_weights.txt + + cluster_posteriors : boolean + (Default: True) + Produce posterior files for each cluster? + Does nothing if do_clustering=False. + + write_resume : boolean + (Default: True) + Create a resume file. + + read_resume : boolean + (Default: True) + Read from resume file. + + write_stats : boolean + (Default: True) + Write an evidence statistics file. + + write_live : boolean + (Default: True) + Write a live points file. + + write_dead : boolean + (Default: True) + Write a dead points file. + + write_prior : boolean + (Default: True) + Write a prior points file. + + maximise : boolean + (Default: False) + Perform maximisation at the end of the run to find the maximum + likelihood point and value + + compression_factor : double + (Default: exp(-1)) + How often to update the files and do clustering + + synchronous : boolean + (Default: True) + Parallelise with synchronous workers, rather than asynchronous ones. + This can be set to False if the likelihood speed is known to be + approximately constant across the parameter space. Synchronous + parallelisation is less effective than asynchronous by a factor ~O(1) + for large parallelisation. + + base_dir : string + (Default: 'chains') + Where to store output files. + + file_root : string + (Default: 'test') + Root name of the files produced. + + grade_frac : List[float] + (Default: [1]) + The amount of time to spend in each speed. + If any of grade_frac are <= 1, then polychord will time each sub-speed, + and then choose num_repeats for the number of slowest repeats, and + spend the proportion of time indicated by grade_frac. Otherwise this + indicates the number of repeats to spend in each speed. + + grade_dims : List[int] + (Default: nDims) + The number of parameters within each speed. + + nlives : dict {double:int} + (Default: {}) + Variable number of live points option. This dictionary is a mapping + between loglike contours and nlive. + You should still set nlive to be a sensible number, as this indicates + how often to update the clustering, and to define the default value. + seed : positive int + (Default: system time in milliseconds) + Choose the seed to seed the random number generator. + Note **Positive seeds only** + a negative seed indicates that you should use the system time in + milliseconds + cube_samples: array-like + (Default: None) + samples from the unit hypercube to start nested sampling from. This is + useful if the prior is hard to rejection sample directly from the unit + hypercube (e.g. if there is a large amount of excluded parameter + space), but can be done more efficiently manually. A concrete example + could be include sorted uniform parameters by requiring 0 < x1 < x2 < + x3 < 1, if one did not want to use SortedUniformPrior. + Only available in Python interface. + shape (:, nDims) Returns ------- From 85b7dfbcdeace9ebbaa39124747ef6640fb25525 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 19:20:39 +0100 Subject: [PATCH 05/87] changed examples to use **kwargs --- Python_Functions/run_pypolychord_eggbox.py | 12 +++++------ Python_Functions/run_pypolychord_gaussian.py | 12 +++++------ .../run_pypolychord_gaussian_shell.py | 12 +++++------ .../run_pypolychord_gaussian_shells.py | 12 +++++------ .../run_pypolychord_half_gaussian.py | 12 +++++------ .../run_pypolychord_himmelblau.py | 12 +++++------ Python_Functions/run_pypolychord_pyramidal.py | 12 +++++------ .../run_pypolychord_random_gaussian.py | 12 +++++------ Python_Functions/run_pypolychord_rastrigin.py | 12 +++++------ .../run_pypolychord_rosenbrock.py | 12 +++++------ .../run_pypolychord_twin_gaussian.py | 10 +++++----- pypolychord/__init__.py | 1 - run_pypolychord.ipynb | 20 +++++++++---------- run_pypolychord.py | 16 +++++++-------- 14 files changed, 83 insertions(+), 84 deletions(-) diff --git a/Python_Functions/run_pypolychord_eggbox.py b/Python_Functions/run_pypolychord_eggbox.py index 6acbedee..431cd957 100755 --- a/Python_Functions/run_pypolychord_eggbox.py +++ b/Python_Functions/run_pypolychord_eggbox.py @@ -1,6 +1,5 @@ from numpy import log, prod, cos import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior #EGGBOX @@ -28,12 +27,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) -settings.file_root = functionName -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_gaussian.py b/Python_Functions/run_pypolychord_gaussian.py index b58389ac..b3917681 100755 --- a/Python_Functions/run_pypolychord_gaussian.py +++ b/Python_Functions/run_pypolychord_gaussian.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -31,12 +30,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_gaussian_shell.py b/Python_Functions/run_pypolychord_gaussian_shell.py index ec33ffbe..0c375684 100644 --- a/Python_Functions/run_pypolychord_gaussian_shell.py +++ b/Python_Functions/run_pypolychord_gaussian_shell.py @@ -1,7 +1,6 @@ from numpy import pi, log, sqrt, zeros from scipy.special import loggamma import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -48,12 +47,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_gaussian_shells.py b/Python_Functions/run_pypolychord_gaussian_shells.py index 7ce4ed02..f70a5a31 100644 --- a/Python_Functions/run_pypolychord_gaussian_shells.py +++ b/Python_Functions/run_pypolychord_gaussian_shells.py @@ -1,7 +1,6 @@ from numpy import pi, log, sqrt, logaddexp, exp, zeros from scipy.special import loggamma import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -70,12 +69,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_half_gaussian.py b/Python_Functions/run_pypolychord_half_gaussian.py index 28ada465..50fa30d2 100644 --- a/Python_Functions/run_pypolychord_half_gaussian.py +++ b/Python_Functions/run_pypolychord_half_gaussian.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -33,12 +32,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_himmelblau.py b/Python_Functions/run_pypolychord_himmelblau.py index 487509e3..8f8a183a 100644 --- a/Python_Functions/run_pypolychord_himmelblau.py +++ b/Python_Functions/run_pypolychord_himmelblau.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -27,12 +26,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_pyramidal.py b/Python_Functions/run_pypolychord_pyramidal.py index 05ca1d0e..d82898e7 100644 --- a/Python_Functions/run_pypolychord_pyramidal.py +++ b/Python_Functions/run_pypolychord_pyramidal.py @@ -1,7 +1,6 @@ from numpy import log, exp, sqrt, pi from scipy.special import loggamma import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior #EGGBOX @@ -43,12 +42,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) -settings.file_root = functionName -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_random_gaussian.py b/Python_Functions/run_pypolychord_random_gaussian.py index b58389ac..b3917681 100644 --- a/Python_Functions/run_pypolychord_random_gaussian.py +++ b/Python_Functions/run_pypolychord_random_gaussian.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -31,12 +30,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_rastrigin.py b/Python_Functions/run_pypolychord_rastrigin.py index 97dd97ff..4e300502 100755 --- a/Python_Functions/run_pypolychord_rastrigin.py +++ b/Python_Functions/run_pypolychord_rastrigin.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt, cos import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -30,12 +29,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_rosenbrock.py b/Python_Functions/run_pypolychord_rosenbrock.py index c92d9d77..9b9e7f21 100755 --- a/Python_Functions/run_pypolychord_rosenbrock.py +++ b/Python_Functions/run_pypolychord_rosenbrock.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -36,12 +35,13 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] output.make_paramnames_files(paramnames) diff --git a/Python_Functions/run_pypolychord_twin_gaussian.py b/Python_Functions/run_pypolychord_twin_gaussian.py index 2eaa3c1b..7fa60aa2 100644 --- a/Python_Functions/run_pypolychord_twin_gaussian.py +++ b/Python_Functions/run_pypolychord_twin_gaussian.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt, logaddexp, exp import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -56,10 +55,11 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) -settings = PolyChordSettings(nDims, nDerived) #settings is an object -settings.file_root = functionName #string -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": functionName, #string + "do_clustering": True, + "read_resume": False, +} output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] diff --git a/pypolychord/__init__.py b/pypolychord/__init__.py index 3e4047d7..9fe3a240 100644 --- a/pypolychord/__init__.py +++ b/pypolychord/__init__.py @@ -1,3 +1,2 @@ __version__ = "1.20.1" -from pypolychord.settings import PolyChordSettings from pypolychord.polychord import run_polychord diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 79d69f78..cb7b3f2d 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -8,7 +8,6 @@ "source": [ "from numpy import pi, log, sqrt\n", "import pypolychord\n", - "from pypolychord.settings import PolyChordSettings\n", "from pypolychord.priors import UniformPrior" ] }, @@ -91,11 +90,12 @@ "metadata": {}, "outputs": [], "source": [ - "settings = PolyChordSettings(nDims, nDerived)\n", - "settings.file_root = 'gaussian'\n", - "settings.nlive = 200\n", - "settings.do_clustering = True\n", - "settings.read_resume = False" + "kwargs = {\n", + " \"file_root\": 'gaussian',\n", + " \"nlive\": 200,\n", + " \"do_clustering\": True,\n", + " \"read_resume\": False,\n", + "}" ] }, { @@ -148,7 +148,7 @@ } ], "source": [ - "output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper)" + "output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs)" ] }, { @@ -183,7 +183,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -197,7 +197,7 @@ "source": [ "try:\n", " from anesthetic import NestedSamples\n", - " samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root)\n", + " samples = NestedSamples(root= \"chains/\" + kwargs[\"file_root\"])\n", " fig, axes = samples.plot_2d(['p0','p1','p2','p3','r'])\n", " fig.savefig('posterior.pdf')\n", "\n", @@ -211,7 +211,7 @@ " except ImportError:\n", " print(\"Install matplotlib and getdist for plotting examples\")\n", "\n", - " print(\"Install anesthetic or getdist for for plotting examples\")" + " print(\"Install anesthetic or getdist for for plotting examples\")" ] }, { diff --git a/run_pypolychord.py b/run_pypolychord.py index 465c3543..4d5a12a7 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,6 +1,5 @@ from numpy import pi, log, sqrt import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior try: from mpi4py import MPI @@ -40,15 +39,16 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Initialise the settings -settings = PolyChordSettings(nDims, nDerived) -settings.file_root = 'gaussian' -settings.nlive = 200 -settings.do_clustering = True -settings.read_resume = False +kwargs = { + "file_root": 'gaussian', + "nlive": 200, + "do_clustering": True, + "read_resume": False, +} #| Run PolyChord -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) #| Create a paramnames file @@ -59,7 +59,7 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot (could also use getdist) try: from anesthetic import NestedSamples - samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root) + samples = NestedSamples(root= "chains/" + kwargs["file_root"]) fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) fig.savefig('posterior.pdf') From 8cfcd22878eef7bac4a552406a83b6beb18bef4f Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 19:25:18 +0100 Subject: [PATCH 06/87] accidental rename of run_polychord() --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 7a061dc3..0e1f2532 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -12,7 +12,7 @@ def default_dumper(live, dead, logweights, logZ, logZerr): pass -def run_pypolychord(loglikelihood, nDims, nDerived, +def run_polychord(loglikelihood, nDims, nDerived, prior=default_prior, dumper=default_dumper, **kwargs): """ Runs PolyChord. From 84c30c35bcb4fadbb74727a2a24ad201976f3846 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 22:43:24 +0100 Subject: [PATCH 07/87] set kwarg defaults separately to run_pypolychord so that they can be given to resume file --- pypolychord/polychord.py | 99 ++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 0e1f2532..22bef508 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -302,6 +302,7 @@ def run_polychord(loglikelihood, nDims, nDerived, """ + kwargs = kwargs.copy() try: from mpi4py import MPI @@ -310,9 +311,36 @@ def run_polychord(loglikelihood, nDims, nDerived, except ImportError: rank = 0 - kwargs.setdefault("base_dir", "chains") - kwargs.setdefault("cluster_dir", "test") - kwargs.setdefault("read_resume", True) + kwargs.setdefault('nlive', nDims*25), + kwargs.setdefault('num_repeats', nDims*5), + kwargs.setdefault('nprior', -1), + kwargs.setdefault('nfail', -1), + kwargs.setdefault('do_clustering', True), + kwargs.setdefault('feedback', 1), + kwargs.setdefault('precision_criterion', 0.001), + kwargs.setdefault('logzero', -1e30), + kwargs.setdefault('max_ndead', -1), + kwargs.setdefault('boost_posterior', 0.0), + kwargs.setdefault('posteriors', True), + kwargs.setdefault('equals', True), + kwargs.setdefault('cluster_posteriors', True), + kwargs.setdefault('write_resume', True), + kwargs.setdefault('write_paramnames', False), + kwargs.setdefault('read_resume', True), + kwargs.setdefault('write_stats', True), + kwargs.setdefault('write_live', True), + kwargs.setdefault('write_dead', True), + kwargs.setdefault('write_prior', True), + kwargs.setdefault('maximise', False), + kwargs.setdefault('compression_factor', np.exp(-1)), + kwargs.setdefault('synchronous', True), + kwargs.setdefault('base_dir', 'chains'), + kwargs.setdefault('file_root', 'test'), + kwargs["grade_dims"] = list(kwargs.pop('grade_dims', [nDims])) + kwargs.setdefault("grade_frac", [1.0]*len(kwargs["grade_dims"])) + kwargs.setdefault("nlives", {}) + kwargs.setdefault("seed", -1), + try: if rank == 0: os.makedirs(kwargs["base_dir"]) @@ -321,7 +349,7 @@ def run_polychord(loglikelihood, nDims, nDerived, try: if rank == 0: - os.makedirs(os.path.join(kwargs["base_dir"], kwargs["cluster_dir"])) + os.makedirs(os.path.join(kwargs["base_dir"], "clusters")) except OSError: pass @@ -346,45 +374,48 @@ def wrap_prior(cube, theta): dumper, nDims, nDerived, - kwargs.pop('nlive', nDims*25), - kwargs.pop('num_repeats', nDims*5), - kwargs.pop('nprior', -1), - kwargs.pop('nfail', -1), - kwargs.pop('do_clustering', True), - kwargs.pop('feedback', 1), - kwargs.pop('precision_criterion', 0.001), - kwargs.pop('logzero', -1e30), - kwargs.pop('max_ndead', -1), - kwargs.pop('boost_posterior', 0.0), - kwargs.pop('posteriors', True), - kwargs.pop('equals', True), - kwargs.pop('cluster_posteriors', True), - kwargs.pop('write_resume', True), - kwargs.pop('write_paramnames', False), - kwargs.pop('read_resume', True), - kwargs.pop('write_stats', True), - kwargs.pop('write_live', True), - kwargs.pop('write_dead', True), - kwargs.pop('write_prior', True), - kwargs.pop('maximise', False), - kwargs.pop('compression_factor', np.exp(-1)), - kwargs.pop('synchronous', True), - kwargs.pop('base_dir', 'chains'), - kwargs.pop('file_root', 'test'), - kwargs.pop('seed', -1), - kwargs.pop("grade_frac", [1.0]*len(kwargs["grade_dims"])), + kwargs['nlive'], + kwargs['num_repeats'], + kwargs['nprior'], + kwargs['nfail'], + kwargs['do_clustering'], + kwargs['feedback'], + kwargs['precision_criterion'], + kwargs['logzero'], + kwargs['max_ndead'], + kwargs['boost_posterior'], + kwargs['posteriors'], + kwargs['equals'], + kwargs['cluster_posteriors'], + kwargs['write_resume'], + kwargs['write_paramnames'], + kwargs['read_resume'], + kwargs['write_stats'], + kwargs['write_live'], + kwargs['write_dead'], + kwargs['write_prior'], + kwargs['maximise'], + kwargs['compression_factor'], + kwargs['synchronous'], + kwargs['base_dir'], + kwargs['file_root'], + kwargs["grade_frac"], kwargs["grade_dims"], kwargs["nlives"], - kwargs.pop("seed", -1), + kwargs["seed"], ) if "cube_samples" in kwargs: kwargs["read_resume"] = read_resume + + polychord_output = PolyChordOutput(kwargs["base_dir"], kwargs["file_root"]) + if "paramnames" in kwargs: + polychord_output.make_paramnames_files(kwargs["paramnames"]) try: import anesthetic - return anesthetic.NestedSamples(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) except ImportError: - return PolyChordOutput(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + return polychord_output From 6fe05166db45f7de2c0adde07542a4c5f851e79e Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 22:47:25 +0100 Subject: [PATCH 08/87] run_polychord creates the paramnames file --- run_pypolychord.ipynb | 36 ++++++++++++++++++------------------ run_pypolychord.py | 10 +++++----- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index cb7b3f2d..82070ad1 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -77,6 +77,23 @@ " print(\"Last dead point:\", dead[-1])" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a paramnames file" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", + "paramnames += [('r*', 'r')]" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -95,6 +112,7 @@ " \"nlive\": 200,\n", " \"do_clustering\": True,\n", " \"read_resume\": False,\n", + " \"paramnames\": paramnames,\n", "}" ] }, @@ -151,24 +169,6 @@ "output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a paramnames file" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", - "paramnames += [('r*', 'r')]\n", - "output.make_paramnames_files(paramnames)" - ] - }, { "cell_type": "markdown", "metadata": {}, diff --git a/run_pypolychord.py b/run_pypolychord.py index 4d5a12a7..2d0de54b 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -37,6 +37,10 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) +#| Create a paramnames file +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + #| Initialise the settings kwargs = { @@ -44,17 +48,13 @@ def dumper(live, dead, logweights, logZ, logZerr): "nlive": 200, "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } #| Run PolyChord output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -#| Create a paramnames file - -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) #| Make an anesthetic plot (could also use getdist) try: From 51928d09e2607e5beaae1d41240e00a2b4d6656b Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 22:56:55 +0100 Subject: [PATCH 09/87] changed other examples paramnames are passed to run_pypolychord --- Python_Functions/run_pypolychord_eggbox.py | 7 ++++--- Python_Functions/run_pypolychord_gaussian.py | 6 +++--- Python_Functions/run_pypolychord_gaussian_shell.py | 6 +++--- Python_Functions/run_pypolychord_gaussian_shells.py | 7 ++++--- Python_Functions/run_pypolychord_half_gaussian.py | 7 ++++--- Python_Functions/run_pypolychord_himmelblau.py | 7 ++++--- Python_Functions/run_pypolychord_pyramidal.py | 7 ++++--- Python_Functions/run_pypolychord_random_gaussian.py | 7 ++++--- Python_Functions/run_pypolychord_rastrigin.py | 7 ++++--- Python_Functions/run_pypolychord_rosenbrock.py | 7 ++++--- Python_Functions/run_pypolychord_twin_gaussian.py | 7 ++++--- 11 files changed, 42 insertions(+), 33 deletions(-) diff --git a/Python_Functions/run_pypolychord_eggbox.py b/Python_Functions/run_pypolychord_eggbox.py index 431cd957..135b8961 100755 --- a/Python_Functions/run_pypolychord_eggbox.py +++ b/Python_Functions/run_pypolychord_eggbox.py @@ -27,16 +27,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian.py b/Python_Functions/run_pypolychord_gaussian.py index b3917681..3b661735 100755 --- a/Python_Functions/run_pypolychord_gaussian.py +++ b/Python_Functions/run_pypolychord_gaussian.py @@ -30,16 +30,16 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian_shell.py b/Python_Functions/run_pypolychord_gaussian_shell.py index 0c375684..4d512fc6 100644 --- a/Python_Functions/run_pypolychord_gaussian_shell.py +++ b/Python_Functions/run_pypolychord_gaussian_shell.py @@ -47,16 +47,16 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian_shells.py b/Python_Functions/run_pypolychord_gaussian_shells.py index f70a5a31..b15f8e38 100644 --- a/Python_Functions/run_pypolychord_gaussian_shells.py +++ b/Python_Functions/run_pypolychord_gaussian_shells.py @@ -1,3 +1,4 @@ +from xml.dom.expatbuilder import parseFragmentString from numpy import pi, log, sqrt, logaddexp, exp, zeros from scipy.special import loggamma import pypolychord @@ -69,16 +70,16 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_half_gaussian.py b/Python_Functions/run_pypolychord_half_gaussian.py index 50fa30d2..84270296 100644 --- a/Python_Functions/run_pypolychord_half_gaussian.py +++ b/Python_Functions/run_pypolychord_half_gaussian.py @@ -32,16 +32,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnmames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_himmelblau.py b/Python_Functions/run_pypolychord_himmelblau.py index 8f8a183a..2166ba07 100644 --- a/Python_Functions/run_pypolychord_himmelblau.py +++ b/Python_Functions/run_pypolychord_himmelblau.py @@ -26,16 +26,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_pyramidal.py b/Python_Functions/run_pypolychord_pyramidal.py index d82898e7..e9cc3abf 100644 --- a/Python_Functions/run_pypolychord_pyramidal.py +++ b/Python_Functions/run_pypolychord_pyramidal.py @@ -42,16 +42,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_random_gaussian.py b/Python_Functions/run_pypolychord_random_gaussian.py index b3917681..b0936346 100644 --- a/Python_Functions/run_pypolychord_random_gaussian.py +++ b/Python_Functions/run_pypolychord_random_gaussian.py @@ -30,16 +30,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_rastrigin.py b/Python_Functions/run_pypolychord_rastrigin.py index 4e300502..73671ff6 100755 --- a/Python_Functions/run_pypolychord_rastrigin.py +++ b/Python_Functions/run_pypolychord_rastrigin.py @@ -29,16 +29,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_rosenbrock.py b/Python_Functions/run_pypolychord_rosenbrock.py index 9b9e7f21..331c1d14 100755 --- a/Python_Functions/run_pypolychord_rosenbrock.py +++ b/Python_Functions/run_pypolychord_rosenbrock.py @@ -35,16 +35,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_twin_gaussian.py b/Python_Functions/run_pypolychord_twin_gaussian.py index 7fa60aa2..a240ae2a 100644 --- a/Python_Functions/run_pypolychord_twin_gaussian.py +++ b/Python_Functions/run_pypolychord_twin_gaussian.py @@ -55,16 +55,17 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] + kwargs = { "file_root": functionName, #string "do_clustering": True, "read_resume": False, + "paramnames": paramnames, } output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) try: import getdist.plots From da44aa759d141521cecfd1b450e07657df4ffd06 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 23:03:43 +0100 Subject: [PATCH 10/87] mention anesthetic output in docstring (incomplete) --- pypolychord/polychord.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 22bef508..4ebdfe8b 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -55,7 +55,7 @@ def run_polychord(loglikelihood, nDims, nDerived, ------- logL: float log-likelihood - TODO: what about []? + TODO: what about []? I don't think it can be omitted from the return of loglikelihood. nDims: int Dimensionality of the model, i.e. the number of physical parameters. @@ -267,6 +267,10 @@ def run_polychord(loglikelihood, nDims, nDerived, Will Handley: wh260@cam.ac.uk + OR if anesthetic is installed: + + anesthetic.NestedSamples object + In general the contents of is a set of getdist compatible files. = / @@ -299,7 +303,7 @@ def run_polychord(loglikelihood, nDims, nDerived, Final output evidence statistics - + TODO: Check for kwargs that shouldn't be there. """ kwargs = kwargs.copy() From 617ad125e73e1f7eb901697cae439069297713e4 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 23:10:37 +0100 Subject: [PATCH 11/87] tidied up grade_dims and nlives argument processing --- pypolychord/polychord.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 4ebdfe8b..9921beb8 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -340,9 +340,11 @@ def run_polychord(loglikelihood, nDims, nDerived, kwargs.setdefault('synchronous', True), kwargs.setdefault('base_dir', 'chains'), kwargs.setdefault('file_root', 'test'), - kwargs["grade_dims"] = list(kwargs.pop('grade_dims', [nDims])) + kwargs.setdefault("grade_dims", [nDims]) + kwargs["grade_dims"] = [int(d) for d in list(kwargs["grade_dims"])] kwargs.setdefault("grade_frac", [1.0]*len(kwargs["grade_dims"])) kwargs.setdefault("nlives", {}) + kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs["nlives"].items()} kwargs.setdefault("seed", -1), try: @@ -369,8 +371,6 @@ def wrap_loglikelihood(theta, phi): def wrap_prior(cube, theta): theta[:] = prior(cube) - kwargs["grade_dims"] = [int(d) for d in kwargs.pop("grade_dims", [nDims])] - kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs.pop("nlives", {}).items()} # Run polychord from module library _pypolychord.run(wrap_loglikelihood, From d37581003b87fc838065e256a6d68ff1dd4dbb02 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 23:21:48 +0100 Subject: [PATCH 12/87] logx should be logc --- Python_Functions/run_pypolychord_gaussian_shells.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python_Functions/run_pypolychord_gaussian_shells.py b/Python_Functions/run_pypolychord_gaussian_shells.py index b15f8e38..999b0336 100644 --- a/Python_Functions/run_pypolychord_gaussian_shells.py +++ b/Python_Functions/run_pypolychord_gaussian_shells.py @@ -41,7 +41,7 @@ def likelihood(theta): r2 = 0 - def logincexp(loga,logb,logx=None): + def logincexp(loga,logb,logc=None): if (loga>logb): loga = loga + log(exp(logb-loga) + 1) else: From 6270512943825284fac48f3f551ac982a1ded83c Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 23:24:54 +0100 Subject: [PATCH 13/87] missing comma --- Python_Functions/run_pypolychord_gaussian.py | 2 +- Python_Functions/run_pypolychord_random_gaussian.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Python_Functions/run_pypolychord_gaussian.py b/Python_Functions/run_pypolychord_gaussian.py index 3b661735..ef55057b 100755 --- a/Python_Functions/run_pypolychord_gaussian.py +++ b/Python_Functions/run_pypolychord_gaussian.py @@ -39,7 +39,7 @@ def dumper(live, dead, logweights, logZ, logZerr): "paramnames": paramnames, } -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_random_gaussian.py b/Python_Functions/run_pypolychord_random_gaussian.py index b0936346..513a929b 100644 --- a/Python_Functions/run_pypolychord_random_gaussian.py +++ b/Python_Functions/run_pypolychord_random_gaussian.py @@ -40,7 +40,7 @@ def dumper(live, dead, logweights, logZ, logZerr): "paramnames": paramnames, } -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper **kwargs) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) try: import getdist.plots From 1316fa826da89b94f5d9edfe07fa442ea7abb66f Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 27 Jul 2022 23:25:06 +0100 Subject: [PATCH 14/87] removed settings argument --- Python_Functions/run_pypolychord_twin_gaussian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python_Functions/run_pypolychord_twin_gaussian.py b/Python_Functions/run_pypolychord_twin_gaussian.py index a240ae2a..c1b23c4a 100644 --- a/Python_Functions/run_pypolychord_twin_gaussian.py +++ b/Python_Functions/run_pypolychord_twin_gaussian.py @@ -65,7 +65,7 @@ def dumper(live, dead, logweights, logZ, logZerr): "paramnames": paramnames, } -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) +output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) try: import getdist.plots From 28ee39b1d7ee7b8da8dacfd48be587f82156e555 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:03:59 +0100 Subject: [PATCH 15/87] Restored old interface --- pypolychord/polychord.py | 349 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 347 insertions(+), 2 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 9921beb8..f3e0c902 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -11,8 +11,353 @@ def default_prior(cube): def default_dumper(live, dead, logweights, logZ, logZerr): pass +def run_polychord(loglikelihood, nDims, nDerived, settings, + prior=default_prior, dumper=default_dumper): + """ + Runs PolyChord. + + For full details of nested sampling and PolyChord, please refer to: + + * PolyChord paper: http://arxiv.org/abs/1506.00171 + * Nested Sampling paper: http://projecteuclid.org/euclid.ba/1340370944 + + To run in mpi mode, just run your script with mpirun as usual. + Make sure that pypolychord is compiled with MPI: + $ make veryclean + $ make pypolychord MPI=1 + + Users are also required to cite the PolyChord papers: + arXiv:1502.01856 + arXiv:1506.00171 + in their publications. + + + Parameters + ---------- + + loglikelihood: function + This function computes the log-likelihood of the model and derived + parameters (phi) from the physical coordinates (theta). + + Parameters + ---------- + theta: numpy.array + physical coordinate. Length nDims. + + Returns + ------- + (logL, phi): (float, array-like) + return is a 2-tuple of the log-likelihood (logL) and the derived + parameters (phi). phi length nDerived. + + Returns + ------- + logL: float + log-likelihood + + nDims: int + Dimensionality of the model, i.e. the number of physical parameters. + + nDerived: int + The number of derived parameters (can be 0). + + settings: settings.Settings + Object containing polychord settings + + Optional Arguments + ------------------ + + prior: function + This function converts from the unit hypercube to the physical + parameters. + (Default: identity function => prior(cube) == cube ) + + Parameters + ---------- + cube: numpy.array + coordinates in the unit hypercube. Length nDims. + + Returns + ------- + theta: array-like + physical coordinates. Length nDims. + + dumper: function + This function gives run-time access to the posterior and live points. + + Parameters + ---------- + live: numpy.array + The live points and their loglikelihood birth and death contours + Shape (nlive, nDims+nDerived+2) + dead: numpy.array + The dead points and their loglikelihood birth and death contours + Shape (ndead, nDims+nDerived+2) + logweights: numpy.array + The posterior weights of the dead points + Shape (ndead) + logZ: float + The current log-evidence estimate + logZerr: float + The current log-evidence error estimate + + Returns + ------- + None. (in Python) + + All output is currently produced in the form of text files in + directory. If you would like to contribute to pypolychord and improve this, + please get in touch: + + Will Handley: wh260@cam.ac.uk + + In general the contents of is a set of getdist compatible files. + + = / + + .txt (posteriors = True) + Weighted posteriors. Compatible with getdist. Each line contains: + + weight, -2*loglikelihood, , + + Note that here the weights are not normalised, but instead are chosen + so that the maximum weight is 1.0. + + _equal_weights.txt (equals = True) + Weighted posteriors. Compatible with getdist. Each line contains: + 1.0, -2*loglikelihood, , + + _dead.txt (write_dead = True) + Dead points. Each line contains: + loglikelihood, , + + _phys_live.txt (write_live = True) + Live points. Each line contains: + , , loglikelihood + Mostly useful for run-time monitoring. + + .resume + Resume file. Human readable. + + .stats + Final output evidence statistics + + """ + + try: + from mpi4py import MPI + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + except ImportError: + rank = 0 + + try: + if rank == 0: + os.makedirs(settings.base_dir) + except OSError: + pass + + try: + if rank == 0: + os.makedirs(settings.cluster_dir) + except OSError: + pass + + if settings.cube_samples is not None: + make_resume_file(settings, loglikelihood, prior) + read_resume = settings.read_resume + settings.read_resume=True + + def wrap_loglikelihood(theta, phi): + logL, phi[:] = loglikelihood(theta) + return logL + + def wrap_prior(cube, theta): + theta[:] = prior(cube) + + settings.grade_dims = [int(d) for d in settings.grade_dims] + settings.nlives = {float(logL):int(nlive) for logL, nlive in settings.nlives.items()} + + # Run polychord from module library + _pypolychord.run(wrap_loglikelihood, + wrap_prior, + dumper, + nDims, + nDerived, + settings.nlive, + settings.num_repeats, + settings.nprior, + settings.nfail, + settings.do_clustering, + settings.feedback, + settings.precision_criterion, + settings.logzero, + settings.max_ndead, + settings.boost_posterior, + settings.posteriors, + settings.equals, + settings.cluster_posteriors, + settings.write_resume, + settings.write_paramnames, + settings.read_resume, + settings.write_stats, + settings.write_live, + settings.write_dead, + settings.write_prior, + settings.maximise, + settings.compression_factor, + settings.synchronous, + settings.base_dir, + settings.file_root, + settings.grade_frac, + settings.grade_dims, + settings.nlives, + settings.seed) + + if settings.cube_samples is not None: + settings.read_resume = read_resume + + return PolyChordOutput(settings.base_dir, settings.file_root) + + +def make_resume_file(settings, loglikelihood, prior): + import fortranformat as ff + resume_filename = os.path.join(settings.base_dir, + settings.file_root)+".resume" + + try: + from mpi4py import MPI + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + except ImportError: + rank = 0 + size = 1 + + lives = [] + logL_birth = settings.logzero + for i in np.array_split(np.arange(len(settings.cube_samples)), size)[rank]: + cube = settings.cube_samples[i] + theta = prior(cube) + logL, derived = loglikelihood(theta) + nDims = len(theta) + nDerived = len(derived) + lives.append(np.concatenate([cube,theta,derived,[logL_birth, logL]])) + + try: + sendbuf = np.array(lives).flatten() + sendcounts = np.array(comm.gather(len(sendbuf))) + if rank == 0: + recvbuf = np.empty(sum(sendcounts), dtype=int) + else: + recvbuf = None + comm.Gatherv(sendbuf=sendbuf, recvbuf=(recvbuf, sendcounts), root=root) + + lives = np.reshape(sendbuf, (len(settings.cube_samples), len(lives[0]))) + except NameError: + lives = np.array(lives) + + if rank == 0: + with open(resume_filename,"w") as f: + def write(var): + var = np.atleast_1d(var) + if isinstance(var[0], np.integer): + fmt = '(%iI12)' % var.size + elif isinstance(var[0], np.double): + fmt = '(%iE24.15E3)' % var.size + else: + fmt = '(A)' + writer = ff.FortranRecordWriter(fmt) + f.write(writer.write(var) + '\n') + + write('=== Number of dimensions ===') + write(nDims) + write('=== Number of derived parameters ===') + write(nDerived) + write('=== Number of dead points/iterations ===') + write(0) + write('=== Number of clusters ===') + write(1) + write('=== Number of dead clusters ===') + write(0) + write('=== Number of global weighted posterior points ===') + write(0) + write('=== Number of global equally weighted posterior points ===') + write(0) + write('=== Number of grades ===') + write(len(settings.grade_dims)) + write('=== positions of grades ===') + write(settings.grade_dims) + write('=== Number of repeats ===') + write(settings.num_repeats) + write('=== Number of likelihood calls ===') + write(len(lives)) + write('=== Number of live points in each cluster ===') + write(len(lives)) + write('=== Number of phantom points in each cluster ===') + write(0) + write('=== Number of weighted posterior points in each cluster ===') + write(0) + write('=== Number of equally weighted posterior points in each cluster ===') + write(0) + write('=== Minimum loglikelihood positions ===') + write(np.argmin(lives[:,-1])) + write('=== Number of weighted posterior points in each dead cluster ===') + write('=== Number of equally weighted posterior points in each dead cluster ===') + write('=== global evidence -- log() ===') + write(settings.logzero) + write('=== global evidence^2 -- log() ===') + write(settings.logzero) + write('=== posterior thin factor ===') + write(settings.boost_posterior) + write('=== local loglikelihood bounds ===') + write(lives[:,-1].min()) + write('=== local volume -- log() ===') + write(0.0) + write('=== last update volume ===') + write(0.0) + write('=== global evidence volume cross correlation -- log() ===') + write(settings.logzero) + write('=== local evidence -- log() ===') + write(settings.logzero) + write('=== local evidence^2 -- log() ===') + write(settings.logzero) + write('=== local evidence volume cross correlation -- log() ===') + write(settings.logzero) + write('=== local volume cross correlation -- log() ===') + write(0.0) + write('=== maximum log weights -- log(w_p) ===') + write(settings.logzero) + write('=== local dead evidence -- log() ===') + write('=== local dead evidence^2 -- log() ===') + write('=== maximum dead log weights -- log(w_p) ===') + write('=== covariance matrices ===') + write('---------------------------------------') + for x in np.identity(nDims): + write(x) + write('=== cholesky decompositions ===') + write('---------------------------------------') + for x in np.identity(nDims): + write(x) + write('=== live points ===') + write('---------------------------------------') + for x in lives: + write(x) + write('=== dead points ===') + write('=== logweights of dead points ===') + write('=== phantom points ===') + write('---------------------------------------') + write('=== weighted posterior points ===') + write('---------------------------------------') + write('=== dead weighted posterior points ===') + write('=== global weighted posterior points ===') + write('=== equally weighted posterior points ===') + write('---------------------------------------') + write('=== dead equally weighted posterior points ===') + write('=== global equally weighted posterior points ===') + -def run_polychord(loglikelihood, nDims, nDerived, +def run(loglikelihood, nDims, nDerived, prior=default_prior, dumper=default_dumper, **kwargs): """ Runs PolyChord. @@ -423,7 +768,7 @@ def wrap_prior(cube, theta): -def make_resume_file(loglikelihood, prior, **kwargs): +def make_resume_file_kwargs_interface(loglikelihood, prior, **kwargs): import fortranformat as ff resume_filename = os.path.join(kwargs["base_dir"], kwargs["file_root"])+".resume" From 50fa72b3428efcfa264b0f336ed975edb268486d Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:04:58 +0100 Subject: [PATCH 16/87] run_polychord -> run --- run_pypolychord.ipynb | 2 +- run_pypolychord.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 82070ad1..525cc0dd 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -166,7 +166,7 @@ } ], "source": [ - "output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs)" + "output = pypolychord.run(likelihood, nDims, nDerived, prior, dumper, **kwargs)" ] }, { diff --git a/run_pypolychord.py b/run_pypolychord.py index 2d0de54b..ade09200 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -53,7 +53,7 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Run PolyChord -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output = pypolychord.run(likelihood, nDims, nDerived, prior, dumper, **kwargs) #| Make an anesthetic plot (could also use getdist) From ae5c2611d3d80c595653f1579b30dd6b3293fefa Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:09:48 +0100 Subject: [PATCH 17/87] reverted Python_Functions/ (apart from logx->logc correction --- Python_Functions/run_pypolychord_eggbox.py | 17 ++++++++--------- Python_Functions/run_pypolychord_gaussian.py | 16 ++++++++-------- .../run_pypolychord_gaussian_shell.py | 16 ++++++++-------- .../run_pypolychord_gaussian_shells.py | 17 ++++++++--------- .../run_pypolychord_half_gaussian.py | 17 ++++++++--------- Python_Functions/run_pypolychord_himmelblau.py | 17 ++++++++--------- Python_Functions/run_pypolychord_pyramidal.py | 17 ++++++++--------- .../run_pypolychord_random_gaussian.py | 17 ++++++++--------- Python_Functions/run_pypolychord_rastrigin.py | 17 ++++++++--------- Python_Functions/run_pypolychord_rosenbrock.py | 17 ++++++++--------- .../run_pypolychord_twin_gaussian.py | 17 ++++++++--------- 11 files changed, 88 insertions(+), 97 deletions(-) diff --git a/Python_Functions/run_pypolychord_eggbox.py b/Python_Functions/run_pypolychord_eggbox.py index 135b8961..6acbedee 100755 --- a/Python_Functions/run_pypolychord_eggbox.py +++ b/Python_Functions/run_pypolychord_eggbox.py @@ -1,5 +1,6 @@ from numpy import log, prod, cos import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior #EGGBOX @@ -27,17 +28,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) +settings.file_root = functionName +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian.py b/Python_Functions/run_pypolychord_gaussian.py index ef55057b..b58389ac 100755 --- a/Python_Functions/run_pypolychord_gaussian.py +++ b/Python_Functions/run_pypolychord_gaussian.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -30,16 +31,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian_shell.py b/Python_Functions/run_pypolychord_gaussian_shell.py index 4d512fc6..ec33ffbe 100644 --- a/Python_Functions/run_pypolychord_gaussian_shell.py +++ b/Python_Functions/run_pypolychord_gaussian_shell.py @@ -1,6 +1,7 @@ from numpy import pi, log, sqrt, zeros from scipy.special import loggamma import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -47,16 +48,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_gaussian_shells.py b/Python_Functions/run_pypolychord_gaussian_shells.py index 999b0336..c3540e64 100644 --- a/Python_Functions/run_pypolychord_gaussian_shells.py +++ b/Python_Functions/run_pypolychord_gaussian_shells.py @@ -1,7 +1,7 @@ -from xml.dom.expatbuilder import parseFragmentString from numpy import pi, log, sqrt, logaddexp, exp, zeros from scipy.special import loggamma import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -70,16 +70,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_half_gaussian.py b/Python_Functions/run_pypolychord_half_gaussian.py index 84270296..28ada465 100644 --- a/Python_Functions/run_pypolychord_half_gaussian.py +++ b/Python_Functions/run_pypolychord_half_gaussian.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -32,17 +33,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnmames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_himmelblau.py b/Python_Functions/run_pypolychord_himmelblau.py index 2166ba07..487509e3 100644 --- a/Python_Functions/run_pypolychord_himmelblau.py +++ b/Python_Functions/run_pypolychord_himmelblau.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -26,17 +27,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_pyramidal.py b/Python_Functions/run_pypolychord_pyramidal.py index e9cc3abf..05ca1d0e 100644 --- a/Python_Functions/run_pypolychord_pyramidal.py +++ b/Python_Functions/run_pypolychord_pyramidal.py @@ -1,6 +1,7 @@ from numpy import log, exp, sqrt, pi from scipy.special import loggamma import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior #EGGBOX @@ -42,17 +43,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) +settings.file_root = functionName +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_random_gaussian.py b/Python_Functions/run_pypolychord_random_gaussian.py index 513a929b..b58389ac 100644 --- a/Python_Functions/run_pypolychord_random_gaussian.py +++ b/Python_Functions/run_pypolychord_random_gaussian.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -30,17 +31,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_rastrigin.py b/Python_Functions/run_pypolychord_rastrigin.py index 73671ff6..97dd97ff 100755 --- a/Python_Functions/run_pypolychord_rastrigin.py +++ b/Python_Functions/run_pypolychord_rastrigin.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt, cos import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -29,17 +30,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_rosenbrock.py b/Python_Functions/run_pypolychord_rosenbrock.py index 331c1d14..c92d9d77 100755 --- a/Python_Functions/run_pypolychord_rosenbrock.py +++ b/Python_Functions/run_pypolychord_rosenbrock.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -35,17 +36,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots diff --git a/Python_Functions/run_pypolychord_twin_gaussian.py b/Python_Functions/run_pypolychord_twin_gaussian.py index c1b23c4a..2eaa3c1b 100644 --- a/Python_Functions/run_pypolychord_twin_gaussian.py +++ b/Python_Functions/run_pypolychord_twin_gaussian.py @@ -1,5 +1,6 @@ from numpy import pi, log, sqrt, logaddexp, exp import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior nDims = 3 @@ -55,17 +56,15 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) # prints last element of dead (wich is an array) +settings = PolyChordSettings(nDims, nDerived) #settings is an object +settings.file_root = functionName #string +settings.do_clustering = True +settings.read_resume = False + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] - -kwargs = { - "file_root": functionName, #string - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output.make_paramnames_files(paramnames) try: import getdist.plots From 247c51cb1b6a49c16de70a1c4b93558af87fc5fd Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:12:02 +0100 Subject: [PATCH 18/87] put PolyChordSettings back in __init__.py --- pypolychord/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pypolychord/__init__.py b/pypolychord/__init__.py index 9fe3a240..3e4047d7 100644 --- a/pypolychord/__init__.py +++ b/pypolychord/__init__.py @@ -1,2 +1,3 @@ __version__ = "1.20.1" +from pypolychord.settings import PolyChordSettings from pypolychord.polychord import run_polychord From cf54330c6c8c5fa6f9e8e3a44967bcae79bf09b8 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:16:58 +0100 Subject: [PATCH 19/87] run() uses make_resume_file_kwargs_interface --- pypolychord/polychord.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index f3e0c902..e148bcda 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -357,6 +357,9 @@ def write(var): write('=== global equally weighted posterior points ===') +### new interface ### + + def run(loglikelihood, nDims, nDerived, prior=default_prior, dumper=default_dumper, **kwargs): """ @@ -705,7 +708,7 @@ def run(loglikelihood, nDims, nDerived, pass if "cube_samples" in kwargs: - make_resume_file(loglikelihood, prior, **kwargs) + make_resume_file_kwargs_interface(loglikelihood, prior, **kwargs) read_resume = kwargs["read_resume"] kwargs["read_resume"] = True From dd2cf63e3f7082e99378584981a29cc0a0775f6a Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:19:42 +0100 Subject: [PATCH 20/87] added run to __init__.py --- pypolychord/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/__init__.py b/pypolychord/__init__.py index 3e4047d7..f2d2886a 100644 --- a/pypolychord/__init__.py +++ b/pypolychord/__init__.py @@ -1,3 +1,3 @@ __version__ = "1.20.1" from pypolychord.settings import PolyChordSettings -from pypolychord.polychord import run_polychord +from pypolychord.polychord import run_polychord, run From 7c06066bfd592c326f7c0c90c0a987e0116922e3 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:31:06 +0100 Subject: [PATCH 21/87] update nDerived, prior and dumper to kwargs --- pypolychord/polychord.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index e148bcda..ddb0bd0c 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -360,8 +360,7 @@ def write(var): ### new interface ### -def run(loglikelihood, nDims, nDerived, - prior=default_prior, dumper=default_dumper, **kwargs): +def run(loglikelihood, nDims, **kwargs): """ Runs PolyChord. @@ -408,15 +407,12 @@ def run(loglikelihood, nDims, nDerived, nDims: int Dimensionality of the model, i.e. the number of physical parameters. + Keyword arguments + ----------------- + nDerived: int The number of derived parameters (can be 0). - settings: settings.Settings - Object containing polychord settings - - Optional Arguments - ------------------ - prior: function This function converts from the unit hypercube to the physical parameters. @@ -451,8 +447,6 @@ def run(loglikelihood, nDims, nDerived, logZerr: float The current log-evidence error estimate - Keyword arguments - ----------------- nlive: int (Default: nDims*25) The number of live points. @@ -663,6 +657,9 @@ def run(loglikelihood, nDims, nDerived, except ImportError: rank = 0 + kwargs.setdefault("nDerived", 0) + kwargs.setdefault("prior", default_prior) + kwargs.setdefault("dumper", default_dumper) kwargs.setdefault('nlive', nDims*25), kwargs.setdefault('num_repeats', nDims*5), kwargs.setdefault('nprior', -1), @@ -708,7 +705,7 @@ def run(loglikelihood, nDims, nDerived, pass if "cube_samples" in kwargs: - make_resume_file_kwargs_interface(loglikelihood, prior, **kwargs) + make_resume_file_kwargs_interface(loglikelihood, kwargs["prior"], **kwargs) read_resume = kwargs["read_resume"] kwargs["read_resume"] = True @@ -717,15 +714,15 @@ def wrap_loglikelihood(theta, phi): return logL def wrap_prior(cube, theta): - theta[:] = prior(cube) + theta[:] = kwargs["prior"](cube) # Run polychord from module library _pypolychord.run(wrap_loglikelihood, wrap_prior, - dumper, - nDims, - nDerived, + kwargs["dumper"], + kwargs["nDims"], + kwargs["nDerived"], kwargs['nlive'], kwargs['num_repeats'], kwargs['nprior'], @@ -771,7 +768,7 @@ def wrap_prior(cube, theta): -def make_resume_file_kwargs_interface(loglikelihood, prior, **kwargs): +def make_resume_file_kwargs_interface(loglikelihood, **kwargs): import fortranformat as ff resume_filename = os.path.join(kwargs["base_dir"], kwargs["file_root"])+".resume" @@ -789,7 +786,7 @@ def make_resume_file_kwargs_interface(loglikelihood, prior, **kwargs): logL_birth = kwargs["logzero"] for i in np.array_split(np.arange(len(kwargs["cube_samples"])), size)[rank]: cube = kwargs["cube_samples"][i] - theta = prior(cube) + theta = kwargs["prior"](cube) logL, derived = loglikelihood(theta) nDims = len(theta) nDerived = len(derived) From 7b79d66cfbaf3481c1590061046ceef74ca3c63c Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:34:45 +0100 Subject: [PATCH 22/87] nDims is not a kwarg --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index ddb0bd0c..2f520b9c 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -721,7 +721,7 @@ def wrap_prior(cube, theta): _pypolychord.run(wrap_loglikelihood, wrap_prior, kwargs["dumper"], - kwargs["nDims"], + nDims, kwargs["nDerived"], kwargs['nlive'], kwargs['num_repeats'], From 969894816909bab7259265a80df9a8613e2c50db Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 10:35:02 +0100 Subject: [PATCH 23/87] add nDerived, prior and dumper to kwargs --- run_pypolychord.ipynb | 5 ++++- run_pypolychord.py | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 525cc0dd..ffb77ea5 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -108,6 +108,9 @@ "outputs": [], "source": [ "kwargs = {\n", + " \"nDerived\": nDerived,\n", + " \"prior\": prior,\n", + " \"dumper\": dumper,\n", " \"file_root\": 'gaussian',\n", " \"nlive\": 200,\n", " \"do_clustering\": True,\n", @@ -166,7 +169,7 @@ } ], "source": [ - "output = pypolychord.run(likelihood, nDims, nDerived, prior, dumper, **kwargs)" + "output = pypolychord.run(likelihood, nDims, **kwargs)" ] }, { diff --git a/run_pypolychord.py b/run_pypolychord.py index ade09200..588d0863 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -44,7 +44,10 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Initialise the settings kwargs = { - "file_root": 'gaussian', + "nDerived": nDerived, + "prior": prior, + "dumper": dumper, + "file_root": 'gaussian', "nlive": 200, "do_clustering": True, "read_resume": False, @@ -53,7 +56,7 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Run PolyChord -output = pypolychord.run(likelihood, nDims, nDerived, prior, dumper, **kwargs) +output = pypolychord.run(likelihood, nDims, **kwargs) #| Make an anesthetic plot (could also use getdist) From 51170929b14d6bed2bf5f2bb4b9758ee83c972a1 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 15:32:02 +0100 Subject: [PATCH 24/87] use default_kwargs to set the defaults, then use this to see if kwargs has any excess keys --- pypolychord/polychord.py | 77 ++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 2f520b9c..2a489915 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -657,40 +657,50 @@ def run(loglikelihood, nDims, **kwargs): except ImportError: rank = 0 - kwargs.setdefault("nDerived", 0) - kwargs.setdefault("prior", default_prior) - kwargs.setdefault("dumper", default_dumper) - kwargs.setdefault('nlive', nDims*25), - kwargs.setdefault('num_repeats', nDims*5), - kwargs.setdefault('nprior', -1), - kwargs.setdefault('nfail', -1), - kwargs.setdefault('do_clustering', True), - kwargs.setdefault('feedback', 1), - kwargs.setdefault('precision_criterion', 0.001), - kwargs.setdefault('logzero', -1e30), - kwargs.setdefault('max_ndead', -1), - kwargs.setdefault('boost_posterior', 0.0), - kwargs.setdefault('posteriors', True), - kwargs.setdefault('equals', True), - kwargs.setdefault('cluster_posteriors', True), - kwargs.setdefault('write_resume', True), - kwargs.setdefault('write_paramnames', False), - kwargs.setdefault('read_resume', True), - kwargs.setdefault('write_stats', True), - kwargs.setdefault('write_live', True), - kwargs.setdefault('write_dead', True), - kwargs.setdefault('write_prior', True), - kwargs.setdefault('maximise', False), - kwargs.setdefault('compression_factor', np.exp(-1)), - kwargs.setdefault('synchronous', True), - kwargs.setdefault('base_dir', 'chains'), - kwargs.setdefault('file_root', 'test'), - kwargs.setdefault("grade_dims", [nDims]) + default_kwargs = { + "nDerived": 0, + "prior": default_prior, + "dumper": default_dumper, + 'nlive': nDims*25, + 'num_repeats': nDims*5, + 'nprior': -1, + 'nfail': -1, + 'do_clustering': True, + 'feedback': 1, + 'precision_criterion': 0.001, + 'logzero': -1e30, + 'max_ndead': -1, + 'boost_posterior': 0.0, + 'posteriors': True, + 'equals': True, + 'cluster_posteriors': True, + 'write_resume': True, + 'write_paramnames': False, + 'read_resume': True, + 'write_stats': True, + 'write_live': True, + 'write_dead': True, + 'write_prior': True, + 'maximise': False, + 'compression_factor': np.exp(-1), + 'synchronous': True, + 'base_dir': 'chains', + 'file_root': 'test', + "grade_dims": [nDims], + "grade_frac": [1.0]*len(default_kwargs["grade_dims"]), + "nlives": {}, + "seed": -1, + "paramnames": [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs("nDerived"))], + } + + + if not set(kwargs.keys()) <= set(default_kwargs.keys()): + raise TypeError(f"{__name__} got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") + default_kwargs.update(kwargs) + kwargs = default_kwargs + kwargs["grade_dims"] = [int(d) for d in list(kwargs["grade_dims"])] - kwargs.setdefault("grade_frac", [1.0]*len(kwargs["grade_dims"])) - kwargs.setdefault("nlives", {}) kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs["nlives"].items()} - kwargs.setdefault("seed", -1), try: if rank == 0: @@ -758,8 +768,7 @@ def wrap_prior(cube, theta): kwargs["read_resume"] = read_resume polychord_output = PolyChordOutput(kwargs["base_dir"], kwargs["file_root"]) - if "paramnames" in kwargs: - polychord_output.make_paramnames_files(kwargs["paramnames"]) + polychord_output.make_paramnames_files(kwargs["paramnames"]) try: import anesthetic return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) From aa64442afe32e64afc043ebe8fdc11b03a1c49fd Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 19:24:02 +0100 Subject: [PATCH 25/87] separate default_kwargs that refer to other default_kwargs --- pypolychord/polychord.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 2a489915..441c75d8 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -687,11 +687,11 @@ def run(loglikelihood, nDims, **kwargs): 'base_dir': 'chains', 'file_root': 'test', "grade_dims": [nDims], - "grade_frac": [1.0]*len(default_kwargs["grade_dims"]), "nlives": {}, "seed": -1, - "paramnames": [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs("nDerived"))], } + default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]), + default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs("nDerived"))], if not set(kwargs.keys()) <= set(default_kwargs.keys()): From 0f153cb371a44bb6bde3979d11b9b6858579b6e5 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 28 Jul 2022 19:57:40 +0100 Subject: [PATCH 26/87] wrong brackets - think it's time to leave this for a moment --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 441c75d8..663987b2 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -691,7 +691,7 @@ def run(loglikelihood, nDims, **kwargs): "seed": -1, } default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]), - default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs("nDerived"))], + default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs["nDerived"])], if not set(kwargs.keys()) <= set(default_kwargs.keys()): From 1ae0da39ade683a235cf9fa358fed03cf8d25c97 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 10:10:32 +0100 Subject: [PATCH 27/87] removed accidental commas --- pypolychord/polychord.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 663987b2..8bd62fc7 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -690,8 +690,8 @@ def run(loglikelihood, nDims, **kwargs): "nlives": {}, "seed": -1, } - default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]), - default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs["nDerived"])], + default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]) + default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs["nDerived"])] if not set(kwargs.keys()) <= set(default_kwargs.keys()): From 477c7964f097195035eb8a87171d66ea170cb12d Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 10:26:09 +0100 Subject: [PATCH 28/87] pass kwargs directly rather than creating dict --- run_pypolychord.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 588d0863..54aa0312 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -41,22 +41,21 @@ def dumper(live, dead, logweights, logZ, logZerr): paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -#| Initialise the settings - -kwargs = { - "nDerived": nDerived, - "prior": prior, - "dumper": dumper, - "file_root": 'gaussian', - "nlive": 200, - "do_clustering": True, - "read_resume": False, - "paramnames": paramnames, -} #| Run PolyChord -output = pypolychord.run(likelihood, nDims, **kwargs) +output = pypolychord.run( + likelihood, + nDims, + nDerived=nDerived, + prior=prior, + dumper=dumper, + file_root='gaussian', + nlive=200, + do_clustering=True, + read_resume=False, + paramnames=paramnames, +) #| Make an anesthetic plot (could also use getdist) From 0b5e998814500c89c0de8627ee4f3a62aa1a148c Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 10:28:56 +0100 Subject: [PATCH 29/87] added anesthetic to install_requires --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 17b939eb..dda4d0ed 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy'], + install_requires=['numpy','scipy',"anesthetic"], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From 25eb1308b688808e0a8def9bd547d7e8db566cf5 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 10:31:06 +0100 Subject: [PATCH 30/87] anesthetic now assumed to be installed, so new interface always returns an anesthetic output --- pypolychord/polychord.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 8bd62fc7..775f1ae4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -2,6 +2,7 @@ import os import _pypolychord import numpy as np +import anesthetic def default_prior(cube): @@ -769,11 +770,7 @@ def wrap_prior(cube, theta): polychord_output = PolyChordOutput(kwargs["base_dir"], kwargs["file_root"]) polychord_output.make_paramnames_files(kwargs["paramnames"]) - try: - import anesthetic - return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) - except ImportError: - return polychord_output + return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) From 4b4f29fa42ed94946224adcaf1ff5d1deabfed3d Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 10:42:40 +0100 Subject: [PATCH 31/87] tidy up plotting to just use anesthetic output --- run_pypolychord.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 54aa0312..64acd562 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -58,21 +58,7 @@ def dumper(live, dead, logweights, logZ, logZerr): ) -#| Make an anesthetic plot (could also use getdist) -try: - from anesthetic import NestedSamples - samples = NestedSamples(root= "chains/" + kwargs["file_root"]) - fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) - fig.savefig('posterior.pdf') +#| Make an anesthetic plot -except ImportError: - try: - import getdist.plots - posterior = output.posterior - g = getdist.plots.getSubplotPlotter() - g.triangle_plot(posterior, filled=True) - g.export('posterior.pdf') - except ImportError: - print("Install matplotlib and getdist for plotting examples") - - print("Install anesthetic or getdist for for plotting examples") +fig, axes = output.plot_2d(['p0','p1','p2','p3','r']) +fig.savefig('posterior.pdf') \ No newline at end of file From 8da3f8b6e198de936f2d4206a1f2bfe9ee2bac42 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 11:09:46 +0100 Subject: [PATCH 32/87] simplified to _make_resume_file(), plus wrapper _legacy_make_resume_file which translates from a settings object --- pypolychord/polychord.py | 148 +++------------------------------------ 1 file changed, 8 insertions(+), 140 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 775f1ae4..f36e85dd 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -165,7 +165,7 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, pass if settings.cube_samples is not None: - make_resume_file(settings, loglikelihood, prior) + _legacy_make_resume_file(settings, loglikelihood, prior) read_resume = settings.read_resume settings.read_resume=True @@ -221,143 +221,6 @@ def wrap_prior(cube, theta): return PolyChordOutput(settings.base_dir, settings.file_root) -def make_resume_file(settings, loglikelihood, prior): - import fortranformat as ff - resume_filename = os.path.join(settings.base_dir, - settings.file_root)+".resume" - - try: - from mpi4py import MPI - comm = MPI.COMM_WORLD - rank = comm.Get_rank() - size = comm.Get_size() - except ImportError: - rank = 0 - size = 1 - - lives = [] - logL_birth = settings.logzero - for i in np.array_split(np.arange(len(settings.cube_samples)), size)[rank]: - cube = settings.cube_samples[i] - theta = prior(cube) - logL, derived = loglikelihood(theta) - nDims = len(theta) - nDerived = len(derived) - lives.append(np.concatenate([cube,theta,derived,[logL_birth, logL]])) - - try: - sendbuf = np.array(lives).flatten() - sendcounts = np.array(comm.gather(len(sendbuf))) - if rank == 0: - recvbuf = np.empty(sum(sendcounts), dtype=int) - else: - recvbuf = None - comm.Gatherv(sendbuf=sendbuf, recvbuf=(recvbuf, sendcounts), root=root) - - lives = np.reshape(sendbuf, (len(settings.cube_samples), len(lives[0]))) - except NameError: - lives = np.array(lives) - - if rank == 0: - with open(resume_filename,"w") as f: - def write(var): - var = np.atleast_1d(var) - if isinstance(var[0], np.integer): - fmt = '(%iI12)' % var.size - elif isinstance(var[0], np.double): - fmt = '(%iE24.15E3)' % var.size - else: - fmt = '(A)' - writer = ff.FortranRecordWriter(fmt) - f.write(writer.write(var) + '\n') - - write('=== Number of dimensions ===') - write(nDims) - write('=== Number of derived parameters ===') - write(nDerived) - write('=== Number of dead points/iterations ===') - write(0) - write('=== Number of clusters ===') - write(1) - write('=== Number of dead clusters ===') - write(0) - write('=== Number of global weighted posterior points ===') - write(0) - write('=== Number of global equally weighted posterior points ===') - write(0) - write('=== Number of grades ===') - write(len(settings.grade_dims)) - write('=== positions of grades ===') - write(settings.grade_dims) - write('=== Number of repeats ===') - write(settings.num_repeats) - write('=== Number of likelihood calls ===') - write(len(lives)) - write('=== Number of live points in each cluster ===') - write(len(lives)) - write('=== Number of phantom points in each cluster ===') - write(0) - write('=== Number of weighted posterior points in each cluster ===') - write(0) - write('=== Number of equally weighted posterior points in each cluster ===') - write(0) - write('=== Minimum loglikelihood positions ===') - write(np.argmin(lives[:,-1])) - write('=== Number of weighted posterior points in each dead cluster ===') - write('=== Number of equally weighted posterior points in each dead cluster ===') - write('=== global evidence -- log() ===') - write(settings.logzero) - write('=== global evidence^2 -- log() ===') - write(settings.logzero) - write('=== posterior thin factor ===') - write(settings.boost_posterior) - write('=== local loglikelihood bounds ===') - write(lives[:,-1].min()) - write('=== local volume -- log() ===') - write(0.0) - write('=== last update volume ===') - write(0.0) - write('=== global evidence volume cross correlation -- log() ===') - write(settings.logzero) - write('=== local evidence -- log() ===') - write(settings.logzero) - write('=== local evidence^2 -- log() ===') - write(settings.logzero) - write('=== local evidence volume cross correlation -- log() ===') - write(settings.logzero) - write('=== local volume cross correlation -- log() ===') - write(0.0) - write('=== maximum log weights -- log(w_p) ===') - write(settings.logzero) - write('=== local dead evidence -- log() ===') - write('=== local dead evidence^2 -- log() ===') - write('=== maximum dead log weights -- log(w_p) ===') - write('=== covariance matrices ===') - write('---------------------------------------') - for x in np.identity(nDims): - write(x) - write('=== cholesky decompositions ===') - write('---------------------------------------') - for x in np.identity(nDims): - write(x) - write('=== live points ===') - write('---------------------------------------') - for x in lives: - write(x) - write('=== dead points ===') - write('=== logweights of dead points ===') - write('=== phantom points ===') - write('---------------------------------------') - write('=== weighted posterior points ===') - write('---------------------------------------') - write('=== dead weighted posterior points ===') - write('=== global weighted posterior points ===') - write('=== equally weighted posterior points ===') - write('---------------------------------------') - write('=== dead equally weighted posterior points ===') - write('=== global equally weighted posterior points ===') - - ### new interface ### @@ -716,7 +579,7 @@ def run(loglikelihood, nDims, **kwargs): pass if "cube_samples" in kwargs: - make_resume_file_kwargs_interface(loglikelihood, kwargs["prior"], **kwargs) + _make_resume_file(loglikelihood, kwargs["prior"], **kwargs) read_resume = kwargs["read_resume"] kwargs["read_resume"] = True @@ -774,7 +637,7 @@ def wrap_prior(cube, theta): -def make_resume_file_kwargs_interface(loglikelihood, **kwargs): +def _make_resume_file(loglikelihood, **kwargs): import fortranformat as ff resume_filename = os.path.join(kwargs["base_dir"], kwargs["file_root"])+".resume" @@ -909,3 +772,8 @@ def write(var): write('---------------------------------------') write('=== dead equally weighted posterior points ===') write('=== global equally weighted posterior points ===') + +def _legacy_make_resume_file(settings, loglikelihood, prior): + kwargs = settings.__dict__() + print(kwargs) + _make_resume_file(loglikelihood, prior = prior, **kwargs) \ No newline at end of file From 405f67daf59efc112fdc5bd70139b7e3776066df Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 11:10:41 +0100 Subject: [PATCH 33/87] added run_pypolychord_legacy_interface.py --- run_pypolychord_legacy_interface.py | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 run_pypolychord_legacy_interface.py diff --git a/run_pypolychord_legacy_interface.py b/run_pypolychord_legacy_interface.py new file mode 100644 index 00000000..52bcd707 --- /dev/null +++ b/run_pypolychord_legacy_interface.py @@ -0,0 +1,76 @@ +from numpy import pi, log, sqrt +import pypolychord +from pypolychord.settings import PolyChordSettings +from pypolychord.priors import UniformPrior +try: + from mpi4py import MPI +except ImportError: + pass + + +#| Define a four-dimensional spherical gaussian likelihood, +#| width sigma=0.1, centered on the 0 with one derived parameter. +#| The derived parameter is the squared radius + +nDims = 4 +nDerived = 1 +sigma = 0.1 + +def likelihood(theta): + """ Simple Gaussian Likelihood""" + + nDims = len(theta) + r2 = sum(theta**2) + logL = -log(2*pi*sigma*sigma)*nDims/2.0 + logL += -r2/2/sigma/sigma + + return logL, [r2] + +#| Define a box uniform prior from -1 to 1 + +def prior(hypercube): + """ Uniform prior from [-1,1]^D. """ + return UniformPrior(-1, 1)(hypercube) + +#| Optional dumper function giving run-time read access to +#| the live points, dead points, weights and evidences + +def dumper(live, dead, logweights, logZ, logZerr): + print("Last dead point:", dead[-1]) + +#| Initialise the settings + +settings = PolyChordSettings(nDims, nDerived) +settings.file_root = 'gaussian' +settings.nlive = 200 +settings.do_clustering = True +settings.read_resume = False + +#| Run PolyChord + +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) + +#| Create a paramnames file + +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] +output.make_paramnames_files(paramnames) + +#| Make an anesthetic plot (could also use getdist) +try: + from anesthetic import NestedSamples + samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root) + fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) + fig.savefig('posterior.pdf') + +except ImportError: + try: + import getdist.plots + posterior = output.posterior + g = getdist.plots.getSubplotPlotter() + g.triangle_plot(posterior, filled=True) + g.export('posterior.pdf') + except ImportError: + print("Install matplotlib and getdist for plotting examples") + + print("Install anesthetic or getdist for for plotting examples") \ No newline at end of file From 78a9ca835dc854c0c8fd939765826c3f38dddacc Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 11:15:26 +0100 Subject: [PATCH 34/87] update run() docstring to anesthetic output --- pypolychord/polychord.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index f36e85dd..08eb1406 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -465,17 +465,8 @@ def run(loglikelihood, nDims, **kwargs): Returns ------- - None. (in Python) - - All output is currently produced in the form of text files in - directory. If you would like to contribute to pypolychord and improve this, - please get in touch: - - Will Handley: wh260@cam.ac.uk - - OR if anesthetic is installed: - anesthetic.NestedSamples object + anesthetic.NestedSamples(root=/) In general the contents of is a set of getdist compatible files. @@ -508,9 +499,6 @@ def run(loglikelihood, nDims, **kwargs): .stats Final output evidence statistics - - TODO: Check for kwargs that shouldn't be there. - """ kwargs = kwargs.copy() From 78654d890223758696758f88cbe1261aa13ec488 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 17:08:57 +0100 Subject: [PATCH 35/87] removed unused sqrt from run_pypolychord.py --- run_pypolychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 64acd562..69b2fba4 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,4 +1,4 @@ -from numpy import pi, log, sqrt +from numpy import pi, log import pypolychord from pypolychord.priors import UniformPrior try: From 203f117f4979e328c5f1b4df648ef2afbb52f57d Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 17:10:17 +0100 Subject: [PATCH 36/87] updated run_pypolychord.ipynb with py2nb --- run_pypolychord.ipynb | 177 +++++++++++------------------------------- 1 file changed, 45 insertions(+), 132 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index ffb77ea5..09354c9e 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -2,17 +2,23 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, + "id": "22f3892f", "metadata": {}, "outputs": [], "source": [ - "from numpy import pi, log, sqrt\n", + "from numpy import pi, log\n", "import pypolychord\n", - "from pypolychord.priors import UniformPrior" + "from pypolychord.priors import UniformPrior\n", + "try:\n", + " from mpi4py import MPI\n", + "except ImportError:\n", + " pass" ] }, { "cell_type": "markdown", + "id": "cf2853d6", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -22,7 +28,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, + "id": "699eea21", "metadata": {}, "outputs": [], "source": [ @@ -43,6 +50,7 @@ }, { "cell_type": "markdown", + "id": "8c71a4e6", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -50,7 +58,8 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, + "id": "cf305955", "metadata": {}, "outputs": [], "source": [ @@ -61,6 +70,7 @@ }, { "cell_type": "markdown", + "id": "decdd19d", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -69,7 +79,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, + "id": "31e5565c", "metadata": {}, "outputs": [], "source": [ @@ -79,6 +90,7 @@ }, { "cell_type": "markdown", + "id": "4a566ad9", "metadata": {}, "source": [ "Create a paramnames file" @@ -86,7 +98,8 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, + "id": "51a1d935", "metadata": {}, "outputs": [], "source": [ @@ -96,31 +109,7 @@ }, { "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialise the settings" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "kwargs = {\n", - " \"nDerived\": nDerived,\n", - " \"prior\": prior,\n", - " \"dumper\": dumper,\n", - " \"file_root\": 'gaussian',\n", - " \"nlive\": 200,\n", - " \"do_clustering\": True,\n", - " \"read_resume\": False,\n", - " \"paramnames\": paramnames,\n", - "}" - ] - }, - { - "cell_type": "markdown", + "id": "4360478c", "metadata": {}, "source": [ "Run PolyChord" @@ -128,122 +117,46 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, + "id": "03fbe051", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Last dead point: [ -0.12195646 0.37405823 -0.74549651 -0.57059283 1.03613417\n", - " -107.50194883 -46.27212213]\n", - "Last dead point: [ -0.52163002 -0.06336759 -0.43192652 0.38364359 0.60985625\n", - " -41.35375964 -24.95822646]\n", - "Last dead point: [ -0.37403331 0.03735235 -0.32821553 -0.31645229 0.3491636\n", - " -19.55187971 -11.92359365]\n", - "Last dead point: [-0.11967879 0.22564894 0.34498611 0.12669236 0.20030682 -7.93394934\n", - " -4.480755 ]\n", - "Last dead point: [ 0.15947558 -0.17120628 0.21292861 -0.12136559 0.11481225 -0.97442395\n", - " -0.20602622]\n", - "Last dead point: [-0.05449124 -0.24856098 0.053324 -0.05999985 0.07119528 1.95448519\n", - " 1.97482204]\n", - "Last dead point: [ 0.11354188 0.04542128 0.04324486 0.15991583 0.04239804 -0.65204203\n", - " 3.41468411]\n", - "Last dead point: [0.03245751 0.08394396 0.12165265 0.04758947 0.0251642 4.03292303\n", - " 4.2763761 ]\n", - "Last dead point: [ 0.05227755 -0.01961902 -0.06877328 -0.08374121 0.0148602 4.42213521\n", - " 4.79157616]\n", - "Last dead point: [7.75444943e-02 3.43045989e-02 3.54071198e-03 4.29640866e-02\n", - " 9.04840349e-03 4.89388098e+00 5.08216606e+00]\n", - "Last dead point: [0.01163431 0.01282317 0.04719472 0.05280419 0.00531541 5.02547622\n", - " 5.26881552]\n", - "Last dead point: [-4.50972426e-02 2.65906632e-02 -1.05166059e-02 -2.14135125e-02\n", - " 3.30996217e-03 5.35102039e+00 5.36908813e+00]\n", - "Last dead point: [-3.64909631e-03 -1.36859157e-02 6.78829623e-03 -4.23035741e-02\n", - " 2.03629354e-03 5.11415470e+00 5.43277156e+00]\n", - "Last dead point: [-8.32623304e-03 -7.46293828e-03 -2.53157193e-02 1.98799482e-02\n", - " 1.16111959e-03 5.45981438e+00 5.47653026e+00]\n", - "Last dead point: [ 4.69883258e-03 1.81089351e-03 -4.12460698e-03 -2.22366928e-03\n", - " 4.73154508e-05 5.48264395e+00 5.53222047e+00]\n" - ] - } - ], + "outputs": [], "source": [ - "output = pypolychord.run(likelihood, nDims, **kwargs)" + "output = pypolychord.run(\n", + " likelihood,\n", + " nDims,\n", + " nDerived=nDerived,\n", + " prior=prior,\n", + " dumper=dumper,\n", + " file_root='gaussian',\n", + " nlive=200,\n", + " do_clustering=True,\n", + " read_resume=False,\n", + " paramnames=paramnames,\n", + ")" ] }, { "cell_type": "markdown", + "id": "a339183e", "metadata": {}, "source": [ - "Make an anesthetic plot (could also use getdist)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "try:\n", - " from anesthetic import NestedSamples\n", - " samples = NestedSamples(root= \"chains/\" + kwargs[\"file_root\"])\n", - " fig, axes = samples.plot_2d(['p0','p1','p2','p3','r'])\n", - " fig.savefig('posterior.pdf')\n", - "\n", - "except ImportError:\n", - " try:\n", - " import getdist.plots\n", - " posterior = output.posterior\n", - " g = getdist.plots.getSubplotPlotter()\n", - " g.triangle_plot(posterior, filled=True)\n", - " g.export('posterior.pdf')\n", - " except ImportError:\n", - " print(\"Install matplotlib and getdist for plotting examples\")\n", - "\n", - " print(\"Install anesthetic or getdist for for plotting examples\")" + "Make an anesthetic plot" ] }, { "cell_type": "code", "execution_count": null, + "id": "410eaea3", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "fig, axes = output.plot_2d(['p0','p1','p2','p3','r'])\n", + "fig.savefig('posterior.pdf')" + ] } ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.2" - } - }, + "metadata": {}, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 5 } From 0730a18b1779094a2103980f9f5499130c3850ff Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 29 Jul 2022 19:45:33 +0100 Subject: [PATCH 37/87] change requirement to anesthetic==2.0.0b11 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dda4d0ed..584ffa0c 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy',"anesthetic"], + install_requires=['numpy','scipy',"anesthetic==2.0.0b11"], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From b963910caca6492266a2441f51b0be6ab1baea70 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 3 Aug 2022 17:09:49 +0100 Subject: [PATCH 38/87] removed creation of .paramnames file from run(), added to run_pypolychord.py --- pypolychord/polychord.py | 5 +---- run_pypolychord.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 08eb1406..fccbe0f5 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -543,11 +543,10 @@ def run(loglikelihood, nDims, **kwargs): "seed": -1, } default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]) - default_kwargs["paramnames"] = [(f"p{i}", f"p_{{{i}}}") for i in range(nDims + default_kwargs["nDerived"])] if not set(kwargs.keys()) <= set(default_kwargs.keys()): - raise TypeError(f"{__name__} got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") + raise TypeError(f"run() got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") default_kwargs.update(kwargs) kwargs = default_kwargs @@ -619,8 +618,6 @@ def wrap_prior(cube, theta): if "cube_samples" in kwargs: kwargs["read_resume"] = read_resume - polychord_output = PolyChordOutput(kwargs["base_dir"], kwargs["file_root"]) - polychord_output.make_paramnames_files(kwargs["paramnames"]) return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) diff --git a/run_pypolychord.py b/run_pypolychord.py index 69b2fba4..eb6973a9 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,3 +1,4 @@ +import os from numpy import pi, log import pypolychord from pypolychord.priors import UniformPrior @@ -38,9 +39,18 @@ def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) #| Create a paramnames file + +def getdist_paramnames_file(path, paramnames): + lines = [f"{p} {q}\n" for (p, q) in paramnames] + with open(f"{path}.paramnames", "w") as f: + f.writelines(lines) + paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] +os.mkdir("chains") +getdist_paramnames_file("chains/gaussian", paramnames) + #| Run PolyChord @@ -54,10 +64,11 @@ def dumper(live, dead, logweights, logZ, logZerr): nlive=200, do_clustering=True, read_resume=False, - paramnames=paramnames, + # paramnames=paramnames, ) + #| Make an anesthetic plot fig, axes = output.plot_2d(['p0','p1','p2','p3','r']) From 805db7512eaccaf233c72d107f012dc0838c0911 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 3 Aug 2022 17:10:30 +0100 Subject: [PATCH 39/87] corrected kwargs error message --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index fccbe0f5..353be249 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -546,7 +546,7 @@ def run(loglikelihood, nDims, **kwargs): if not set(kwargs.keys()) <= set(default_kwargs.keys()): - raise TypeError(f"run() got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") + raise TypeError(f"{__name__} got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") default_kwargs.update(kwargs) kwargs = default_kwargs From 6b2ead81dbde7f32d163e1a628da0c8d8299727a Mon Sep 17 00:00:00 2001 From: Ormorod Date: Wed, 3 Aug 2022 17:18:22 +0100 Subject: [PATCH 40/87] exception handling if chains/ already exists --- run_pypolychord.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index eb6973a9..49f903d2 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -48,7 +48,10 @@ def getdist_paramnames_file(path, paramnames): paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -os.mkdir("chains") +try: + os.mkdir("chains") +except FileExistsError: + pass getdist_paramnames_file("chains/gaussian", paramnames) From 07f51e7f1299ccc5846fab9739486bc36d0ab39f Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 16 Sep 2022 18:29:03 +0100 Subject: [PATCH 41/87] update to anesthetic 2.0.0b12 --- pypolychord/polychord.py | 17 +++++++++++++++-- requirements.txt | 2 +- run_pypolychord.py | 18 ++---------------- setup.py | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 353be249..caf80267 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -462,6 +462,11 @@ def run(loglikelihood, nDims, **kwargs): x3 < 1, if one did not want to use SortedUniformPrior. Only available in Python interface. shape (:, nDims) + paramnames: List [(string,string)] + (Default: None) + Mapping of label:Tex for all parameters in order. E.g. for two physical + parameters and one derived: + paramnames = [("p0", "$p_0$"), ("p1", "$p_1$), ("d0", "$d_0$)] Returns ------- @@ -509,6 +514,8 @@ def run(loglikelihood, nDims, **kwargs): except ImportError: rank = 0 + paramnames = kwargs.pop("paramnames", None) + default_kwargs = { "nDerived": 0, "prior": default_prior, @@ -618,7 +625,13 @@ def wrap_prior(cube, theta): if "cube_samples" in kwargs: kwargs["read_resume"] = read_resume - return anesthetic.NestedSamples(root=os.path.join(kwargs["base_dir"], kwargs["file_root"])) + if paramnames is not None: + with open(os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames"), 'w') as f: + for name, latex in paramnames: + f.write('%s %s\n' % (name, latex)) + + + return anesthetic.read_chains(os.path.join(kwargs["base_dir"], kwargs["file_root"])) @@ -761,4 +774,4 @@ def write(var): def _legacy_make_resume_file(settings, loglikelihood, prior): kwargs = settings.__dict__() print(kwargs) - _make_resume_file(loglikelihood, prior = prior, **kwargs) \ No newline at end of file + _make_resume_file(loglikelihood, prior = prior, **kwargs) diff --git a/requirements.txt b/requirements.txt index 3acb9560..419c7538 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ numpy scipy -anesthetic>=2.0.0b11 +anesthetic>=2.0.0b12 diff --git a/run_pypolychord.py b/run_pypolychord.py index 49f903d2..55902f3a 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -40,21 +40,9 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Create a paramnames file -def getdist_paramnames_file(path, paramnames): - lines = [f"{p} {q}\n" for (p, q) in paramnames] - with open(f"{path}.paramnames", "w") as f: - f.writelines(lines) - paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -try: - os.mkdir("chains") -except FileExistsError: - pass -getdist_paramnames_file("chains/gaussian", paramnames) - - #| Run PolyChord output = pypolychord.run( @@ -67,12 +55,10 @@ def getdist_paramnames_file(path, paramnames): nlive=200, do_clustering=True, read_resume=False, - # paramnames=paramnames, + paramnames=paramnames, ) - - #| Make an anesthetic plot fig, axes = output.plot_2d(['p0','p1','p2','p3','r']) -fig.savefig('posterior.pdf') \ No newline at end of file +fig.savefig('posterior.pdf') diff --git a/setup.py b/setup.py index 584ffa0c..a78b032a 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy',"anesthetic==2.0.0b11"], + install_requires=['numpy','scipy',"anesthetic==2.0.0b12"], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From 72bdea6612cdb7c0bc6d30ac50818ed91073b603 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Fri, 16 Sep 2022 18:47:59 +0100 Subject: [PATCH 42/87] rather than repeating PolyChordOutput.make_paramnames_file(), make it a static method and reuse it --- pypolychord/output.py | 3 ++- pypolychord/polychord.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pypolychord/output.py b/pypolychord/output.py index a3efd4ce..545a936d 100644 --- a/pypolychord/output.py +++ b/pypolychord/output.py @@ -170,7 +170,8 @@ def make_paramnames_files(self, paramnames): self._create_pandas_table(paramnames = paramnames) - def make_paramnames_file(self, paramnames, filename): + @staticmethod + def make_paramnames_file(paramnames, filename): with open(filename, 'w') as f: for name, latex in paramnames: f.write('%s %s\n' % (name, latex)) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index caf80267..b464a415 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -626,9 +626,14 @@ def wrap_prior(cube, theta): kwargs["read_resume"] = read_resume if paramnames is not None: - with open(os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames"), 'w') as f: - for name, latex in paramnames: - f.write('%s %s\n' % (name, latex)) + # with open(os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames"), 'w') as f: + # for paramname in paramnames: + # if 2 == len(paramname): + # f.write('%s %s\n' % paramname)) + # else: + # f.write('%s\n' % paramname) + PolyChordOutput.make_paramnames_file(paramnames, os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames")) + return anesthetic.read_chains(os.path.join(kwargs["base_dir"], kwargs["file_root"])) From 25198cd61392de1757896b1d710c6e42aa3338ae Mon Sep 17 00:00:00 2001 From: Ormorod Date: Sun, 18 Sep 2022 13:57:27 +0100 Subject: [PATCH 43/87] changed setup.py to point to github anesthetic as 2.0.0b12 hasn't been released --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a78b032a..ca890ee9 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy',"anesthetic==2.0.0b12"], + install_requires=['numpy','scipy',"anesthetic @ git+https://github.com/williamjameshandley/anesthetic@master"], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From 52d6592ad6b8a369d0d60d5f7bbd586169a39deb Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 22 Sep 2022 12:43:35 +0100 Subject: [PATCH 44/87] remove unecessary os import --- run_pypolychord.py | 1 - 1 file changed, 1 deletion(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 55902f3a..7b53bc8f 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,4 +1,3 @@ -import os from numpy import pi, log import pypolychord from pypolychord.priors import UniformPrior From 5a750faefc7738a7a6a6db8bbe2de49875f16d43 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 22 Sep 2022 13:50:54 +0100 Subject: [PATCH 45/87] removed commented out/empty lines around paramname creation --- pypolychord/polychord.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index b464a415..f907a2a9 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -557,8 +557,6 @@ def run(loglikelihood, nDims, **kwargs): default_kwargs.update(kwargs) kwargs = default_kwargs - kwargs["grade_dims"] = [int(d) for d in list(kwargs["grade_dims"])] - kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs["nlives"].items()} try: if rank == 0: @@ -584,6 +582,8 @@ def wrap_loglikelihood(theta, phi): def wrap_prior(cube, theta): theta[:] = kwargs["prior"](cube) + kwargs["grade_dims"] = [int(d) for d in list(kwargs["grade_dims"])] + kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs["nlives"].items()} # Run polychord from module library _pypolychord.run(wrap_loglikelihood, @@ -626,16 +626,8 @@ def wrap_prior(cube, theta): kwargs["read_resume"] = read_resume if paramnames is not None: - # with open(os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames"), 'w') as f: - # for paramname in paramnames: - # if 2 == len(paramname): - # f.write('%s %s\n' % paramname)) - # else: - # f.write('%s\n' % paramname) PolyChordOutput.make_paramnames_file(paramnames, os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames")) - - return anesthetic.read_chains(os.path.join(kwargs["base_dir"], kwargs["file_root"])) From 7f67a2a5aadaf4c0fcb5fd10d733aaf1a72acb1f Mon Sep 17 00:00:00 2001 From: Ormorod Date: Mon, 26 Sep 2022 15:46:19 +0100 Subject: [PATCH 46/87] removed unnecessary kwargs = kwargs.copy() --- pypolychord/polychord.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index f907a2a9..d0d5cde1 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -505,7 +505,6 @@ def run(loglikelihood, nDims, **kwargs): Final output evidence statistics """ - kwargs = kwargs.copy() try: from mpi4py import MPI From 23a766f514d3e6bf4a6037224e5eb18b110268ed Mon Sep 17 00:00:00 2001 From: Ormorod Date: Mon, 3 Oct 2022 16:17:53 +0100 Subject: [PATCH 47/87] double quotes to be more civilised --- pypolychord/polychord.py | 100 +++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index d0d5cde1..846c2dd3 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -519,31 +519,31 @@ def run(loglikelihood, nDims, **kwargs): "nDerived": 0, "prior": default_prior, "dumper": default_dumper, - 'nlive': nDims*25, - 'num_repeats': nDims*5, - 'nprior': -1, - 'nfail': -1, - 'do_clustering': True, - 'feedback': 1, - 'precision_criterion': 0.001, - 'logzero': -1e30, - 'max_ndead': -1, - 'boost_posterior': 0.0, - 'posteriors': True, - 'equals': True, - 'cluster_posteriors': True, - 'write_resume': True, - 'write_paramnames': False, - 'read_resume': True, - 'write_stats': True, - 'write_live': True, - 'write_dead': True, - 'write_prior': True, - 'maximise': False, - 'compression_factor': np.exp(-1), - 'synchronous': True, - 'base_dir': 'chains', - 'file_root': 'test', + "nlive": nDims*25, + "num_repeats": nDims*5, + "nprior": -1, + "nfail": -1, + "do_clustering": True, + "feedback": 1, + "precision_criterion": 0.001, + "logzero": -1e30, + "max_ndead": -1, + "boost_posterior": 0.0, + "posteriors": True, + "equals": True, + "cluster_posteriors": True, + "write_resume": True, + "write_paramnames": False, + "read_resume": True, + "write_stats": True, + "write_live": True, + "write_dead": True, + "write_prior": True, + "maximise": False, + "compression_factor": np.exp(-1), + "synchronous": True, + "base_dir": "chains", + "file_root": "test", "grade_dims": [nDims], "nlives": {}, "seed": -1, @@ -590,31 +590,31 @@ def wrap_prior(cube, theta): kwargs["dumper"], nDims, kwargs["nDerived"], - kwargs['nlive'], - kwargs['num_repeats'], - kwargs['nprior'], - kwargs['nfail'], - kwargs['do_clustering'], - kwargs['feedback'], - kwargs['precision_criterion'], - kwargs['logzero'], - kwargs['max_ndead'], - kwargs['boost_posterior'], - kwargs['posteriors'], - kwargs['equals'], - kwargs['cluster_posteriors'], - kwargs['write_resume'], - kwargs['write_paramnames'], - kwargs['read_resume'], - kwargs['write_stats'], - kwargs['write_live'], - kwargs['write_dead'], - kwargs['write_prior'], - kwargs['maximise'], - kwargs['compression_factor'], - kwargs['synchronous'], - kwargs['base_dir'], - kwargs['file_root'], + kwargs["nlive"], + kwargs["num_repeats"], + kwargs["nprior"], + kwargs["nfail"], + kwargs["do_clustering"], + kwargs["feedback"], + kwargs["precision_criterion"], + kwargs["logzero"], + kwargs["max_ndead"], + kwargs["boost_posterior"], + kwargs["posteriors"], + kwargs["equals"], + kwargs["cluster_posteriors"], + kwargs["write_resume"], + kwargs["write_paramnames"], + kwargs["read_resume"], + kwargs["write_stats"], + kwargs["write_live"], + kwargs["write_dead"], + kwargs["write_prior"], + kwargs["maximise"], + kwargs["compression_factor"], + kwargs["synchronous"], + kwargs["base_dir"], + kwargs["file_root"], kwargs["grade_frac"], kwargs["grade_dims"], kwargs["nlives"], From 77fe88511ea2bcbd60a0db6fb3f6e2197c115a68 Mon Sep 17 00:00:00 2001 From: Ormorod Date: Thu, 13 Oct 2022 11:52:30 +0100 Subject: [PATCH 48/87] change to single quotes to make Lucas happy --- pypolychord/polychord.py | 192 +++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 846c2dd3..837d1b88 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -513,42 +513,42 @@ def run(loglikelihood, nDims, **kwargs): except ImportError: rank = 0 - paramnames = kwargs.pop("paramnames", None) + paramnames = kwargs.pop('paramnames', None) default_kwargs = { - "nDerived": 0, - "prior": default_prior, - "dumper": default_dumper, - "nlive": nDims*25, - "num_repeats": nDims*5, - "nprior": -1, - "nfail": -1, - "do_clustering": True, - "feedback": 1, - "precision_criterion": 0.001, - "logzero": -1e30, - "max_ndead": -1, - "boost_posterior": 0.0, - "posteriors": True, - "equals": True, - "cluster_posteriors": True, - "write_resume": True, - "write_paramnames": False, - "read_resume": True, - "write_stats": True, - "write_live": True, - "write_dead": True, - "write_prior": True, - "maximise": False, - "compression_factor": np.exp(-1), - "synchronous": True, - "base_dir": "chains", - "file_root": "test", - "grade_dims": [nDims], - "nlives": {}, - "seed": -1, + 'nDerived': 0, + 'prior': default_prior, + 'dumper': default_dumper, + 'nlive': nDims*25, + 'num_repeats': nDims*5, + 'nprior': -1, + 'nfail': -1, + 'do_clustering': True, + 'feedback': 1, + 'precision_criterion': 0.001, + 'logzero': -1e30, + 'max_ndead': -1, + 'boost_posterior': 0.0, + 'posteriors': True, + 'equals': True, + 'cluster_posteriors': True, + 'write_resume': True, + 'write_paramnames': False, + 'read_resume': True, + 'write_stats': True, + 'write_live': True, + 'write_dead': True, + 'write_prior': True, + 'maximise': False, + 'compression_factor': np.exp(-1), + 'synchronous': True, + 'base_dir': 'chains', + 'file_root': 'test', + 'grade_dims': [nDims], + 'nlives': {}, + 'seed': -1, } - default_kwargs["grade_frac"] = [1.0]*len(default_kwargs["grade_dims"]) + default_kwargs['grade_frac'] = [1.0]*len(default_kwargs['grade_dims']) if not set(kwargs.keys()) <= set(default_kwargs.keys()): @@ -559,82 +559,82 @@ def run(loglikelihood, nDims, **kwargs): try: if rank == 0: - os.makedirs(kwargs["base_dir"]) + os.makedirs(kwargs['base_dir']) except OSError: pass try: if rank == 0: - os.makedirs(os.path.join(kwargs["base_dir"], "clusters")) + os.makedirs(os.path.join(kwargs['base_dir'], 'clusters')) except OSError: pass - if "cube_samples" in kwargs: - _make_resume_file(loglikelihood, kwargs["prior"], **kwargs) - read_resume = kwargs["read_resume"] - kwargs["read_resume"] = True + if 'cube_samples' in kwargs: + _make_resume_file(loglikelihood, kwargs['prior'], **kwargs) + read_resume = kwargs['read_resume'] + kwargs['read_resume'] = True def wrap_loglikelihood(theta, phi): logL, phi[:] = loglikelihood(theta) return logL def wrap_prior(cube, theta): - theta[:] = kwargs["prior"](cube) + theta[:] = kwargs['prior'](cube) - kwargs["grade_dims"] = [int(d) for d in list(kwargs["grade_dims"])] - kwargs["nlives"] = {float(logL):int(nlive) for logL, nlive in kwargs["nlives"].items()} + kwargs['grade_dims'] = [int(d) for d in list(kwargs['grade_dims'])] + kwargs['nlives'] = {float(logL):int(nlive) for logL, nlive in kwargs['nlives'].items()} # Run polychord from module library _pypolychord.run(wrap_loglikelihood, wrap_prior, - kwargs["dumper"], + kwargs['dumper'], nDims, - kwargs["nDerived"], - kwargs["nlive"], - kwargs["num_repeats"], - kwargs["nprior"], - kwargs["nfail"], - kwargs["do_clustering"], - kwargs["feedback"], - kwargs["precision_criterion"], - kwargs["logzero"], - kwargs["max_ndead"], - kwargs["boost_posterior"], - kwargs["posteriors"], - kwargs["equals"], - kwargs["cluster_posteriors"], - kwargs["write_resume"], - kwargs["write_paramnames"], - kwargs["read_resume"], - kwargs["write_stats"], - kwargs["write_live"], - kwargs["write_dead"], - kwargs["write_prior"], - kwargs["maximise"], - kwargs["compression_factor"], - kwargs["synchronous"], - kwargs["base_dir"], - kwargs["file_root"], - kwargs["grade_frac"], - kwargs["grade_dims"], - kwargs["nlives"], - kwargs["seed"], + kwargs['nDerived'], + kwargs['nlive'], + kwargs['num_repeats'], + kwargs['nprior'], + kwargs['nfail'], + kwargs['do_clustering'], + kwargs['feedback'], + kwargs['precision_criterion'], + kwargs['logzero'], + kwargs['max_ndead'], + kwargs['boost_posterior'], + kwargs['posteriors'], + kwargs['equals'], + kwargs['cluster_posteriors'], + kwargs['write_resume'], + kwargs['write_paramnames'], + kwargs['read_resume'], + kwargs['write_stats'], + kwargs['write_live'], + kwargs['write_dead'], + kwargs['write_prior'], + kwargs['maximise'], + kwargs['compression_factor'], + kwargs['synchronous'], + kwargs['base_dir'], + kwargs['file_root'], + kwargs['grade_frac'], + kwargs['grade_dims'], + kwargs['nlives'], + kwargs['seed'], ) - if "cube_samples" in kwargs: - kwargs["read_resume"] = read_resume + if 'cube_samples' in kwargs: + kwargs['read_resume'] = read_resume if paramnames is not None: - PolyChordOutput.make_paramnames_file(paramnames, os.path.join(kwargs["base_dir"], kwargs["file_root"]+".paramnames")) + PolyChordOutput.make_paramnames_file(paramnames, os.path.join(kwargs['base_dir'], kwargs['file_root']+".paramnames")) - return anesthetic.read_chains(os.path.join(kwargs["base_dir"], kwargs["file_root"])) + return anesthetic.read_chains(os.path.join(kwargs['base_dir'], kwargs['file_root'])) def _make_resume_file(loglikelihood, **kwargs): import fortranformat as ff - resume_filename = os.path.join(kwargs["base_dir"], - kwargs["file_root"])+".resume" + resume_filename = os.path.join(kwargs['base_dir'], + kwargs['file_root'])+".resume" try: from mpi4py import MPI @@ -646,10 +646,10 @@ def _make_resume_file(loglikelihood, **kwargs): size = 1 lives = [] - logL_birth = kwargs["logzero"] - for i in np.array_split(np.arange(len(kwargs["cube_samples"])), size)[rank]: - cube = kwargs["cube_samples"][i] - theta = kwargs["prior"](cube) + logL_birth = kwargs['logzero'] + for i in np.array_split(np.arange(len(kwargs['cube_samples'])), size)[rank]: + cube = kwargs['cube_samples'][i] + theta = kwargs['prior'](cube) logL, derived = loglikelihood(theta) nDims = len(theta) nDerived = len(derived) @@ -664,12 +664,12 @@ def _make_resume_file(loglikelihood, **kwargs): recvbuf = None comm.Gatherv(sendbuf=sendbuf, recvbuf=(recvbuf, sendcounts), root=root) - lives = np.reshape(sendbuf, (len(kwargs["cube_samples"]), len(lives[0]))) + lives = np.reshape(sendbuf, (len(kwargs['cube_samples']), len(lives[0]))) except NameError: lives = np.array(lives) if rank == 0: - with open(resume_filename,"w") as f: + with open(resume_filename,'w') as f: def write(var): var = np.atleast_1d(var) if isinstance(var[0], np.integer): @@ -696,11 +696,11 @@ def write(var): write('=== Number of global equally weighted posterior points ===') write(0) write('=== Number of grades ===') - write(len(kwargs["grade_dims"])) + write(len(kwargs['grade_dims'])) write('=== positions of grades ===') - write(kwargs["grade_dims"]) + write(kwargs['grade_dims']) write('=== Number of repeats ===') - write(kwargs["num_repeats"]) + write(kwargs['num_repeats']) write('=== Number of likelihood calls ===') write(len(lives)) write('=== Number of live points in each cluster ===') @@ -716,11 +716,11 @@ def write(var): write('=== Number of weighted posterior points in each dead cluster ===') write('=== Number of equally weighted posterior points in each dead cluster ===') write('=== global evidence -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== global evidence^2 -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== posterior thin factor ===') - write(kwargs["boost_posterior"]) + write(kwargs['boost_posterior']) write('=== local loglikelihood bounds ===') write(lives[:,-1].min()) write('=== local volume -- log() ===') @@ -728,17 +728,17 @@ def write(var): write('=== last update volume ===') write(0.0) write('=== global evidence volume cross correlation -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== local evidence -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== local evidence^2 -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== local evidence volume cross correlation -- log() ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== local volume cross correlation -- log() ===') write(0.0) write('=== maximum log weights -- log(w_p) ===') - write(kwargs["logzero"]) + write(kwargs['logzero']) write('=== local dead evidence -- log() ===') write('=== local dead evidence^2 -- log() ===') write('=== maximum dead log weights -- log(w_p) ===') From 6c650adb0d051da1d4e9be3dc73ec1cc536959ea Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 12:18:40 +0000 Subject: [PATCH 49/87] update for more recent anesthetic --- run_pypolychord.ipynb | 30 ++++++++++++++++-------------- run_pypolychord.py | 4 +++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index dc4a5e45..7bb496e3 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -3,11 +3,12 @@ { "cell_type": "code", "execution_count": null, - "id": "e64c9d3a", + "id": "19cee382", "metadata": {}, "outputs": [], "source": [ "from numpy import pi, log\n", + "import anesthetic as ac\n", "import pypolychord\n", "from pypolychord.priors import UniformPrior\n", "try:\n", @@ -18,7 +19,7 @@ }, { "cell_type": "markdown", - "id": "831b6c74", + "id": "86c075cf", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +30,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34a9898a", + "id": "43c8c687", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "dbac2675", + "id": "3499789d", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e07d1653", + "id": "410ef669", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +71,7 @@ }, { "cell_type": "markdown", - "id": "fd470ff9", + "id": "85d8c71c", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8f372fea", + "id": "bd8940ca", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +91,7 @@ }, { "cell_type": "markdown", - "id": "d2626bea", + "id": "5e29f3fc", "metadata": {}, "source": [ "Create a paramnames file" @@ -99,7 +100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58ab7dfe", + "id": "a2bc3f14", "metadata": {}, "outputs": [], "source": [ @@ -109,7 +110,7 @@ }, { "cell_type": "markdown", - "id": "134967f4", + "id": "a4357358", "metadata": {}, "source": [ "Run PolyChord" @@ -118,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4d337450", + "id": "2937a7e4", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +139,7 @@ }, { "cell_type": "markdown", - "id": "8af8de25", + "id": "489dd2ef", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -147,11 +148,12 @@ { "cell_type": "code", "execution_count": null, - "id": "15dcfd5d", + "id": "49a3962b", "metadata": {}, "outputs": [], "source": [ - "fig, axes = output.plot_2d(['p0','p1','p2','p3','r'])\n", + "fig, ax = ac.make_2d_axes(['p0','p1','p2','p3','r'])\n", + "output.plot_2d(ax)\n", "fig.savefig('posterior.pdf')" ] } diff --git a/run_pypolychord.py b/run_pypolychord.py index 7b53bc8f..6cbce6de 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,4 +1,5 @@ from numpy import pi, log +import anesthetic as ac import pypolychord from pypolychord.priors import UniformPrior try: @@ -59,5 +60,6 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot -fig, axes = output.plot_2d(['p0','p1','p2','p3','r']) +fig, ax = ac.make_2d_axes(['p0','p1','p2','p3','r']) +output.plot_2d(ax) fig.savefig('posterior.pdf') From 02c3b73e6742c259678c3d2b12dc7e67f463aa98 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 12:20:20 +0000 Subject: [PATCH 50/87] line spacing and remove todo (see pr 110 --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index b8916710..a06651aa 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -12,6 +12,7 @@ def default_prior(cube): def default_dumper(live, dead, logweights, logZ, logZerr): pass + def run_polychord(loglikelihood, nDims, nDerived, settings, prior=default_prior, dumper=default_dumper): """ @@ -266,7 +267,6 @@ def run(loglikelihood, nDims, **kwargs): ------- logL: float log-likelihood - TODO: what about []? I don't think it can be omitted from the return of loglikelihood. nDims: int Dimensionality of the model, i.e. the number of physical parameters. From aed15cf77457b5c4326ce589b54406add97d251a Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 15:19:26 +0000 Subject: [PATCH 51/87] get anesthetic from pypi instead of gh --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eb37e8d7..fac6477d 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy',"anesthetic @ git+https://github.com/williamjameshandley/anesthetic@master"], + install_requires=['numpy','scipy',"anesthetic>=2.0.0"], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From bd763d4745b3a1750d035033fddbceed7c36bded Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 15:31:45 +0000 Subject: [PATCH 52/87] might as well do some formatting --- pypolychord/polychord.py | 88 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index ca5a5a73..312d5fa4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -314,7 +314,7 @@ def run(loglikelihood, nDims, **kwargs): The current log-evidence estimate logZerr: float The current log-evidence error estimate - + nlive: int (Default: nDims*25) The number of live points. @@ -434,7 +434,7 @@ def run(loglikelihood, nDims, **kwargs): grade_frac : List[float] (Default: [1]) - The amount of time to spend in each speed. + The amount of time to spend in each speed. If any of grade_frac are <= 1, then polychord will time each sub-speed, and then choose num_repeats for the number of slowest repeats, and spend the proportion of time indicated by grade_frac. Otherwise this @@ -474,7 +474,7 @@ def run(loglikelihood, nDims, **kwargs): Returns ------- - + anesthetic.NestedSamples(root=/) In general the contents of is a set of getdist compatible files. @@ -554,19 +554,17 @@ def run(loglikelihood, nDims, **kwargs): } default_kwargs['grade_frac'] = [1.0]*len(default_kwargs['grade_dims']) - if not set(kwargs.keys()) <= set(default_kwargs.keys()): raise TypeError(f"{__name__} got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") default_kwargs.update(kwargs) kwargs = default_kwargs - try: if rank == 0: os.makedirs(kwargs['base_dir']) except OSError: pass - + try: if rank == 0: os.makedirs(os.path.join(kwargs['base_dir'], 'clusters')) @@ -586,52 +584,56 @@ def wrap_prior(cube, theta): theta[:] = kwargs['prior'](cube) kwargs['grade_dims'] = [int(d) for d in list(kwargs['grade_dims'])] - kwargs['nlives'] = {float(logL):int(nlive) for logL, nlive in kwargs['nlives'].items()} + kwargs['nlives'] = {float(logL): int(nlive) + for logL, nlive in kwargs['nlives'].items()} # Run polychord from module library _pypolychord.run(wrap_loglikelihood, - wrap_prior, - kwargs['dumper'], - nDims, - kwargs['nDerived'], - kwargs['nlive'], - kwargs['num_repeats'], - kwargs['nprior'], - kwargs['nfail'], - kwargs['do_clustering'], - kwargs['feedback'], - kwargs['precision_criterion'], - kwargs['logzero'], - kwargs['max_ndead'], - kwargs['boost_posterior'], - kwargs['posteriors'], - kwargs['equals'], - kwargs['cluster_posteriors'], - kwargs['write_resume'], - kwargs['write_paramnames'], - kwargs['read_resume'], - kwargs['write_stats'], - kwargs['write_live'], - kwargs['write_dead'], - kwargs['write_prior'], - kwargs['maximise'], - kwargs['compression_factor'], - kwargs['synchronous'], - kwargs['base_dir'], - kwargs['file_root'], - kwargs['grade_frac'], - kwargs['grade_dims'], - kwargs['nlives'], - kwargs['seed'], - ) + wrap_prior, + kwargs['dumper'], + nDims, + kwargs['nDerived'], + kwargs['nlive'], + kwargs['num_repeats'], + kwargs['nprior'], + kwargs['nfail'], + kwargs['do_clustering'], + kwargs['feedback'], + kwargs['precision_criterion'], + kwargs['logzero'], + kwargs['max_ndead'], + kwargs['boost_posterior'], + kwargs['posteriors'], + kwargs['equals'], + kwargs['cluster_posteriors'], + kwargs['write_resume'], + kwargs['write_paramnames'], + kwargs['read_resume'], + kwargs['write_stats'], + kwargs['write_live'], + kwargs['write_dead'], + kwargs['write_prior'], + kwargs['maximise'], + kwargs['compression_factor'], + kwargs['synchronous'], + kwargs['base_dir'], + kwargs['file_root'], + kwargs['grade_frac'], + kwargs['grade_dims'], + kwargs['nlives'], + kwargs['seed'], + ) if 'cube_samples' in kwargs: kwargs['read_resume'] = read_resume if paramnames is not None: - PolyChordOutput.make_paramnames_file(paramnames, os.path.join(kwargs['base_dir'], kwargs['file_root']+".paramnames")) + PolyChordOutput.make_paramnames_file( + paramnames, os.path.join(kwargs['base_dir'], + kwargs['file_root']+".paramnames")) - return anesthetic.read_chains(os.path.join(kwargs['base_dir'], kwargs['file_root'])) + return anesthetic.read_chains( + os.path.join(kwargs['base_dir'], kwargs['file_root'])) From bd6dff1b368d04bc29be6aa49b80851a4c8c3381 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 15:36:01 +0000 Subject: [PATCH 53/87] also fix wrap_loglikelihood for new interface (#110) --- pypolychord/polychord.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 312d5fa4..38eb6a56 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -577,7 +577,11 @@ def run(loglikelihood, nDims, **kwargs): kwargs['read_resume'] = True def wrap_loglikelihood(theta, phi): - logL, phi[:] = loglikelihood(theta) + logL = loglikelihood(theta) + try: + logL, phi[:] = logL + except TypeError: + pass return logL def wrap_prior(cube, theta): From 34488e770c803c0ac129d9440b5762aed79cfabe Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:43:31 +0000 Subject: [PATCH 54/87] anesthetic back to optional, return None if not installed --- README.rst | 2 ++ pypolychord/polychord.py | 7 ++++++- run_pypolychord.py | 11 +++++++---- setup.py | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 09c5792b..842de37c 100644 --- a/README.rst +++ b/README.rst @@ -68,6 +68,8 @@ https://github.com/handley-lab/anesthetic pip install anesthetic ``` +If `anesthetic` is already installed, then `pypolychord.run()` will return an `anesthetic.NestedSamples` object, which can be used directly for post-processing. + MPI Support =========== diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 38eb6a56..f556c874 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -1,8 +1,8 @@ from pypolychord.output import PolyChordOutput import os +import warnings import _pypolychord import numpy as np -import anesthetic def default_prior(cube): @@ -636,6 +636,11 @@ def wrap_prior(cube, theta): paramnames, os.path.join(kwargs['base_dir'], kwargs['file_root']+".paramnames")) + try: + import anesthetic + except ImportError: + warnings.warn("anesthetic not installed. Cannot return NestedSamples object.") + return return anesthetic.read_chains( os.path.join(kwargs['base_dir'], kwargs['file_root'])) diff --git a/run_pypolychord.py b/run_pypolychord.py index 6cbce6de..a2cb0ce0 100755 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,5 +1,4 @@ from numpy import pi, log -import anesthetic as ac import pypolychord from pypolychord.priors import UniformPrior try: @@ -60,6 +59,10 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot -fig, ax = ac.make_2d_axes(['p0','p1','p2','p3','r']) -output.plot_2d(ax) -fig.savefig('posterior.pdf') +try: + import anesthetic as ac + fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + output.plot_2d(ax) + fig.savefig('posterior.pdf') +except ImportError: + print("Install anesthetic for plotting examples.") diff --git a/setup.py b/setup.py index fac6477d..23ff9384 100644 --- a/setup.py +++ b/setup.py @@ -130,7 +130,7 @@ def run(self): author_email='wh260@cam.ac.uk', license='PolyChord', packages=find_packages(), - install_requires=['numpy','scipy',"anesthetic>=2.0.0"], + install_requires=['numpy','scipy'], extras_require={'plotting': 'getdist'}, distclass=DistributionWithOption, ext_modules=[pypolychord_module], From a7c33470f8aa324fda2f9475709aaeb68bfde81b Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:44:26 +0000 Subject: [PATCH 55/87] version bump to 1.22.0 --- README.rst | 2 +- pypolychord/__init__.py | 2 +- src/polychord/feedback.f90 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 842de37c..e1bc0414 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ :target: https://arxiv.org/abs/1506.00171 :alt: Open-access paper -PolyChord v 1.21.3 +PolyChord v 1.22.0 Will Handley, Mike Hobson & Anthony Lasenby diff --git a/pypolychord/__init__.py b/pypolychord/__init__.py index 4c5f2570..d786aab1 100644 --- a/pypolychord/__init__.py +++ b/pypolychord/__init__.py @@ -1,3 +1,3 @@ -__version__ = "1.21.3" +__version__ = "1.22.0" from pypolychord.settings import PolyChordSettings from pypolychord.polychord import run_polychord, run diff --git a/src/polychord/feedback.f90 b/src/polychord/feedback.f90 index dfd76404..7e302b6d 100644 --- a/src/polychord/feedback.f90 +++ b/src/polychord/feedback.f90 @@ -28,7 +28,7 @@ subroutine write_opening_statement(settings) write(stdout_unit,'("")') write(stdout_unit,'("PolyChord: Next Generation Nested Sampling")') write(stdout_unit,'("copyright: Will Handley, Mike Hobson & Anthony Lasenby")') - write(stdout_unit,'(" version: 1.21.3")') + write(stdout_unit,'(" version: 1.22.0")') write(stdout_unit,'(" release: 9th Jan 2024")') write(stdout_unit,'(" email: wh260@mrao.cam.ac.uk")') write(stdout_unit,'("")') From 9852ba3fc515161660a2538922ef07169149a032 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:46:16 +0000 Subject: [PATCH 56/87] py2nb run_pypolychord.py --- run_pypolychord.ipynb | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 7bb496e3..8df5a021 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -3,12 +3,11 @@ { "cell_type": "code", "execution_count": null, - "id": "19cee382", + "id": "e660da7d", "metadata": {}, "outputs": [], "source": [ "from numpy import pi, log\n", - "import anesthetic as ac\n", "import pypolychord\n", "from pypolychord.priors import UniformPrior\n", "try:\n", @@ -19,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "86c075cf", + "id": "dff90bd3", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -30,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43c8c687", + "id": "fb94e706", "metadata": {}, "outputs": [], "source": [ @@ -51,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "3499789d", + "id": "95bcdbcc", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -60,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "410ef669", + "id": "c992b135", "metadata": {}, "outputs": [], "source": [ @@ -71,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "85d8c71c", + "id": "f9b784ba", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -81,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "bd8940ca", + "id": "37921844", "metadata": {}, "outputs": [], "source": [ @@ -91,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "5e29f3fc", + "id": "74771c7b", "metadata": {}, "source": [ "Create a paramnames file" @@ -100,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a2bc3f14", + "id": "4669bbcc", "metadata": {}, "outputs": [], "source": [ @@ -110,7 +109,7 @@ }, { "cell_type": "markdown", - "id": "a4357358", + "id": "d7b04e1f", "metadata": {}, "source": [ "Run PolyChord" @@ -119,7 +118,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2937a7e4", + "id": "5fadbbab", "metadata": {}, "outputs": [], "source": [ @@ -139,7 +138,7 @@ }, { "cell_type": "markdown", - "id": "489dd2ef", + "id": "4376730e", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -148,13 +147,17 @@ { "cell_type": "code", "execution_count": null, - "id": "49a3962b", + "id": "068188a3", "metadata": {}, "outputs": [], "source": [ - "fig, ax = ac.make_2d_axes(['p0','p1','p2','p3','r'])\n", - "output.plot_2d(ax)\n", - "fig.savefig('posterior.pdf')" + "try:\n", + " import anesthetic as ac\n", + " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " output.plot_2d(ax)\n", + " fig.savefig('posterior.pdf')\n", + "except ImportError:\n", + " print(\"Install anesthetic for plotting examples.\")" ] } ], From 5503a09041894b733038aa5f0faef49e3ea54200 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:52:03 +0000 Subject: [PATCH 57/87] Fix names of run scripts/notebooks --- run.ipynb | 167 ++++++++++++++++++ run_pypolychord_legacy_interface.py => run.py | 52 +++--- run_pypolychord.ipynb | 91 ++++++---- run_pypolychord.py | 52 +++--- 4 files changed, 276 insertions(+), 86 deletions(-) create mode 100644 run.ipynb rename run_pypolychord_legacy_interface.py => run.py (52%) mode change 100644 => 100755 mode change 100755 => 100644 run_pypolychord.py diff --git a/run.ipynb b/run.ipynb new file mode 100644 index 00000000..8df5a021 --- /dev/null +++ b/run.ipynb @@ -0,0 +1,167 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e660da7d", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import pi, log\n", + "import pypolychord\n", + "from pypolychord.priors import UniformPrior\n", + "try:\n", + " from mpi4py import MPI\n", + "except ImportError:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "dff90bd3", + "metadata": {}, + "source": [ + "Define a four-dimensional spherical gaussian likelihood,\n", + " width sigma=0.1, centered on the 0 with one derived parameter.\n", + " The derived parameter is the squared radius" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb94e706", + "metadata": {}, + "outputs": [], + "source": [ + "nDims = 4\n", + "nDerived = 1\n", + "sigma = 0.1\n", + "\n", + "def likelihood(theta):\n", + " \"\"\" Simple Gaussian Likelihood\"\"\"\n", + "\n", + " nDims = len(theta)\n", + " r2 = sum(theta**2)\n", + " logL = -log(2*pi*sigma*sigma)*nDims/2.0\n", + " logL += -r2/2/sigma/sigma\n", + "\n", + " return logL, [r2]" + ] + }, + { + "cell_type": "markdown", + "id": "95bcdbcc", + "metadata": {}, + "source": [ + "Define a box uniform prior from -1 to 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c992b135", + "metadata": {}, + "outputs": [], + "source": [ + "def prior(hypercube):\n", + " \"\"\" Uniform prior from [-1,1]^D. \"\"\"\n", + " return UniformPrior(-1, 1)(hypercube)" + ] + }, + { + "cell_type": "markdown", + "id": "f9b784ba", + "metadata": {}, + "source": [ + "Optional dumper function giving run-time read access to\n", + " the live points, dead points, weights and evidences" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37921844", + "metadata": {}, + "outputs": [], + "source": [ + "def dumper(live, dead, logweights, logZ, logZerr):\n", + " print(\"Last dead point:\", dead[-1])" + ] + }, + { + "cell_type": "markdown", + "id": "74771c7b", + "metadata": {}, + "source": [ + "Create a paramnames file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4669bbcc", + "metadata": {}, + "outputs": [], + "source": [ + "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", + "paramnames += [('r*', 'r')]" + ] + }, + { + "cell_type": "markdown", + "id": "d7b04e1f", + "metadata": {}, + "source": [ + "Run PolyChord" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fadbbab", + "metadata": {}, + "outputs": [], + "source": [ + "output = pypolychord.run(\n", + " likelihood,\n", + " nDims,\n", + " nDerived=nDerived,\n", + " prior=prior,\n", + " dumper=dumper,\n", + " file_root='gaussian',\n", + " nlive=200,\n", + " do_clustering=True,\n", + " read_resume=False,\n", + " paramnames=paramnames,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4376730e", + "metadata": {}, + "source": [ + "Make an anesthetic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "068188a3", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import anesthetic as ac\n", + " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " output.plot_2d(ax)\n", + " fig.savefig('posterior.pdf')\n", + "except ImportError:\n", + " print(\"Install anesthetic for plotting examples.\")" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/run_pypolychord_legacy_interface.py b/run.py old mode 100644 new mode 100755 similarity index 52% rename from run_pypolychord_legacy_interface.py rename to run.py index 52bcd707..a2cb0ce0 --- a/run_pypolychord_legacy_interface.py +++ b/run.py @@ -1,6 +1,5 @@ -from numpy import pi, log, sqrt +from numpy import pi, log import pypolychord -from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior try: from mpi4py import MPI @@ -38,39 +37,32 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) -#| Initialise the settings - -settings = PolyChordSettings(nDims, nDerived) -settings.file_root = 'gaussian' -settings.nlive = 200 -settings.do_clustering = True -settings.read_resume = False - -#| Run PolyChord - -output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) - #| Create a paramnames file paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] -output.make_paramnames_files(paramnames) -#| Make an anesthetic plot (could also use getdist) +#| Run PolyChord + +output = pypolychord.run( + likelihood, + nDims, + nDerived=nDerived, + prior=prior, + dumper=dumper, + file_root='gaussian', + nlive=200, + do_clustering=True, + read_resume=False, + paramnames=paramnames, +) + +#| Make an anesthetic plot + try: - from anesthetic import NestedSamples - samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root) - fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) + import anesthetic as ac + fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + output.plot_2d(ax) fig.savefig('posterior.pdf') - except ImportError: - try: - import getdist.plots - posterior = output.posterior - g = getdist.plots.getSubplotPlotter() - g.triangle_plot(posterior, filled=True) - g.export('posterior.pdf') - except ImportError: - print("Install matplotlib and getdist for plotting examples") - - print("Install anesthetic or getdist for for plotting examples") \ No newline at end of file + print("Install anesthetic for plotting examples.") diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 8df5a021..24516667 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -3,12 +3,13 @@ { "cell_type": "code", "execution_count": null, - "id": "e660da7d", + "id": "ec5c17d6", "metadata": {}, "outputs": [], "source": [ - "from numpy import pi, log\n", + "from numpy import pi, log, sqrt\n", "import pypolychord\n", + "from pypolychord.settings import PolyChordSettings\n", "from pypolychord.priors import UniformPrior\n", "try:\n", " from mpi4py import MPI\n", @@ -18,7 +19,7 @@ }, { "cell_type": "markdown", - "id": "dff90bd3", + "id": "32e68405", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +30,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fb94e706", + "id": "8806a3c3", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "95bcdbcc", + "id": "b6f2df98", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c992b135", + "id": "a46b1458", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +71,7 @@ }, { "cell_type": "markdown", - "id": "f9b784ba", + "id": "a8653669", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37921844", + "id": "16273350", "metadata": {}, "outputs": [], "source": [ @@ -90,26 +91,29 @@ }, { "cell_type": "markdown", - "id": "74771c7b", + "id": "2974bbee", "metadata": {}, "source": [ - "Create a paramnames file" + "Initialise the settings" ] }, { "cell_type": "code", "execution_count": null, - "id": "4669bbcc", + "id": "7f324d0b", "metadata": {}, "outputs": [], "source": [ - "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", - "paramnames += [('r*', 'r')]" + "settings = PolyChordSettings(nDims, nDerived)\n", + "settings.file_root = 'gaussian'\n", + "settings.nlive = 200\n", + "settings.do_clustering = True\n", + "settings.read_resume = False" ] }, { "cell_type": "markdown", - "id": "d7b04e1f", + "id": "2011857f", "metadata": {}, "source": [ "Run PolyChord" @@ -118,46 +122,65 @@ { "cell_type": "code", "execution_count": null, - "id": "5fadbbab", + "id": "b9954305", + "metadata": {}, + "outputs": [], + "source": [ + "output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper)" + ] + }, + { + "cell_type": "markdown", + "id": "b68aa3f4", + "metadata": {}, + "source": [ + "Create a paramnames file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d298c38f", "metadata": {}, "outputs": [], "source": [ - "output = pypolychord.run(\n", - " likelihood,\n", - " nDims,\n", - " nDerived=nDerived,\n", - " prior=prior,\n", - " dumper=dumper,\n", - " file_root='gaussian',\n", - " nlive=200,\n", - " do_clustering=True,\n", - " read_resume=False,\n", - " paramnames=paramnames,\n", - ")" + "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", + "paramnames += [('r*', 'r')]\n", + "output.make_paramnames_files(paramnames)" ] }, { "cell_type": "markdown", - "id": "4376730e", + "id": "1be0826b", "metadata": {}, "source": [ - "Make an anesthetic plot" + "Make an anesthetic plot (could also use getdist)" ] }, { "cell_type": "code", "execution_count": null, - "id": "068188a3", + "id": "e0218490", "metadata": {}, "outputs": [], "source": [ "try:\n", - " import anesthetic as ac\n", - " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", - " output.plot_2d(ax)\n", + " from anesthetic import NestedSamples\n", + " samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root)\n", + " fig, axes = samples.plot_2d(['p0','p1','p2','p3','r'])\n", " fig.savefig('posterior.pdf')\n", + "\n", "except ImportError:\n", - " print(\"Install anesthetic for plotting examples.\")" + " try:\n", + " import getdist.plots\n", + " posterior = output.posterior\n", + " g = getdist.plots.getSubplotPlotter()\n", + " g.triangle_plot(posterior, filled=True)\n", + " g.export('posterior.pdf')\n", + " except ImportError:\n", + " print(\"Install matplotlib and getdist for plotting examples\")\n", + "\n", + " print(\"Install anesthetic or getdist for for plotting examples\")" ] } ], diff --git a/run_pypolychord.py b/run_pypolychord.py old mode 100755 new mode 100644 index a2cb0ce0..52bcd707 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,5 +1,6 @@ -from numpy import pi, log +from numpy import pi, log, sqrt import pypolychord +from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior try: from mpi4py import MPI @@ -37,32 +38,39 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) -#| Create a paramnames file +#| Initialise the settings -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] -paramnames += [('r*', 'r')] +settings = PolyChordSettings(nDims, nDerived) +settings.file_root = 'gaussian' +settings.nlive = 200 +settings.do_clustering = True +settings.read_resume = False #| Run PolyChord -output = pypolychord.run( - likelihood, - nDims, - nDerived=nDerived, - prior=prior, - dumper=dumper, - file_root='gaussian', - nlive=200, - do_clustering=True, - read_resume=False, - paramnames=paramnames, -) - -#| Make an anesthetic plot +output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) + +#| Create a paramnames file +paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames += [('r*', 'r')] +output.make_paramnames_files(paramnames) + +#| Make an anesthetic plot (could also use getdist) try: - import anesthetic as ac - fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) - output.plot_2d(ax) + from anesthetic import NestedSamples + samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root) + fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) fig.savefig('posterior.pdf') + except ImportError: - print("Install anesthetic for plotting examples.") + try: + import getdist.plots + posterior = output.posterior + g = getdist.plots.getSubplotPlotter() + g.triangle_plot(posterior, filled=True) + g.export('posterior.pdf') + except ImportError: + print("Install matplotlib and getdist for plotting examples") + + print("Install anesthetic or getdist for for plotting examples") \ No newline at end of file From 5b962d4a6d4abf4aa4dc638fc263b74a5e0b470e Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:53:22 +0000 Subject: [PATCH 58/87] remove testing print statement --- pypolychord/polychord.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index f556c874..8497d5c7 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -789,5 +789,4 @@ def write(var): def _legacy_make_resume_file(settings, loglikelihood, prior): kwargs = settings.__dict__() - print(kwargs) _make_resume_file(loglikelihood, prior = prior, **kwargs) From ca9c0c8cdcc5331e15f30256c3cdf1c09e7fabdc Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 16:59:41 +0000 Subject: [PATCH 59/87] remove sqrt import, who knows where this came from --- run_pypolychord.ipynb | 32 ++++++++++++++++---------------- run_pypolychord.py | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index 24516667..fef54174 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -3,11 +3,11 @@ { "cell_type": "code", "execution_count": null, - "id": "ec5c17d6", + "id": "ea2f2400", "metadata": {}, "outputs": [], "source": [ - "from numpy import pi, log, sqrt\n", + "from numpy import pi, log\n", "import pypolychord\n", "from pypolychord.settings import PolyChordSettings\n", "from pypolychord.priors import UniformPrior\n", @@ -19,7 +19,7 @@ }, { "cell_type": "markdown", - "id": "32e68405", + "id": "241798b0", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -30,7 +30,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8806a3c3", + "id": "05eb1e5a", "metadata": {}, "outputs": [], "source": [ @@ -51,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "b6f2df98", + "id": "57f2d465", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -60,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a46b1458", + "id": "85f817f4", "metadata": {}, "outputs": [], "source": [ @@ -71,7 +71,7 @@ }, { "cell_type": "markdown", - "id": "a8653669", + "id": "748f5d7e", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -81,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16273350", + "id": "a776e774", "metadata": {}, "outputs": [], "source": [ @@ -91,7 +91,7 @@ }, { "cell_type": "markdown", - "id": "2974bbee", + "id": "7d309ed1", "metadata": {}, "source": [ "Initialise the settings" @@ -100,7 +100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7f324d0b", + "id": "d0fae60e", "metadata": {}, "outputs": [], "source": [ @@ -113,7 +113,7 @@ }, { "cell_type": "markdown", - "id": "2011857f", + "id": "1b789e2e", "metadata": {}, "source": [ "Run PolyChord" @@ -122,7 +122,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b9954305", + "id": "1a43380a", "metadata": {}, "outputs": [], "source": [ @@ -131,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "b68aa3f4", + "id": "729b8354", "metadata": {}, "source": [ "Create a paramnames file" @@ -140,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d298c38f", + "id": "d5181a20", "metadata": {}, "outputs": [], "source": [ @@ -151,7 +151,7 @@ }, { "cell_type": "markdown", - "id": "1be0826b", + "id": "cabb76a6", "metadata": {}, "source": [ "Make an anesthetic plot (could also use getdist)" @@ -160,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e0218490", + "id": "9f70bc9e", "metadata": {}, "outputs": [], "source": [ diff --git a/run_pypolychord.py b/run_pypolychord.py index 52bcd707..00781da8 100644 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -1,4 +1,4 @@ -from numpy import pi, log, sqrt +from numpy import pi, log import pypolychord from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior @@ -73,4 +73,4 @@ def dumper(live, dead, logweights, logZ, logZerr): except ImportError: print("Install matplotlib and getdist for plotting examples") - print("Install anesthetic or getdist for for plotting examples") \ No newline at end of file + print("Install anesthetic or getdist for for plotting examples") From 89ed5073749b82f1bfc747e92e967a0a1bb1586d Mon Sep 17 00:00:00 2001 From: Will Handley Date: Tue, 9 Jan 2024 17:16:44 +0000 Subject: [PATCH 60/87] Updated with new recommended interface --- README.rst | 52 +++++++++++++++++++++-------------- run.ipynb => quickstart.ipynb | 26 +++++++++--------- run.py => quickstart.py | 0 3 files changed, 45 insertions(+), 33 deletions(-) rename run.ipynb => quickstart.ipynb (92%) rename run.py => quickstart.py (100%) mode change 100755 => 100644 diff --git a/README.rst b/README.rst index e1bc0414..33a8f8e1 100644 --- a/README.rst +++ b/README.rst @@ -11,11 +11,11 @@ Will Handley, Mike Hobson & Anthony Lasenby wh260@mrao.cam.ac.uk -arXiv:1502.01856 +`arXiv:1502.01856 `_ -arXiv:1506.00171 +`arXiv:1506.00171 `_ -Latest version Released Apr 2020 +Latest version Released Jan 2024 PolyChord Licence @@ -26,11 +26,12 @@ file. PolyChord is free for academic usage Users are also required to cite the PolyChord papers: -- arXiv:1502.01856 -- arXiv:1506.00171 +- `arXiv:1502.01856 `_ +- `arXiv:1506.00171 `_ in their publications. + Python quickstart ================= @@ -39,14 +40,14 @@ For Python users in a hurry: .. code:: bash pip install git+https://github.com/PolyChord/PolyChordLite@master - wget https://raw.githubusercontent.com/PolyChord/PolyChordLite/master/run_pypolychord.py - python run_pypolychord.py + wget https://raw.githubusercontent.com/PolyChord/PolyChordLite/master/quickstart.py + python quickstart.py -You can then modify the file run_pypolychord.py to your needs. If you have mpi compilers available, this version can be run in parallel with mpi. +You should make sure that you have gfortran/gcc (or equivalent) fortran compilers installed. -You should make sure that you have gfortran (or equivalent) fortran compilers installed. +You can then modify the file quickstart.py to your needs. If you have mpi compilers available, this version can be run in parallel with mpi. -If any of the above steps fail (this can in general happen for certain Mac OSX versions), then try installing without pip: +If any of the above steps fail (this can in general happen for certain macOS versions), then try installing without pip: .. code:: bash @@ -54,12 +55,23 @@ If any of the above steps fail (this can in general happen for certain Mac OSX v cd PolyChordLite python setup.py install +or perhaps: + +.. code:: bash + + git clone https://github.com/PolyChord/PolyChordLite.git + cd PolyChordLite + make + pip install . + +our apologies -- the shifting sands that are macOS do not play well with the delicate dance of fortran, C and Python that is (py)PolyChordLite. + If you do not have sudo access/virtual environments/anaconda, then appending `--user` to the install command may be necessary. Post Processing =============== -We recommend the tool `anesthetic `_ for post-processing your nested sampling runs. A plot gallery can be found `here `_ +We recommend the pip-installable tool `anesthetic `_ for post-processing your nested sampling runs. A plot gallery can be found `here `_ https://github.com/handley-lab/anesthetic @@ -213,7 +225,6 @@ You can install direct from the git repository using: pip install https://github.com/PolyChord/PolyChordLite/archive/master.zip -(N.B. PyPi coming soon) or you can install locally with the command: .. code:: bash @@ -234,26 +245,27 @@ and check that it's working by running: .. code:: bash - $ python run_pypolychord.py + $ python quickstart.py or in MPI: .. code:: bash - $ mpirun -np 4 python run_pypolychord.py + $ mpirun -np 4 python quickstart.py If so, the rest of the interface is relatively painless. Follow the example in -run_pypolychord.py, and consult the docstring if you need help: +quickstart.py, and consult the docstring if you need help: .. code:: python - import pypolychord - from pypolychord.settings import PolyChordSettings + >>> import pypolychord + >>> help(pypolychord.run) + +There is also a demo `python notebook `_. + +To post-process nested sampling runs we recommend the pip-installable tool `anesthetic `_. A plot gallery can be found `here `_ - help(pypolychord.run_polychord) - help(PolyChordSettings) -There is also a demo `python notebook `_. Output files ============= diff --git a/run.ipynb b/quickstart.ipynb similarity index 92% rename from run.ipynb rename to quickstart.ipynb index 8df5a021..21191b0e 100644 --- a/run.ipynb +++ b/quickstart.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e660da7d", + "id": "3ffc7bce", "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "dff90bd3", + "id": "615a42e9", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fb94e706", + "id": "47ba71d9", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "95bcdbcc", + "id": "a1145660", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c992b135", + "id": "49121208", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "f9b784ba", + "id": "210672a3", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37921844", + "id": "04f0cb82", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "74771c7b", + "id": "d4440ac9", "metadata": {}, "source": [ "Create a paramnames file" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4669bbcc", + "id": "5d08dbd1", "metadata": {}, "outputs": [], "source": [ @@ -109,7 +109,7 @@ }, { "cell_type": "markdown", - "id": "d7b04e1f", + "id": "42d5a505", "metadata": {}, "source": [ "Run PolyChord" @@ -118,7 +118,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5fadbbab", + "id": "c94f5132", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +138,7 @@ }, { "cell_type": "markdown", - "id": "4376730e", + "id": "8081b256", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -147,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "068188a3", + "id": "81bc11ba", "metadata": {}, "outputs": [], "source": [ diff --git a/run.py b/quickstart.py old mode 100755 new mode 100644 similarity index 100% rename from run.py rename to quickstart.py From 7c8eae9086983e6dc80a8bda2ab32ae8e9b0cb63 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 17:19:38 +0000 Subject: [PATCH 61/87] anesthetic root is deprecated --- run_pypolychord.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 00781da8..41d51d52 100644 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -58,8 +58,8 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot (could also use getdist) try: - from anesthetic import NestedSamples - samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root) + import anesthetic as ac + samples = ac.read_chains(settings.base_dir + '/' + settings.file_root) fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) fig.savefig('posterior.pdf') From 2e3208de84815bae7f24265e7b851104d2a794aa Mon Sep 17 00:00:00 2001 From: Will Handley Date: Tue, 9 Jan 2024 17:21:17 +0000 Subject: [PATCH 62/87] Update README.rst --- README.rst | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 33a8f8e1..407e4b4c 100644 --- a/README.rst +++ b/README.rst @@ -61,8 +61,8 @@ or perhaps: git clone https://github.com/PolyChord/PolyChordLite.git cd PolyChordLite - make - pip install . + make + pip install . our apologies -- the shifting sands that are macOS do not play well with the delicate dance of fortran, C and Python that is (py)PolyChordLite. @@ -73,12 +73,9 @@ Post Processing We recommend the pip-installable tool `anesthetic `_ for post-processing your nested sampling runs. A plot gallery can be found `here `_ +.. code:: bash -https://github.com/handley-lab/anesthetic - -``` -pip install anesthetic -``` + pip install anesthetic If `anesthetic` is already installed, then `pypolychord.run()` will return an `anesthetic.NestedSamples` object, which can be used directly for post-processing. From e54ad7873da28efde75abac81af5033ace2465ba Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 17:37:51 +0000 Subject: [PATCH 63/87] more anesthetic 2 madness --- run_pypolychord.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run_pypolychord.py b/run_pypolychord.py index 41d51d52..1dbc032c 100644 --- a/run_pypolychord.py +++ b/run_pypolychord.py @@ -60,7 +60,8 @@ def dumper(live, dead, logweights, logZ, logZerr): try: import anesthetic as ac samples = ac.read_chains(settings.base_dir + '/' + settings.file_root) - fig, axes = samples.plot_2d(['p0','p1','p2','p3','r']) + fig, axes = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + samples.plot_2d(axes) fig.savefig('posterior.pdf') except ImportError: From b6c50ca4cba6e5954aa393c405060dbc895fb0d9 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 17:40:57 +0000 Subject: [PATCH 64/87] update notebooks --- quickstart.ipynb | 26 +++++++++++++------------- run_pypolychord.ipynb | 37 +++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/quickstart.ipynb b/quickstart.ipynb index 21191b0e..35c5afea 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3ffc7bce", + "id": "eec3c0e5", "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "615a42e9", + "id": "b33904a7", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47ba71d9", + "id": "0adf46e1", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "a1145660", + "id": "cd52eac3", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49121208", + "id": "909ab913", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "210672a3", + "id": "b4767da0", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "04f0cb82", + "id": "30144ccb", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "d4440ac9", + "id": "702531a5", "metadata": {}, "source": [ "Create a paramnames file" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5d08dbd1", + "id": "17623b74", "metadata": {}, "outputs": [], "source": [ @@ -109,7 +109,7 @@ }, { "cell_type": "markdown", - "id": "42d5a505", + "id": "9b05ff7d", "metadata": {}, "source": [ "Run PolyChord" @@ -118,7 +118,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c94f5132", + "id": "418f1dcd", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +138,7 @@ }, { "cell_type": "markdown", - "id": "8081b256", + "id": "169f46a2", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -147,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "81bc11ba", + "id": "f16b0b2a", "metadata": {}, "outputs": [], "source": [ diff --git a/run_pypolychord.ipynb b/run_pypolychord.ipynb index fef54174..f63aa1a5 100644 --- a/run_pypolychord.ipynb +++ b/run_pypolychord.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ea2f2400", + "id": "4d1fc41a", "metadata": {}, "outputs": [], "source": [ @@ -19,7 +19,7 @@ }, { "cell_type": "markdown", - "id": "241798b0", + "id": "1ca223cf", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -30,7 +30,7 @@ { "cell_type": "code", "execution_count": null, - "id": "05eb1e5a", + "id": "a6e26034", "metadata": {}, "outputs": [], "source": [ @@ -51,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "57f2d465", + "id": "30f9057e", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -60,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85f817f4", + "id": "36df3068", "metadata": {}, "outputs": [], "source": [ @@ -71,7 +71,7 @@ }, { "cell_type": "markdown", - "id": "748f5d7e", + "id": "0866528b", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -81,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a776e774", + "id": "60a6b0d0", "metadata": {}, "outputs": [], "source": [ @@ -91,7 +91,7 @@ }, { "cell_type": "markdown", - "id": "7d309ed1", + "id": "52d9ce7b", "metadata": {}, "source": [ "Initialise the settings" @@ -100,7 +100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d0fae60e", + "id": "fbacdc2b", "metadata": {}, "outputs": [], "source": [ @@ -113,7 +113,7 @@ }, { "cell_type": "markdown", - "id": "1b789e2e", + "id": "9151acb4", "metadata": {}, "source": [ "Run PolyChord" @@ -122,7 +122,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1a43380a", + "id": "c0bcadad", "metadata": {}, "outputs": [], "source": [ @@ -131,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "729b8354", + "id": "ac1c6942", "metadata": {}, "source": [ "Create a paramnames file" @@ -140,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d5181a20", + "id": "1b947d71", "metadata": {}, "outputs": [], "source": [ @@ -151,7 +151,7 @@ }, { "cell_type": "markdown", - "id": "cabb76a6", + "id": "94b3acd9", "metadata": {}, "source": [ "Make an anesthetic plot (could also use getdist)" @@ -160,14 +160,15 @@ { "cell_type": "code", "execution_count": null, - "id": "9f70bc9e", + "id": "070c235e", "metadata": {}, "outputs": [], "source": [ "try:\n", - " from anesthetic import NestedSamples\n", - " samples = NestedSamples(root= settings.base_dir + '/' + settings.file_root)\n", - " fig, axes = samples.plot_2d(['p0','p1','p2','p3','r'])\n", + " import anesthetic as ac\n", + " samples = ac.read_chains(settings.base_dir + '/' + settings.file_root)\n", + " fig, axes = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " samples.plot_2d(axes)\n", " fig.savefig('posterior.pdf')\n", "\n", "except ImportError:\n", From e646727ee29fd548c3bee317ec6eaf1175ad0323 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 18:03:26 +0000 Subject: [PATCH 65/87] remove () from dict --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 8497d5c7..8215dcb3 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -788,5 +788,5 @@ def write(var): write('=== global equally weighted posterior points ===') def _legacy_make_resume_file(settings, loglikelihood, prior): - kwargs = settings.__dict__() + kwargs = settings.__dict__ _make_resume_file(loglikelihood, prior = prior, **kwargs) From 093738754e92dd9ce98bf67eec326c731f52200e Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 22:24:45 +0000 Subject: [PATCH 66/87] play around with tests --- tests/test_run_pypolychord.py | 54 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/tests/test_run_pypolychord.py b/tests/test_run_pypolychord.py index 9f9a5175..bd1e1db9 100644 --- a/tests/test_run_pypolychord.py +++ b/tests/test_run_pypolychord.py @@ -1,5 +1,7 @@ import pytest import numpy as np +from mpi4py import MPI +import anesthetic as ac import pypolychord from pypolychord.settings import PolyChordSettings from pypolychord.priors import UniformPrior @@ -36,7 +38,7 @@ def gaussian_likelihood(theta): @pytest.mark.parametrize("settings, likelihood, nDims, nDerived", [(default_settings, gaussian_likelihood, 4, 1), (cube_samples_settings, gaussian_likelihood, 4, 1)]) -def test_run(settings, likelihood, nDims, nDerived): +def test_run_polychord(settings, likelihood, nDims, nDerived): # Define a box uniform prior from -1 to 1 def prior(hypercube): """ Uniform prior from [-1,1]^D. """ @@ -50,8 +52,8 @@ def dumper(live, dead, logweights, logZ, logZerr): # Run PolyChord - print("Running PolyChord") - output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) + output = pypolychord.run_polychord(likelihood, nDims, nDerived, + settings, prior, dumper) # Create a paramnames file @@ -60,33 +62,43 @@ def dumper(live, dead, logweights, logZ, logZerr): output.make_paramnames_files(paramnames) -@pytest.mark.parametrize("settings, likelihood, nDims, nDerived", - [(default_settings, gaussian_likelihood, 4, 1), - (cube_samples_settings, gaussian_likelihood, 4, 1)]) -@pytest.mark.mpi -def test_run_mpi(settings, likelihood, nDims, nDerived): - from mpi4py import MPI - - settings.file_root += '_mpi' +@pytest.mark.parametrize("likelihood, nDims, nDerived", + [(gaussian_likelihood, 4, 1),]) +def test_run(likelihood, nDims, nDerived): # Define a box uniform prior from -1 to 1 def prior(hypercube): """ Uniform prior from [-1,1]^D. """ return UniformPrior(-1, 1)(hypercube) - # Optional dumper function giving run-time read access to - # the live points, dead points, weights and evidences - - def dumper(live, dead, logweights, logZ, logZerr): - print("Last dead point:", dead[-1]) + # Create paramnames + paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] + paramnames += [('r*', 'r')] # Run PolyChord - print("Running PolyChord") - output = pypolychord.run_polychord(likelihood, nDims, nDerived, settings, prior, dumper) + ns = pypolychord.run(likelihood, nDims, nDerived=nDerived, + prior=prior, paramnames=paramnames, read_resume=False) + assert isinstance(ns, ac.NestedSamples) - # Create a paramnames file - paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +@pytest.mark.parametrize("seed", [-1, 0, 1, 2]) +def test_seed(seed): + # Define a box uniform prior from -1 to 1 + def prior(hypercube): + """ Uniform prior from [-1,1]^D. """ + return UniformPrior(-1, 1)(hypercube) + + + # Create paramnames + paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(4)] paramnames += [('r*', 'r')] - output.make_paramnames_files(paramnames) + + # Run PolyChord twice + ns0 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, + prior=prior, paramnames=paramnames, read_resume=False, + seed=seed) + ns1 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, + prior=prior, paramnames=paramnames, read_resume=False, + seed=seed) + assert ns0.equals(ns1) != (seed < 0) From f0cdfdb25aeba1940cf7accf7bf9e5f1470b066d Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 22:33:08 +0000 Subject: [PATCH 67/87] add anesthetic to CI install step --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4a9d30c8..8711ea51 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -75,7 +75,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install mpi4py pytest pytest-mpi fortranformat + python -m pip install mpi4py pytest pytest-mpi fortranformat anesthetic - name: Install pypolychord run: pip install -v '.[test]' From ddff23988c4266f53235395371eb5c185f59a1d1 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 22:37:44 +0000 Subject: [PATCH 68/87] only run tests on python>=3.8 --- .github/workflows/CI.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8711ea51..e201b627 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -57,10 +57,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] - include: - - os: ubuntu-20.04 - python-version: '3.6' + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 From 2dabeef97ad6eafd46e3c043dddf066e2de2d5c4 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 22:54:51 +0000 Subject: [PATCH 69/87] test likelihood without derived parameters --- tests/test_run_pypolychord.py | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/test_run_pypolychord.py b/tests/test_run_pypolychord.py index bd1e1db9..28d621ef 100644 --- a/tests/test_run_pypolychord.py +++ b/tests/test_run_pypolychord.py @@ -20,6 +20,19 @@ def gaussian_likelihood(theta): return logL, [r2] +def no_derived_gaussian_likelihood(theta): + """ Simple Gaussian Likelihood without derived parameters""" + + sigma = 0.1 + + nDims = len(theta) + r2 = sum(theta**2) + logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0 + logL += -r2/2/sigma/sigma + + return logL + + default_settings = PolyChordSettings(4, 1) default_settings.file_root = 'settings' default_settings.nlive = 200 @@ -89,7 +102,6 @@ def prior(hypercube): """ Uniform prior from [-1,1]^D. """ return UniformPrior(-1, 1)(hypercube) - # Create paramnames paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(4)] paramnames += [('r*', 'r')] @@ -102,3 +114,25 @@ def prior(hypercube): prior=prior, paramnames=paramnames, read_resume=False, seed=seed) assert ns0.equals(ns1) != (seed < 0) + + +def test_no_derived(): + # Define a box uniform prior from -1 to 1 + seed = 1 + + def prior(hypercube): + """ Uniform prior from [-1,1]^D. """ + return UniformPrior(-1, 1)(hypercube) + + paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(4)] + + # Run without derived parameters + ns0 = pypolychord.run(no_derived_gaussian_likelihood, 4, + prior=prior, paramnames=paramnames, read_resume=False, + seed=seed) + # Run with derived parameters + paramnames += [('r*', 'r')] + ns1 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, + prior=prior, paramnames=paramnames, read_resume=False, + seed=seed) + assert ns0.equals(ns1.drop(columns='r')) From ae0bb6db172c77719b55b41741465e1ce52b912b Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Tue, 9 Jan 2024 23:06:47 +0000 Subject: [PATCH 70/87] some test housekeeping --- tests/test_run_pypolychord.py | 67 +++++++++++++---------------------- 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/tests/test_run_pypolychord.py b/tests/test_run_pypolychord.py index 28d621ef..23747f7c 100644 --- a/tests/test_run_pypolychord.py +++ b/tests/test_run_pypolychord.py @@ -20,18 +20,7 @@ def gaussian_likelihood(theta): return logL, [r2] -def no_derived_gaussian_likelihood(theta): - """ Simple Gaussian Likelihood without derived parameters""" - - sigma = 0.1 - - nDims = len(theta) - r2 = sum(theta**2) - logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0 - logL += -r2/2/sigma/sigma - - return logL - +uniform_prior = UniformPrior(-1, 1) default_settings = PolyChordSettings(4, 1) default_settings.file_root = 'settings' @@ -52,11 +41,6 @@ def no_derived_gaussian_likelihood(theta): [(default_settings, gaussian_likelihood, 4, 1), (cube_samples_settings, gaussian_likelihood, 4, 1)]) def test_run_polychord(settings, likelihood, nDims, nDerived): - # Define a box uniform prior from -1 to 1 - def prior(hypercube): - """ Uniform prior from [-1,1]^D. """ - return UniformPrior(-1, 1)(hypercube) - # Optional dumper function giving run-time read access to # the live points, dead points, weights and evidences @@ -66,7 +50,7 @@ def dumper(live, dead, logweights, logZ, logZerr): # Run PolyChord output = pypolychord.run_polychord(likelihood, nDims, nDerived, - settings, prior, dumper) + settings, uniform_prior, dumper) # Create a paramnames file @@ -78,12 +62,6 @@ def dumper(live, dead, logweights, logZ, logZerr): @pytest.mark.parametrize("likelihood, nDims, nDerived", [(gaussian_likelihood, 4, 1),]) def test_run(likelihood, nDims, nDerived): - - # Define a box uniform prior from -1 to 1 - def prior(hypercube): - """ Uniform prior from [-1,1]^D. """ - return UniformPrior(-1, 1)(hypercube) - # Create paramnames paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] paramnames += [('r*', 'r')] @@ -91,48 +69,51 @@ def prior(hypercube): # Run PolyChord ns = pypolychord.run(likelihood, nDims, nDerived=nDerived, - prior=prior, paramnames=paramnames, read_resume=False) + prior=uniform_prior, paramnames=paramnames, + read_resume=False) assert isinstance(ns, ac.NestedSamples) @pytest.mark.parametrize("seed", [-1, 0, 1, 2]) def test_seed(seed): - # Define a box uniform prior from -1 to 1 - def prior(hypercube): - """ Uniform prior from [-1,1]^D. """ - return UniformPrior(-1, 1)(hypercube) - # Create paramnames paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(4)] paramnames += [('r*', 'r')] # Run PolyChord twice ns0 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, - prior=prior, paramnames=paramnames, read_resume=False, - seed=seed) + prior=uniform_prior, paramnames=paramnames, + read_resume=False, seed=seed) ns1 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, - prior=prior, paramnames=paramnames, read_resume=False, - seed=seed) + prior=uniform_prior, paramnames=paramnames, + read_resume=False, seed=seed) assert ns0.equals(ns1) != (seed < 0) +@pytest.mark.filterwarnings("ignore::pandas.errors.PerformanceWarning") def test_no_derived(): - # Define a box uniform prior from -1 to 1 - seed = 1 + def no_derived_gaussian_likelihood(theta): + """ Simple Gaussian Likelihood without derived parameters""" + + sigma = 0.1 - def prior(hypercube): - """ Uniform prior from [-1,1]^D. """ - return UniformPrior(-1, 1)(hypercube) + nDims = len(theta) + r2 = sum(theta**2) + logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0 + logL += -r2/2/sigma/sigma + return logL + + seed = 1 paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(4)] # Run without derived parameters ns0 = pypolychord.run(no_derived_gaussian_likelihood, 4, - prior=prior, paramnames=paramnames, read_resume=False, - seed=seed) + prior=uniform_prior, paramnames=paramnames, + read_resume=False, seed=seed) # Run with derived parameters paramnames += [('r*', 'r')] ns1 = pypolychord.run(gaussian_likelihood, 4, nDerived=1, - prior=prior, paramnames=paramnames, read_resume=False, - seed=seed) + prior=uniform_prior, paramnames=paramnames, + read_resume=False, seed=seed) assert ns0.equals(ns1.drop(columns='r')) From 8143c0a27417a8642e8f70beed7b4bb18c1fa3d9 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 13:30:28 +0000 Subject: [PATCH 71/87] use or between the two loglikelihood return options --- pypolychord/polychord.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 8215dcb3..2f81d3c4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -51,9 +51,7 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, (logL, phi): (float, array-like) return is a 2-tuple of the log-likelihood (logL) and the derived parameters (phi). phi length nDerived. - - Returns - ------- + OR logL: float log-likelihood @@ -266,9 +264,7 @@ def run(loglikelihood, nDims, **kwargs): (logL, phi): (float, array-like) return is a 2-tuple of the log-likelihood (logL) and the derived parameters (phi). phi length nDerived. - - Returns - ------- + OR logL: float log-likelihood From 3465c209ac947d9beda3fbdfb91b4311b11268eb Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 13:48:36 +0000 Subject: [PATCH 72/87] use pathlib.Path to save OSError nonsense --- pypolychord/polychord.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 2f81d3c4..62ddf976 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -1,5 +1,6 @@ from pypolychord.output import PolyChordOutput import os +from pathlib import Path import warnings import _pypolychord import numpy as np @@ -555,17 +556,9 @@ def run(loglikelihood, nDims, **kwargs): default_kwargs.update(kwargs) kwargs = default_kwargs - try: - if rank == 0: - os.makedirs(kwargs['base_dir']) - except OSError: - pass - - try: - if rank == 0: - os.makedirs(os.path.join(kwargs['base_dir'], 'clusters')) - except OSError: - pass + if rank == 0: + (Path(kwargs['base_dir']) / "clusters").mkdir( + parents=True, exist_ok=True) if 'cube_samples' in kwargs: _make_resume_file(loglikelihood, kwargs['prior'], **kwargs) From 98851217bad15d4daf3a8fc9f0bf2c88c78a49da Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 13:52:52 +0000 Subject: [PATCH 73/87] explain paramnames more clearly --- quickstart.ipynb | 33 ++++++++++++++++++--------------- quickstart.py | 6 ++++-- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/quickstart.ipynb b/quickstart.ipynb index 35c5afea..7cfaf948 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "eec3c0e5", + "id": "f1479f13", "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "b33904a7", + "id": "5cd722d8", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0adf46e1", + "id": "3ffe63bf", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "cd52eac3", + "id": "b872ec58", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "909ab913", + "id": "2ddab209", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "b4767da0", + "id": "0df31bf8", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30144ccb", + "id": "57b91f61", "metadata": {}, "outputs": [], "source": [ @@ -90,26 +90,29 @@ }, { "cell_type": "markdown", - "id": "702531a5", + "id": "4e1eba5e", "metadata": {}, "source": [ - "Create a paramnames file" + "Parameter names" ] }, { "cell_type": "code", "execution_count": null, - "id": "17623b74", + "id": "f9e59423", "metadata": {}, "outputs": [], "source": [ - "paramnames = [('p%i' % i, r'\\theta_%i' % i) for i in range(nDims)]\n", + "#! This is a list of tuples (label, latex)\n", + "#! Derived parameters should be followed by a *\n", + "\n", + "paramnames = [(f'p{i}', f'\\\\theta_{i}') for i in range(nDims)]\n", "paramnames += [('r*', 'r')]" ] }, { "cell_type": "markdown", - "id": "9b05ff7d", + "id": "5b4e28be", "metadata": {}, "source": [ "Run PolyChord" @@ -118,7 +121,7 @@ { "cell_type": "code", "execution_count": null, - "id": "418f1dcd", + "id": "775b8c4a", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +141,7 @@ }, { "cell_type": "markdown", - "id": "169f46a2", + "id": "bd570900", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -147,7 +150,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f16b0b2a", + "id": "f4b1541e", "metadata": {}, "outputs": [], "source": [ diff --git a/quickstart.py b/quickstart.py index a2cb0ce0..9b106374 100644 --- a/quickstart.py +++ b/quickstart.py @@ -37,9 +37,11 @@ def prior(hypercube): def dumper(live, dead, logweights, logZ, logZerr): print("Last dead point:", dead[-1]) -#| Create a paramnames file +#| Parameter names +#! This is a list of tuples (label, latex) +#! Derived parameters should be followed by a * -paramnames = [('p%i' % i, r'\theta_%i' % i) for i in range(nDims)] +paramnames = [(f'p{i}', f'\\theta_{i}') for i in range(nDims)] paramnames += [('r*', 'r')] #| Run PolyChord From 82add9d54872bf26f2f00a60f3165b857fc34b74 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:09:33 +0000 Subject: [PATCH 74/87] check that grade_dims add up to nDims and test --- pypolychord/polychord.py | 3 +++ tests/test_run_pypolychord.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 62ddf976..0ced397b 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -577,6 +577,9 @@ def wrap_prior(cube, theta): theta[:] = kwargs['prior'](cube) kwargs['grade_dims'] = [int(d) for d in list(kwargs['grade_dims'])] + if sum(kwargs['grade_dims']) != nDims: + raise ValueError(f"grade_dims ({sum(kwargs['grade_dims'])}) " + f"must sum to nDims ({nDims})") kwargs['nlives'] = {float(logL): int(nlive) for logL, nlive in kwargs['nlives'].items()} diff --git a/tests/test_run_pypolychord.py b/tests/test_run_pypolychord.py index 23747f7c..d9181a86 100644 --- a/tests/test_run_pypolychord.py +++ b/tests/test_run_pypolychord.py @@ -117,3 +117,15 @@ def no_derived_gaussian_likelihood(theta): prior=uniform_prior, paramnames=paramnames, read_resume=False, seed=seed) assert ns0.equals(ns1.drop(columns='r')) + + +def test_grade_dims(): + grade_dims = [1, 3] + grade_frac = [0.5, 0.5] + pypolychord.run(gaussian_likelihood, 4, nDerived=1, + prior=uniform_prior, read_resume=False, + grade_dims=grade_dims, grade_frac=grade_frac) + with pytest.raises(ValueError): + pypolychord.run(gaussian_likelihood, 5, nDerived=1, + prior=uniform_prior, read_resume=False, + grade_dims=grade_dims, grade_frac=grade_frac) From b9cc4fed76f70a9fc00f35a111a02c055143425e Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:12:57 +0000 Subject: [PATCH 75/87] update run docstring anesthetic stuff --- pypolychord/polychord.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 0ced397b..d7ec01d4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -472,7 +472,13 @@ def run(loglikelihood, nDims, **kwargs): Returns ------- - anesthetic.NestedSamples(root=/) + anesthetic.NestedSamples + + All output is currently produced in the form of text files in , + anesthetic can read these in directly: + + >>> import anesthetic + >>> anesthetic.read_chains("base_dir/file_root") In general the contents of is a set of getdist compatible files. From 2cae544b15ef258dc91d53e1bdb325115f7ab755 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:14:51 +0000 Subject: [PATCH 76/87] correct run_pypolychord return --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index d7ec01d4..dad96be4 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -104,7 +104,7 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, Returns ------- - None. (in Python) + pypolychord.output.PolyChordOutput All output is currently produced in the form of text files in directory. If you would like to contribute to pypolychord and improve this, From 9b4be33b2254875783e9eec6ea600a4528b04929 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:18:14 +0000 Subject: [PATCH 77/87] correct missing blank lines in run() docstring --- pypolychord/polychord.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index dad96be4..62ada4f9 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -447,12 +447,14 @@ def run(loglikelihood, nDims, **kwargs): between loglike contours and nlive. You should still set nlive to be a sensible number, as this indicates how often to update the clustering, and to define the default value. + seed : positive int (Default: system time in milliseconds) Choose the seed to seed the random number generator. Note **Positive seeds only** a negative seed indicates that you should use the system time in milliseconds + cube_samples: array-like (Default: None) samples from the unit hypercube to start nested sampling from. This is @@ -463,6 +465,7 @@ def run(loglikelihood, nDims, **kwargs): x3 < 1, if one did not want to use SortedUniformPrior. Only available in Python interface. shape (:, nDims) + paramnames: List [(string,string)] (Default: None) Mapping of label:Tex for all parameters in order. E.g. for two physical From 45e9f8c344655175548983e06ef42ce79b9b5505 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:24:10 +0000 Subject: [PATCH 78/87] add cluster_dir to kwargs, since technically it could be changed in PolyChordSettings (with difficulty) --- pypolychord/polychord.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 62ada4f9..40ca7606 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -429,6 +429,10 @@ def run(loglikelihood, nDims, **kwargs): (Default: 'test') Root name of the files produced. + cluster_dir : string + (Default: 'clusters') + Where to store clustering files. (base_dir/cluster_dir) + grade_frac : List[float] (Default: [1]) The amount of time to spend in each speed. @@ -554,6 +558,7 @@ def run(loglikelihood, nDims, **kwargs): 'synchronous': True, 'base_dir': 'chains', 'file_root': 'test', + 'cluster_dir': 'clusters', 'grade_dims': [nDims], 'nlives': {}, 'seed': -1, @@ -566,7 +571,7 @@ def run(loglikelihood, nDims, **kwargs): kwargs = default_kwargs if rank == 0: - (Path(kwargs['base_dir']) / "clusters").mkdir( + (Path(kwargs['base_dir']) / kwargs['cluster_dir']).mkdir( parents=True, exist_ok=True) if 'cube_samples' in kwargs: From 387b0d0e944686692bb9e266ee2fa061dca4812a Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:52:43 +0000 Subject: [PATCH 79/87] remove os entirely in favour of pathlib --- pypolychord/polychord.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 40ca7606..4e8f71ef 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -1,5 +1,4 @@ from pypolychord.output import PolyChordOutput -import os from pathlib import Path import warnings import _pypolychord @@ -152,17 +151,8 @@ def run_polychord(loglikelihood, nDims, nDerived, settings, except ImportError: rank = 0 - try: - if rank == 0: - os.makedirs(settings.base_dir) - except OSError: - pass - - try: - if rank == 0: - os.makedirs(settings.cluster_dir) - except OSError: - pass + if rank == 0: + Path(settings.cluster_dir).mkdir(parents=True, exist_ok=True) if settings.cube_samples is not None: _legacy_make_resume_file(settings, loglikelihood, prior) @@ -639,23 +629,25 @@ def wrap_prior(cube, theta): if paramnames is not None: PolyChordOutput.make_paramnames_file( - paramnames, os.path.join(kwargs['base_dir'], - kwargs['file_root']+".paramnames")) + paramnames, + Path(kwargs['base_dir']) / + (kwargs['file_root'] + ".paramnames")) try: import anesthetic except ImportError: - warnings.warn("anesthetic not installed. Cannot return NestedSamples object.") + warnings.warn("anesthetic not installed. " + "Cannot return NestedSamples object.") return return anesthetic.read_chains( - os.path.join(kwargs['base_dir'], kwargs['file_root'])) + str(Path(kwargs['base_dir']) / kwargs['file_root'])) def _make_resume_file(loglikelihood, **kwargs): import fortranformat as ff - resume_filename = os.path.join(kwargs['base_dir'], - kwargs['file_root'])+".resume" + resume_filename = Path(kwargs['base_dir']) / (kwargs['file_root'] + + ".resume") try: from mpi4py import MPI From 4f5270fc432c4b4f4ef0a2036db4a25fad1a02be Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 14:59:57 +0000 Subject: [PATCH 80/87] fix line too long --- pypolychord/polychord.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 4e8f71ef..76ba3862 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -556,7 +556,8 @@ def run(loglikelihood, nDims, **kwargs): default_kwargs['grade_frac'] = [1.0]*len(default_kwargs['grade_dims']) if not set(kwargs.keys()) <= set(default_kwargs.keys()): - raise TypeError(f"{__name__} got unknown keyword arguments {kwargs.keys() - default_kwargs.keys()}") + raise TypeError(f"{__name__} got unknown keyword arguments: " + f"{kwargs.keys() - default_kwargs.keys()}") default_kwargs.update(kwargs) kwargs = default_kwargs From a17d78d80cf9b8f9b4ba502af743e731a3fbbd46 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 15:02:57 +0000 Subject: [PATCH 81/87] update date --- src/polychord/feedback.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polychord/feedback.f90 b/src/polychord/feedback.f90 index 7e302b6d..370c1c38 100644 --- a/src/polychord/feedback.f90 +++ b/src/polychord/feedback.f90 @@ -29,7 +29,7 @@ subroutine write_opening_statement(settings) write(stdout_unit,'("PolyChord: Next Generation Nested Sampling")') write(stdout_unit,'("copyright: Will Handley, Mike Hobson & Anthony Lasenby")') write(stdout_unit,'(" version: 1.22.0")') - write(stdout_unit,'(" release: 9th Jan 2024")') + write(stdout_unit,'(" release: 10th Jan 2024")') write(stdout_unit,'(" email: wh260@mrao.cam.ac.uk")') write(stdout_unit,'("")') end if From cdaf0630e3fc31ca5ef7d2be36779d73d63d7d35 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 15:09:47 +0000 Subject: [PATCH 82/87] fix default grade_frac --- pypolychord/polychord.py | 4 +++- tests/test_run_pypolychord.py | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index 76ba3862..a040ac72 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -553,7 +553,9 @@ def run(loglikelihood, nDims, **kwargs): 'nlives': {}, 'seed': -1, } - default_kwargs['grade_frac'] = [1.0]*len(default_kwargs['grade_dims']) + default_kwargs['grade_frac'] = ([1.0]*len(default_kwargs['grade_dims']) + if 'grade_dims' not in kwargs else + [1.0]*len(kwargs['grade_dims'])) if not set(kwargs.keys()) <= set(default_kwargs.keys()): raise TypeError(f"{__name__} got unknown keyword arguments: " diff --git a/tests/test_run_pypolychord.py b/tests/test_run_pypolychord.py index d9181a86..6852da37 100644 --- a/tests/test_run_pypolychord.py +++ b/tests/test_run_pypolychord.py @@ -121,11 +121,10 @@ def no_derived_gaussian_likelihood(theta): def test_grade_dims(): grade_dims = [1, 3] - grade_frac = [0.5, 0.5] pypolychord.run(gaussian_likelihood, 4, nDerived=1, prior=uniform_prior, read_resume=False, - grade_dims=grade_dims, grade_frac=grade_frac) + grade_dims=grade_dims) with pytest.raises(ValueError): pypolychord.run(gaussian_likelihood, 5, nDerived=1, prior=uniform_prior, read_resume=False, - grade_dims=grade_dims, grade_frac=grade_frac) + grade_dims=grade_dims) From 40cb5593a3315f613bfa34b2c07fd1c48fc5db68 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Wed, 10 Jan 2024 15:14:04 +0000 Subject: [PATCH 83/87] remove pytest-mpi requirement from CI --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e201b627..ed6a52bb 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -72,7 +72,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install mpi4py pytest pytest-mpi fortranformat anesthetic + python -m pip install mpi4py pytest fortranformat anesthetic - name: Install pypolychord run: pip install -v '.[test]' @@ -81,4 +81,4 @@ jobs: run: python -m pytest tests - name: Test pypolychord (MPI) - run: mpirun --oversubscribe -np 3 python -m pytest tests --only-mpi + run: mpirun --oversubscribe -np 3 python -m pytest tests From d420526ba0eca73853ee01f15adef705311493e5 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Thu, 25 Jan 2024 16:25:58 +0000 Subject: [PATCH 84/87] remove unecessary cast to set --- pypolychord/polychord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypolychord/polychord.py b/pypolychord/polychord.py index a040ac72..6e6ca398 100644 --- a/pypolychord/polychord.py +++ b/pypolychord/polychord.py @@ -557,7 +557,7 @@ def run(loglikelihood, nDims, **kwargs): if 'grade_dims' not in kwargs else [1.0]*len(kwargs['grade_dims'])) - if not set(kwargs.keys()) <= set(default_kwargs.keys()): + if not kwargs.keys() <= default_kwargs.keys(): raise TypeError(f"{__name__} got unknown keyword arguments: " f"{kwargs.keys() - default_kwargs.keys()}") default_kwargs.update(kwargs) From cc807f5dc7e6481420f9b9b3fdca966f7df3c147 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Sat, 27 Jan 2024 12:34:57 +0000 Subject: [PATCH 85/87] don't use ac shorthand, but also don't clutter the namespace with log and pi --- quickstart.ipynb | 34 +++++++++++++++++----------------- quickstart.py | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/quickstart.ipynb b/quickstart.ipynb index 7cfaf948..54f6a984 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -3,11 +3,11 @@ { "cell_type": "code", "execution_count": null, - "id": "f1479f13", + "id": "37be54d9", "metadata": {}, "outputs": [], "source": [ - "from numpy import pi, log\n", + "import numpy as np\n", "import pypolychord\n", "from pypolychord.priors import UniformPrior\n", "try:\n", @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "5cd722d8", + "id": "aeaae184", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3ffe63bf", + "id": "49320101", "metadata": {}, "outputs": [], "source": [ @@ -42,7 +42,7 @@ "\n", " nDims = len(theta)\n", " r2 = sum(theta**2)\n", - " logL = -log(2*pi*sigma*sigma)*nDims/2.0\n", + " logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0\n", " logL += -r2/2/sigma/sigma\n", "\n", " return logL, [r2]" @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "b872ec58", + "id": "8dd8603b", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2ddab209", + "id": "79b76ebc", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "0df31bf8", + "id": "0d0a2ba3", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57b91f61", + "id": "168ef65c", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "4e1eba5e", + "id": "d51bebd8", "metadata": {}, "source": [ "Parameter names" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f9e59423", + "id": "18a88ea5", "metadata": {}, "outputs": [], "source": [ @@ -112,7 +112,7 @@ }, { "cell_type": "markdown", - "id": "5b4e28be", + "id": "0a16c903", "metadata": {}, "source": [ "Run PolyChord" @@ -121,7 +121,7 @@ { "cell_type": "code", "execution_count": null, - "id": "775b8c4a", + "id": "d1626d93", "metadata": {}, "outputs": [], "source": [ @@ -141,7 +141,7 @@ }, { "cell_type": "markdown", - "id": "bd570900", + "id": "89234435", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -150,13 +150,13 @@ { "cell_type": "code", "execution_count": null, - "id": "f4b1541e", + "id": "6c486a8b", "metadata": {}, "outputs": [], "source": [ "try:\n", - " import anesthetic as ac\n", - " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " import anesthetic\n", + " fig, ax = anesthetic.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", " output.plot_2d(ax)\n", " fig.savefig('posterior.pdf')\n", "except ImportError:\n", diff --git a/quickstart.py b/quickstart.py index 9b106374..fde25961 100644 --- a/quickstart.py +++ b/quickstart.py @@ -1,4 +1,4 @@ -from numpy import pi, log +import numpy as np import pypolychord from pypolychord.priors import UniformPrior try: @@ -20,7 +20,7 @@ def likelihood(theta): nDims = len(theta) r2 = sum(theta**2) - logL = -log(2*pi*sigma*sigma)*nDims/2.0 + logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0 logL += -r2/2/sigma/sigma return logL, [r2] @@ -62,8 +62,8 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot try: - import anesthetic as ac - fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + import anesthetic + fig, ax = anesthetic.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) output.plot_2d(ax) fig.savefig('posterior.pdf') except ImportError: From 6d735e5c31e18ae40e3ac366b83432da36bc92d4 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Sun, 28 Jan 2024 22:58:37 +0000 Subject: [PATCH 86/87] Revert "don't use ac shorthand, but also don't clutter the namespace with log and pi" This reverts commit cc807f5dc7e6481420f9b9b3fdca966f7df3c147. --- quickstart.ipynb | 34 +++++++++++++++++----------------- quickstart.py | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/quickstart.ipynb b/quickstart.ipynb index 54f6a984..7cfaf948 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -3,11 +3,11 @@ { "cell_type": "code", "execution_count": null, - "id": "37be54d9", + "id": "f1479f13", "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", + "from numpy import pi, log\n", "import pypolychord\n", "from pypolychord.priors import UniformPrior\n", "try:\n", @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "aeaae184", + "id": "5cd722d8", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49320101", + "id": "3ffe63bf", "metadata": {}, "outputs": [], "source": [ @@ -42,7 +42,7 @@ "\n", " nDims = len(theta)\n", " r2 = sum(theta**2)\n", - " logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0\n", + " logL = -log(2*pi*sigma*sigma)*nDims/2.0\n", " logL += -r2/2/sigma/sigma\n", "\n", " return logL, [r2]" @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "8dd8603b", + "id": "b872ec58", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79b76ebc", + "id": "2ddab209", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "0d0a2ba3", + "id": "0df31bf8", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "168ef65c", + "id": "57b91f61", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "d51bebd8", + "id": "4e1eba5e", "metadata": {}, "source": [ "Parameter names" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18a88ea5", + "id": "f9e59423", "metadata": {}, "outputs": [], "source": [ @@ -112,7 +112,7 @@ }, { "cell_type": "markdown", - "id": "0a16c903", + "id": "5b4e28be", "metadata": {}, "source": [ "Run PolyChord" @@ -121,7 +121,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d1626d93", + "id": "775b8c4a", "metadata": {}, "outputs": [], "source": [ @@ -141,7 +141,7 @@ }, { "cell_type": "markdown", - "id": "89234435", + "id": "bd570900", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -150,13 +150,13 @@ { "cell_type": "code", "execution_count": null, - "id": "6c486a8b", + "id": "f4b1541e", "metadata": {}, "outputs": [], "source": [ "try:\n", - " import anesthetic\n", - " fig, ax = anesthetic.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " import anesthetic as ac\n", + " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", " output.plot_2d(ax)\n", " fig.savefig('posterior.pdf')\n", "except ImportError:\n", diff --git a/quickstart.py b/quickstart.py index fde25961..9b106374 100644 --- a/quickstart.py +++ b/quickstart.py @@ -1,4 +1,4 @@ -import numpy as np +from numpy import pi, log import pypolychord from pypolychord.priors import UniformPrior try: @@ -20,7 +20,7 @@ def likelihood(theta): nDims = len(theta) r2 = sum(theta**2) - logL = -np.log(2*np.pi*sigma*sigma)*nDims/2.0 + logL = -log(2*pi*sigma*sigma)*nDims/2.0 logL += -r2/2/sigma/sigma return logL, [r2] @@ -62,8 +62,8 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot try: - import anesthetic - fig, ax = anesthetic.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + import anesthetic as ac + fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) output.plot_2d(ax) fig.savefig('posterior.pdf') except ImportError: From 5d9dadf4f0c16ccbc51a97ced516ebcc094cfc53 Mon Sep 17 00:00:00 2001 From: AdamOrmondroyd Date: Sun, 28 Jan 2024 23:01:02 +0000 Subject: [PATCH 87/87] from anesthetic import make_2d_axes --- quickstart.ipynb | 30 +++++++++++++++--------------- quickstart.py | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/quickstart.ipynb b/quickstart.ipynb index 7cfaf948..83b254c1 100644 --- a/quickstart.ipynb +++ b/quickstart.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f1479f13", + "id": "74897445", "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "markdown", - "id": "5cd722d8", + "id": "0b27c3aa", "metadata": {}, "source": [ "Define a four-dimensional spherical gaussian likelihood,\n", @@ -29,7 +29,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3ffe63bf", + "id": "c9453fa2", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "b872ec58", + "id": "a2e69376", "metadata": {}, "source": [ "Define a box uniform prior from -1 to 1" @@ -59,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2ddab209", + "id": "93de201d", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "0df31bf8", + "id": "fe13bd20", "metadata": {}, "source": [ "Optional dumper function giving run-time read access to\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57b91f61", + "id": "a0565d20", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "4e1eba5e", + "id": "f9ed3ce3", "metadata": {}, "source": [ "Parameter names" @@ -99,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f9e59423", + "id": "c1442195", "metadata": {}, "outputs": [], "source": [ @@ -112,7 +112,7 @@ }, { "cell_type": "markdown", - "id": "5b4e28be", + "id": "58c57e67", "metadata": {}, "source": [ "Run PolyChord" @@ -121,7 +121,7 @@ { "cell_type": "code", "execution_count": null, - "id": "775b8c4a", + "id": "f2dbb2bb", "metadata": {}, "outputs": [], "source": [ @@ -141,7 +141,7 @@ }, { "cell_type": "markdown", - "id": "bd570900", + "id": "a8c1be16", "metadata": {}, "source": [ "Make an anesthetic plot" @@ -150,13 +150,13 @@ { "cell_type": "code", "execution_count": null, - "id": "f4b1541e", + "id": "39356e18", "metadata": {}, "outputs": [], "source": [ "try:\n", - " import anesthetic as ac\n", - " fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", + " from anesthetic import make_2d_axes\n", + " fig, ax = make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r'])\n", " output.plot_2d(ax)\n", " fig.savefig('posterior.pdf')\n", "except ImportError:\n", diff --git a/quickstart.py b/quickstart.py index 9b106374..d0f9a700 100644 --- a/quickstart.py +++ b/quickstart.py @@ -62,8 +62,8 @@ def dumper(live, dead, logweights, logZ, logZerr): #| Make an anesthetic plot try: - import anesthetic as ac - fig, ax = ac.make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) + from anesthetic import make_2d_axes + fig, ax = make_2d_axes(['p0', 'p1', 'p2', 'p3', 'r']) output.plot_2d(ax) fig.savefig('posterior.pdf') except ImportError: