Source code for dyPolyChord.polychord_utils

#!/usr/bin/env python
"""
Functions for running dyPolyChord using compiled PolyChord C++ or Fortran
likelihoods.
"""
import os


[docs]class RunCompiledPolyChord(object): """Object for running a compiled PolyChord executable with specified inputs."""
[docs] def __init__(self, executable_path, prior_str, **kwargs): """ Specify path to executable, priors and derived parameters. Parameters ---------- executable_path: str Path to compiled likelihood. If this is in the directory from which dyPolyChord is being run, you may need to prepend "./" to the executable name for it to work. prior_str: str String specifying prior in the format required for PolyChord .ini files (see get_prior_block_str for more details). config_str: str, optional String to be written to [root].cfg file if required. derived_str: str or None, optional String specifying prior in the format required for PolyChord .ini files (see prior_str for more details). mpi_str: str or None, optional Optional mpi command to preprend to run command. For example to run with 8 processors, use mpi_str = 'mprun -np 8'. Note that PolyChord must be installed with MPI enabled to allow running with MPI. """ self.config_str = kwargs.pop('config_str', None) self.derived_str = kwargs.pop('derived_str', None) self.mpi_str = kwargs.pop('mpi_str', None) if kwargs: raise TypeError('unexpected **kwargs: {0}'.format(kwargs)) self.executable_path = executable_path self.prior_str = prior_str
[docs] def __call__(self, settings_dict, comm=None): """ Run PolyChord with the input settings by writing a .ini file then using the compiled likelihood specified in executable_path. See the PolyChord documentation for more details. Parameters ---------- settings_dict: dict Input PolyChord settings. comm: None, optional Not used. Included only so __call__ has the same arguments as the equivalent python function (which uses the comm argument for runnign with MPI). """ assert os.path.isfile(self.executable_path), ( 'executable not found: ' + self.executable_path) assert comm is None, 'comm not used for compiled likelihoods.' # Write settings to ini file file_path = os.path.join( settings_dict['base_dir'], settings_dict['file_root']) with open(file_path + '.ini', 'w') as ini_file: ini_file.write(self.ini_string(settings_dict)) # If required, write config file if self.config_str is not None: with open(file_path + '.cfg', 'w') as cfg_file: cfg_file.write(self.config_str) # Execute command command_str = self.executable_path + ' ' + file_path + '.ini' if self.mpi_str is not None: command_str = self.mpi_str + ' ' + command_str os.system(command_str)
[docs] def ini_string(self, settings): """Get a PolyChord format .ini file string based on the input settings. Parameters ---------- settings: dict Returns ------- string: str """ string = '' # Add the settings for key, value in settings.items(): if key == 'nlives': if value: loglikes = sorted(settings['nlives']) string += 'loglikes = ' + format_setting(loglikes) + '\n' nlives = [settings['nlives'][ll] for ll in loglikes] string += 'nlives = ' + format_setting(nlives) + '\n' else: string += key + ' = ' + format_setting(value) + '\n' # Add the prior string += self.prior_str if self.derived_str is not None: string += self.derived_str return string
# Helper functions for making PolyChord prior strings # ---------------------------------------------------
[docs]def get_prior_block_str(prior_name, prior_params, nparam, **kwargs): """ Returns a PolyChord format prior block for inclusion in PolyChord .ini files. See the PolyChord documentation for more details. Parameters ---------- prior_name: str Name of prior. See the PolyChord documentation for a list of currently available priors and details of how to add your own. prior_params: str, float or list of strs and floats Parameters for the prior function. nparam: int Number of parameters. start_param: int, optional Where to start param numbering. For when multiple prior blocks are being used. block: int, optional Number of block (only needed when using multiple prior blocks). speed: int, optional Use to specify fast and slow parameters if required. See the PolyChord documentation for more details. Returns ------- block_str: str PolyChord format prior block string for ini file. """ start_param = kwargs.pop('start_param', 1) speed = kwargs.pop('speed', 1) block = kwargs.pop('block', 1) if kwargs: raise TypeError('unexpected **kwargs: {0}'.format(kwargs)) block_str = '' for i in range(start_param, nparam + start_param): block_str += ('P : p{0} | \\theta_{{{0}}} | {1} | {2} | {3} |' .format(i, speed, prior_name, block)) block_str += format_setting(prior_params) + '\n' return block_str
[docs]def format_setting(setting): """ Return setting as string in the format needed for PolyChord's .ini files. These use 'T' for True and 'F' for False, and require lists of numbers written separated by spaces and without commas or brackets. Parameters ---------- setting: (can be any type for which str(settings) works) Returns ------- str """ if isinstance(setting, bool): return str(setting)[0] elif isinstance(setting, (list, tuple)): string = str(setting) for char in [',', '[', ']', '(', ')']: string = string.replace(char, '') return string else: return str(setting)
[docs]def python_prior_to_str(prior, **kwargs): """Utility function for mapping python priors (of the type in python_priors.py) to ini file format strings used for compiled (C++ or Fortran) likelihoods. The input prior must correspond to a prior function set up in PolyChord/src/polychord/priors.f90. You can easily add your own too. Note that some of the priors are only available in PolyChord >= v1.15. Parameters ---------- prior_obj: python prior object Of the type defined in python_priors.py kwargs: dict, optional Passed to get_prior_block_str (see its docstring for more details). Returns ------- block_str: str PolyChord format prior block string for ini file. """ nparam = kwargs.pop('nparam') name = type(prior).__name__.lower() if name == 'uniform': parameters = [prior.minimum, prior.maximum] elif name == 'poweruniform': name = 'power_uniform' parameters = [prior.minimum, prior.maximum, prior.power] assert prior.power < 0, ( 'compiled power_uniform currently only takes negative powers.' 'power={}'.format(prior.power)) elif name == 'gaussian': parameters = [getattr(prior, 'mu', 0.0), prior.sigma] if getattr(prior, 'half', False): name = 'half_' + name elif name == 'exponential': parameters = [prior.lambd] else: raise TypeError('Not set up for ' + name) if getattr(prior, 'sort', False): name = 'sorted_' + name if getattr(prior, 'adaptive', False): name = 'adaptive_' + name assert getattr(prior, "nfunc_min", 1) == 1, ( 'compiled adaptive priors currently only take nfunc_min=1.' 'prior.nfunc_min={}'.format(prior.nfunc_min)) return get_prior_block_str(name, parameters, nparam, **kwargs)
[docs]def python_block_prior_to_str(bp_obj): """As for python_prior_to_str, but for BlockPrior objects of the type defined in python_priors.py. python_prior_to_str is called seperately on every block. Parameters ---------- prior_obj: python prior object Of the type defined in python_priors.py. kwargs: dict, optional Passed to get_prior_block_str (see its docstring for more details). Returns ------- block_str: str PolyChord format prior block string for ini file. """ assert type(bp_obj).__name__ == 'BlockPrior', ( 'Unexpected input object type: {}'.format( type(bp_obj).__name__)) start_param = 1 string = '' for i, prior in enumerate(bp_obj.prior_blocks): string += python_prior_to_str( prior, block=(i + 1), start_param=start_param, nparam=bp_obj.block_sizes[i]) start_param += bp_obj.block_sizes[i] return string