Source code for data

"""
.. module:: data
   :synopsis: Define the Data and Parameter classes

.. moduleauthor:: Benjamin Audren <benjamin.audren@epfl.ch>
"""
import os
import sys
import math
import random as rd
import warnings
import subprocess as sp
import re

import io_mp  # Needs to talk to io_mp.py file for the logging
                               # of parameters
import prior

# A modified version of Python dictionary in order to keep track of the order
# in it (much the same as in an array). In case an older version of Python is
# used, this module does not belong to collections. Please remind to put that
# into your PYTHONPATH variable.
try:
    from collections import OrderedDict as od
except ImportError:
    try:
        from ordereddict import OrderedDict as od
    except ImportError:
        raise io_mp.MissingLibraryError(
            "If you are running with Python v2.5 or 2.6, you need" +
            "to manually install the ordereddict package by placing" +
            "the file ordereddict.py in your Python Path")


[docs]class Data(object): """ Store all relevant data to communicate between the different modules. """ def __init__(self, command_line, path): """ The Data class holds the cosmological information, the parameters from the MCMC run, the information coming from the likelihoods. It is a wide collections of information, with in particular two main dictionaries: cosmo_arguments and mcmc_parameters. It defines several useful **methods**. The following ones are called just once, at initialization: * :func:`fill_mcmc_parameters` * :func:`read_file` * :func:`read_version` * :func:`group_parameters_in_blocks` On the other hand, these two following functions are called every step. * :func:`check_for_slow_step` * :func:`update_cosmo_arguments` Finally, the convenient method :func:`get_mcmc_parameters` will be called in many places, to return the proper list of desired parameters. It has a number of different **attributes**, and the more important ones are listed here: * :attr:`boundary_loglike` * :attr:`cosmo_arguments` * :attr:`mcmc_parameters` * :attr:`need_cosmo_update` * :attr:`log_flag` .. note:: The `experiments` attribute is extracted from the parameter file, and contains the list of likelihoods to use .. note:: The path argument will be used in case it is a first run, and hence a new folder is created. If starting from an existing folder, this dictionary will be compared with the one extracted from the log.param, and will use the latter while warning the user. .. warning:: New in version 2.0.0, you can now specify an oversampling of the nuisance parameters, to hasten the execution of a run with likelihoods that have many of them. You should specify a new field in the parameter file, `data.over_sampling = [1, ...]`, that contains a 1 on the first element, and then the over sampling of the desired likelihoods. This array must have the same size as the number of blocks (1 for the cosmo + 1 for each likelihood with varying nuisance parameters). You need to call the code with the flag `-j jast` for it to be used. To create an instance of this class, one must feed the following parameters and keyword arguments: Parameters ---------- command_line : NameSpace NameSpace containing the input from the :mod:`parser_mp`. It stores the input parameter file, the jumping methods, the output folder, etc... Most of the information extracted from the command_file will be transformed into :class:`Data` attributes, whenever it felt meaningful to do so. path : dict Contains a dictionary of important local paths. It is used here to find the cosmological module location. """ # Initialisation of the random seed rd.seed() # Store the parameter file self.param = command_line.param # Recover jumping method from command_line self.jumping = command_line.jumping self.jumping_factor = command_line.jumping_factor # Store the rest of the command line self.command_line = command_line # Initialise the path dictionnary. self.path = {} self.boundary_loglike = -1e30 """ Define the boundary loglike, the value used to defined a loglike that is out of bounds. If a point in the parameter space is affected to this value, it will be automatically rejected, hence increasing the multiplicity of the last accepted point. """ # Creation of the two main dictionnaries: self.cosmo_arguments = {} """ Simple dictionary that will serve as a communication interface with the cosmological code. It contains all the parameters for the code that will not be set to their default values. It is updated from :attr:`mcmc_parameters`. :rtype: dict """ self.mcmc_parameters = od() """ Ordered dictionary of dictionaries, it contains everything needed by the :mod:`mcmc` module for the MCMC procedure. Every parameter name will be the key of a dictionary, containing the initial configuration, role, status, last accepted point and current point. :rtype: ordereddict """ # Arguments for PyMultiNest self.NS_param_names = [] self.NS_arguments = {} """ Dictionary containing the parameters needed by the PyMultiNest sampler. It is filled just before the run of the sampler. Those parameters not defined will be set to the default value of PyMultiNest. :rtype: dict """ # Initialise the experiments attribute self.experiments = [] # Initialise the oversampling setting self.over_sampling = [] """ List storing the respective over sampling of the parameters. The first entry, applied to the cosmological parameters, will always be 1. Setting it to anything else would simply rescale the whole process. If not specified otherwise in the parameter file, all other numbers will be set to 1 as well. :rtype: list """ # Default value for the number of steps self.N = 10 # Create the variable out, and out_name, which will be initialised # later by the :mod:`io_mp` module self.out = None self.out_name = '' # If the parameter file is not a log.param, the path will be read # before reading the parameter file. if self.param.find('log.param') == -1: self.path.update(path) # Read from the parameter file to fill properly the mcmc_parameters # dictionary. self.fill_mcmc_parameters() # Test if the recovered path agrees with the one extracted from # the configuration file. if self.path != {}: if not self.path.has_key('root'): self.path.update({'root': path['root']}) if self.path != path: warnings.warn( "Your code location in the log.param file is " "in contradiction with your .conf file. " "I will use the one from log.param.") # Determine which cosmological code is in use if self.path['cosmo'].find('class') != -1: self.cosmological_module_name = 'CLASS' else: self.cosmological_module_name = None # Recover the cosmological code version (and git hash if relevant). # To implement a new cosmological code, please add another case to the # test below. if self.cosmological_module_name == 'CLASS': # Official version number common_file_path = os.path.join( self.path['cosmo'], 'include', 'common.h') with open(common_file_path, 'r') as common_file: for line in common_file: if line.find('_VERSION_') != -1: self.version = line.split()[-1].replace('"', '') break if not command_line.silent: print 'with CLASS %s' % self.version # Git version number and branch try: # This nul_file helps to get read of a potential useless error # message with open(os.devnull, "w") as nul_file: self.git_version = sp.Popen( ["git", "rev-parse", "HEAD"], cwd=self.path['cosmo'], stdout=sp.PIPE, stderr=nul_file).communicate()[0].strip() self.git_branch = sp.Popen( ["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=self.path['cosmo'], stdout=sp.PIPE, stderr=nul_file).communicate()[0].strip() except (sp.CalledProcessError, OSError): # Note, OSError seems to be raised on some systems, instead of # sp.CalledProcessError - which seems to be linked to the # existence of os.devnull, so now both error are caught. warnings.warn( "Running CLASS from a non version-controlled repository") self.git_version, self.git_branch = '', '' # If using an existing log.param, read in and compare this number # to the already stored one if self.param.find('log.param') != -1: try: version, git_version, git_branch = self.read_version( self.param_file) if version != self.version: warnings.warn( "Your version of CLASS: %s" % self.version + " does not match the one used previously" + " in this folder (%s)." % version + " Proceed with caution") else: if self.git_branch != git_branch: warnings.warn( "CLASS set to branch %s" % self.git_branch + ", wrt. the one used in the log.param:" + " %s." % git_branch) if self.git_version != git_version: warnings.warn( "CLASS set to version %s" % self.git_version + ", wrt. the one used in the log.param:" + " %s." % git_version) except AttributeError: # This error is raised when the regular expression match # failed - due to comparing to an old log.param that did # not have this feature properly implemented. Ignore this. pass else: raise io_mp.CosmologicalModuleError( "If you want to check for another cosmological module version" " please add an elif clause to this part") # End of initialisation with the parameter file self.param_file.close() self.log_flag = False """ Stores the information whether or not the likelihood data files need to be written down in the log.param file. Initially at False. :rtype: bool """ self.need_cosmo_update = True """ `added in version 1.1.1`. It stores the truth value of whether the cosmological block of parameters was changed from one step to another. See :meth:`group_parameters_in_blocks` :rtype: bool """ # logging the parameter file (only if folder does not exist !) ## temporary variable for readability log_param = os.path.join(command_line.folder, 'log.param') if (os.path.exists(command_line.folder) and not os.path.exists(log_param)): if command_line.param is not None: warnings.warn( "Detecting empty folder, logging the parameter file") io_mp.log_parameters(self, command_line) self.log_flag = True if not os.path.exists(command_line.folder): os.makedirs(command_line.folder) # Logging of parameters io_mp.log_parameters(self, command_line) self.log_flag = True if not command_line.silent: print '\nTesting likelihoods for:\n ->', print ', '.join(self.experiments)+'\n' self.initialise_likelihoods(self.experiments) # Storing parameters by blocks of speed self.group_parameters_in_blocks() # Finally, log the cosmo_arguments used. This comes in the end, because # it can be modified inside the likelihoods init functions if self.log_flag: io_mp.log_cosmo_arguments(self, command_line) io_mp.log_default_configuration(self, command_line)
[docs] def fill_mcmc_parameters(self): """ Initializes the ordered dictionary :attr:`mcmc_parameters` from the input parameter file. It uses :meth:`read_file`, and initializes instances of :class:`parameter` to actually fill in :attr:`mcmc_parameters`. """ # Define temporary quantities, only to simplify the input in the # parameter file self.parameters = od() # Read from the parameter file everything try: self.param_file = open(self.param, 'r') except IOError: raise io_mp.ConfigurationError( "Error in initializing the Data class, the parameter file " + "{0} does not point to a proper file".format(self.param)) # In case the parameter file is a log.param, scan first once the file # to extract only the path dictionnary. if self.param.find('log.param') != -1: self.read_file(self.param, 'data', field='path') self.read_file(self.param, 'data') # Test here whether the number of parameters extracted correspond to # the number of lines (to make sure no doublon is present) number_of_parameters = sum( [1 for l in open(self.param, 'r') if l and l.find('#') == -1 and l.find('data.parameters[') != -1]) if number_of_parameters != len(self.parameters): raise io_mp.ConfigurationError( "You probably have two lines in your parameter files with " "the same parameter name. This is most probably an error, " "which will cause problems down the line. Please fix this.") # Do the same for every experiments - but only if you are starting a # new folder. Otherwise, this step will actually be done when # initializing the likelihood. if self.param.find('log.param') == -1: for experiment in self.experiments: self.read_file(self.param, experiment, separate=True) # Finally create all the instances of the Parameter given the input. for key, value in self.parameters.iteritems(): self.mcmc_parameters[key] = Parameter(value, key) """ Transform from parameters dictionary to mcmc_parameters dictionary of instances from the class :class:`parameter` (inheriting from dict) """
[docs] def initialise_likelihoods(self, experiments): """ Given an array of experiments, return an ordered dict of instances .. Note:: in the __init__ method, experiments is naturally self.experiments, but it is useful to keep it as a parameter, for the case of importance sampling. """ self.lkl = od() # adding the likelihood directory to the path, to import the module # then, for each library, calling an instance of the likelihood. # Beware, though, if you add new likelihoods, they should go to the # folder likelihoods/yourlike/yourlike.py, and contain a yourlike.data, # otherwise the following set of commands will not work anymore. # For the logging if log_flag is True, each likelihood will log its # parameters # Due to problems in relative import, this line must be there. Until a # better solution is found. It adds the root folder of the MontePython # used as the first element in the sys.path sys.path.insert(0, self.path['root']) for elem in experiments: folder = os.path.abspath(os.path.join( self.path['MontePython'], "likelihoods", "%s" % elem)) # add the folder of the likelihood to the path of libraries to... # ... import easily the likelihood.py program try: exec "from likelihoods.%s import %s" % ( elem, elem) except ImportError as message: raise io_mp.ConfigurationError( "Trying to import the %s likelihood" % elem + " as asked in the parameter file, and failed." " Please make sure it is in the `montepython/" "likelihoods` folder, and is a proper python " "module. Check also that the name of the class" " defined in the __init__.py matches the name " "of the folder. In case this is not enough, " "here is the original message: %s\n" % message) # Initialize the likelihoods. Depending on the values of # command_line and log_flag, the routine will call slightly # different things. If log_flag is True, the log.param will be # appended. try: exec "self.lkl['%s'] = %s('%s/%s.data',\ self, self.command_line)" % ( elem, elem, folder, elem) except KeyError as e: if e.find('clik') != -1: raise io_mp.ConfigurationError( "You should provide a 'clik' entry in the dictionary " "path defined in the file default.conf") else: raise io_mp.ConfigurationError( "The following key: '%s' was not found" % e)
[docs] def read_file(self, param, structure, field='', separate=False): """ Execute all lines concerning the Data class from a parameter file All lines starting with `data.` will be replaced by `self.`, so the current instance of the class will contain all the information. .. note:: A rstrip() was added at the end, because of an incomprehensible bug on some systems that imagined some inexistent characters at the end of the line... Now should work .. note:: A security should be added to protect from obvious attacks. Parameters ---------- param : str Name of the parameter file structure : str Name of the class entries we want to execute (mainly, data, or any other likelihood) Keyword Arguments ----------------- field : str If nothing is specified, this routine will execute all the lines corresponding to the `structure` parameters. If you specify a specific field, like `path`, only this field will be read and executed. separate : bool If this flag is set to True, a container class will be created for the structure field, so instead of appending to the namespace of the data instance, it will append to a sub-namespace named in the same way that the desired structure. This is used to extract custom values from the likelihoods, allowing to specify values for the likelihood directly in the parameter file. """ if separate: exec("self.%s = Container()" % structure) with open(param, 'r') as param_file: for line in param_file: if line.find('#') == -1 and line: lhs = line.split('=')[0] if lhs.find(structure+'.') != -1: if field: # If field is not an empty string, you want to skip # the execution of the line (exec statement) if you # do not find the exact searched field if lhs.find('.'.join([structure, field])) == -1: continue if not separate: exec(line.replace(structure+'.', 'self.').rstrip()) else: exec(line.replace( structure+'.', 'self.'+structure+'.').rstrip())
[docs] def group_parameters_in_blocks(self): """ Regroup mcmc parameters by blocks of same speed This method divides all varying parameters from :attr:`mcmc_parameters` into as many categories as there are likelihoods, plus one (the slow block of cosmological parameters). It creates the attribute :attr:`block_parameters`, to be used in the module :mod:`mcmc`. .. note:: It does not compute by any mean the real speed of each parameter, instead, every parameter belonging to the same likelihood will be considered as fast as its neighbour. .. warning:: It assumes that the nuisance parameters are already written sequentially, and grouped together (not necessarily in the order described in :attr:`experiments`). If you mix up the different nuisance parameters in the .param file, this routine will not method as intended. It also assumes that the cosmological parameters are written at the beginning of the file. """ array = [] # First obvious block is all cosmological parameters array.append(len(self.get_mcmc_parameters(['varying', 'cosmo']))) # Then, store all nuisance parameters nuisance = self.get_mcmc_parameters(['varying', 'nuisance']) # Create an array to keep track of the already taken into account # nuisance parameters. This will come in handy when using likelihoods # that share some nuisance parameters. used_nuisance = [] for likelihood in self.lkl.itervalues(): count = 0 for elem in nuisance: if elem in likelihood.nuisance: if elem not in used_nuisance: count += 1 used_nuisance.append(elem) likelihood.varying_nuisance_parameters = count # Then circle through them index = 0 while index < len(nuisance): elem = nuisance[index] flag = False # For each one, check if they belong to a likelihood for likelihood in self.lkl.itervalues(): if (elem in likelihood.nuisance) and (index < len(nuisance)): # If yes, store the number of nuisance parameters needed # for this likelihood. flag = True array.append( likelihood.varying_nuisance_parameters+array[-1]) index += likelihood.varying_nuisance_parameters continue if not flag: # If the loop reaches this part, it means this nuisance # parameter was associated with no likelihood: this should not # happen raise io_mp.ConfigurationError( "nuisance parameter %s " % elem + "is associated to no likelihood") # Store the result self.block_parameters = array # Setting a default value for the over_sampling array if not self.over_sampling: self.over_sampling = [1 for _ in range(len(self.block_parameters))] # Test that the over_sampling list has the same size as # block_parameters. else: try: assert len(self.block_parameters) == len(self.over_sampling) except AssertionError: raise io_mp.ConfigurationError( "The length of the over_sampling field should be" " equal to the number of blocks (one for cosmological " "parameters, plus one for each likelihood with " "nuisance parameters)") # Create a list of indices corresponding of the oversampling strategy self.assign_over_sampling_indices()
[docs] def assign_over_sampling_indices(self): """ Create the list of varied parameters given the oversampling """ self.over_sampling_indices = [] for index in range(len(self.get_mcmc_parameters(['varying']))): if index == 0: self.over_sampling_indices.append(index) else: block_index = self.block_parameters.index( [i for i in self.block_parameters if index < i][0]) for _ in range(self.over_sampling[block_index]): self.over_sampling_indices.append(index)
[docs] def read_version(self, param_file): """ Extract version and subversion from an existing log.param """ # Read the first line (cosmological code version) first_line = param_file.readline() param_file.seek(0) regexp = re.match( ".*\(branch: (.*), hash: (.*)\).*", first_line) version = first_line.split()[1] git_branch, git_version = regexp.groups() return version, git_version, git_branch
[docs] def get_mcmc_parameters(self, table_of_strings): """ Returns an ordered array of parameter names filtered by `table_of_strings`. Parameters ---------- table_of_strings : list List of strings whose role and status must be matched by a parameter. For instance, >>> data.get_mcmc_parameters(['varying']) ['omega_b', 'h', 'amplitude', 'other'] will return a list of all the varying parameters, both cosmological and nuisance ones (derived parameters being `fixed`, they wont be part of this list). Instead, >>> data.get_mcmc_parameters(['nuisance', 'varying']) ['amplitude', 'other'] will only return the nuisance parameters that are being varied. """ table = [] for key, value in self.mcmc_parameters.iteritems(): number = 0 for subvalue in value.itervalues(): for string in table_of_strings: if subvalue == string: number += 1 if number == len(table_of_strings): table.append(key) return table
[docs] def check_for_slow_step(self, new_step): """ Check whether the value of cosmological parameters were changed, and if no, skip computation of the cosmology. """ parameter_names = self.get_mcmc_parameters(['varying']) cosmo_names = self.get_mcmc_parameters(['cosmo']) need_change = 0 # For all elements in the varying parameters: for elem in parameter_names: i = parameter_names.index(elem) # If it is a cosmological parameter if elem in cosmo_names: if self.mcmc_parameters[elem]['current'] != new_step[i]: need_change += 1 # If any cosmological value was changed, if need_change > 0: self.need_cosmo_update = True else: self.need_cosmo_update = False for likelihood in self.lkl.itervalues(): # If the cosmology changed, you need to recompute the likelihood # anyway if self.need_cosmo_update: likelihood.need_update = True continue # Otherwise, check if the nuisance parameters of this likelihood # were changed need_change = 0 for elem in parameter_names: i = parameter_names.index(elem) if elem in likelihood.nuisance: if self.mcmc_parameters[elem]['current'] != new_step[i]: need_change += 1 if need_change > 0: likelihood.need_update = True else: likelihood.need_update = False
[docs] def update_cosmo_arguments(self): """ Put in :attr:`cosmo_arguments` the current values of :attr:`mcmc_parameters` This method is called at every step in the Markov chain, to update the dictionary. In the Markov chain, the scale is not remembered, so one has to apply it before giving it to the cosmological code. .. note:: When you want to define new parameters in the Markov chain that do not have a one to one correspondance to a cosmological name, you can redefine its behaviour here. You will find in the source several such examples. .. note:: For complex CLASS parameters, that expect a string of numbers separated with commas, you can now use the name of the argument, for instance :code:`m_ncdm`, then append a double underscore and a number. So if you run with two cosmological parameters, :code:`m_ncdm__1` and :code:`m_ncdm__2`, this function will automatically concatenate the two and feed class :code:`m_ncdm`. You still have to make sure that the other variables are properly set, like :code:`N_ncdm` to 2, in this example. """ # For all elements in any cosmological parameters for elem in self.get_mcmc_parameters(['cosmo']): # Fill in the dictionnary with the current value of parameters self.cosmo_arguments[elem] = \ self.mcmc_parameters[elem]['current'] *\ self.mcmc_parameters[elem]['scale'] # For all elements in the cosmological parameters from the mcmc list, # translate any-one that is not directly a CLASS parameter into one. # The try: except: syntax ensures that the first call for elem in self.get_mcmc_parameters(['cosmo']): # infer h from Omega_Lambda and delete Omega_Lambda if elem == 'Omega_Lambda': omega_b = self.cosmo_arguments['omega_b'] omega_cdm = self.cosmo_arguments['omega_cdm'] Omega_Lambda = self.cosmo_arguments['Omega_Lambda'] self.cosmo_arguments['h'] = math.sqrt( (omega_b+omega_cdm) / (1.-Omega_Lambda)) del self.cosmo_arguments[elem] # infer omega_cdm from Omega_L and delete Omega_L elif elem == 'Omega_L': omega_b = self.cosmo_arguments['omega_b'] h = self.cosmo_arguments['h'] Omega_L = self.cosmo_arguments['Omega_L'] self.cosmo_arguments['omega_cdm'] = (1.-Omega_L)*h*h-omega_b del self.cosmo_arguments[elem] elif elem == 'ln10^{10}A_s': self.cosmo_arguments['A_s'] = math.exp( self.cosmo_arguments[elem]) / 1.e10 del self.cosmo_arguments[elem] elif elem == 'exp_m_2_tau_As': tau_reio = self.cosmo_arguments['tau_reio'] self.cosmo_arguments['A_s'] = self.cosmo_arguments[elem] * \ math.exp(2.*tau_reio) del self.cosmo_arguments[elem] elif elem == 'f_cdi': self.cosmo_arguments['n_cdi'] = self.cosmo_arguments['n_s'] elif elem == 'beta': self.cosmo_arguments['alpha'] = 2.*self.cosmo_arguments['beta'] elif elem == 'M_tot': self.cosmo_arguments['m_ncdm'] = self.cosmo_arguments['M_tot']/3. del self.cosmo_arguments[elem] # Finally, deal with all the parameters ending with __i, where i is # an integer. Replace them all with their name without the trailing # double underscore, concatenated with each other. The test is # always on the one ending with __1, as it will be the first on the # list, and deal with all the others. elif re.search(r'__1', elem): original_name = re.search(r'(.*)__1', elem).groups()[0] # Recover the values of all the other elements values = [self.cosmo_arguments[elem]] for other_elem in self.get_mcmc_parameters(['cosmo']): match = re.search(r'%s__([2-9])' % original_name, other_elem) if match: values.append(self.cosmo_arguments[other_elem]) # create the cosmo_argument self.cosmo_arguments[original_name] = ', '.join( ['%g' % value for value in values]) # Delete the now obsolete entries of the dictionary for index in range(1, len(values)+1): del self.cosmo_arguments[ original_name + '__%i' % index]
@staticmethod
[docs] def folder_is_initialised(folder): """ Static method to call for checking if a folder was already initialised This method can be used to speed up the mpi initialisation in :mod:`run`. If a process finds that the folder is already a proper Monte Python one, it sends directly a 'go' signal to its next in line. .. warning:: This method assumes that the last lines of the log.param are the path indication. If this would ever change, adjust this method accordingly. """ # If the folder is not there, easy answer: False! if not os.path.isdir(folder): return False # Recover the log.param from the folder, and assert it exists log_param_path = os.path.join(folder, 'log.param') if not os.path.isfile(log_param_path): return False # Quickly load it to a string, and assert that the path has been # written (which are the last lines) with open(log_param_path, 'r') as log_param: text = log_param.readlines() if text[-1].find('path[') != -1: return True else: return False
[docs] def __cmp__(self, other): """ Redefinition of the 'compare' method for two instances of this class. It will decide which basic operations to perform when the code asked if two instances are the same (in case you want to launch a new chain in an existing folder, with your own parameter file) Comparing cosmological code versions (warning only, will not fail the comparison) """ if self.version != other.version: warnings.warn( "You are running with a different version of your " + "cosmological code") # Defines unordered version of the dictionaries of parameters self.uo_parameters = {} other.uo_parameters = {} # Check if all the experiments are tested again, if len(list(set(other.experiments).symmetric_difference( set(self.experiments)))) == 0: # Check that they have been called with the same .data file, stored # in dictionary when initializing. for experiment in self.experiments: for elem in self.lkl[experiment].dictionary: if self.lkl[experiment].dictionary[elem] != \ other.lkl[experiment].dictionary[elem]: print 'in your parameter file: ', print self.lkl[experiment].dictionary print 'in log.param: ', print other.lkl[experiment].dictionary return -1 # Fill in the unordered version of dictionaries for key, elem in self.mcmc_parameters.iteritems(): self.uo_parameters[key] = elem['initial'] for key, elem in other.mcmc_parameters.iteritems(): other.uo_parameters[key] = elem['initial'] # And finally compare them (standard comparison between # dictionnaries, will return True if both have the same keys and # values associated to them. return cmp(self.uo_parameters, other.uo_parameters) else: return -1
[docs] def __call__(self, ctx): """ Interface layer with CosmoHammer Store quantities to a the context, to be accessed by the Cosmo Module and each of the likelihoods. Parameters ---------- ctx : context Contains several dictionaries storing data and cosmological information """ # Recover the cosmological parameter value from the context parameters = ctx.getParams() # Storing them as current points for index, elem in enumerate(self.get_mcmc_parameters(["varying"])): self.mcmc_parameters[elem]['current'] = parameters[index] # Propagating this to the cosmo_arguments dictionary self.update_cosmo_arguments() # Store itself into the context ctx.add('data', self)
[docs]class Parameter(dict): """ Store all important fields, and define a few convenience methods """ def __init__(self, array, key): """ This class replaces the old function defined in the Data class, called `from_input_to_mcmc_parameters`. The traduction is now done inside the Parameter class, which interprets the array given as an input inside the parameter file, and returns a dictionary having all relevant fields initialized. .. warning:: This used to be an ordered dictionary, for no evident reason. It is now reverted back to an ordinary dictionary. If this broke anything, it will be reverted back At the end of this initialization, every field but one is filled for the specified parameter, be it fixed or varying. The missing field is the 'last_accepted' one, that will be filled in the module :mod:`mcmc`. .. note:: The syntax of the parameter files is defined here - if one wants to change it, one should report the changes in there. The other fields are Attributes ---------- initial : array Initial array of input values defined in the parameter file. Contains (in this order) `mean`, `minimum`, `maximum`, `1-sigma`. If the min/max values (**TO CHECK** proposal density boundaries) are unimportant/unconstrained, use `None` or `-1` (without a period !) scale : float 5th entry of the initial array in the parameter file, defines the factor with which to multiply the values defined in `initial` to give the real value. role : str 6th entry of the initial array, can be `cosmo`, `nuisance` or `derived`. A `derived` parameter will not be considered as varying, but will be instead recovered from the cosmological code for each point in the parameter space. prior : :class:`Prior <prior.Prior>` defined through the optional 7th entry of the initial array, can be ommited or set to `flat` (same), or set to `gaussian`. An instance of the :class:`prior` defined in :mod:`prior` will be initialized and set to this value. tex_name : str A tentative tex version of the name, provided by the function :func:`io_mp.get_tex_name`. status : str Depending on the `1-sigma` value in the initial array, it will be set to `fixed` or `varying` (resp. zero and non-zero) current : float Stores the value at the current point in parameter space (`not allowed initially`) Parameters ---------- value : list Array read from the parameter file key : str Name of the parameter """ # calling the parent method initialization dict.__init__(self) self['initial'] = array[0:4] self['scale'] = array[4] self['role'] = array[-1] self['tex_name'] = io_mp.get_tex_name(key) if array[3] == 0: self['status'] = 'fixed' self['current'] = array[0] else: self['status'] = 'varying' self['prior'] = prior.Prior(array)
class Container(object): """Dummy class to act as a namespace for data""" pass if __name__ == "__main__": import doctest import shutil from initialise import initialise folder = os.path.join('tests', 'doc') cosmo, data, command_line, _ = initialise('-o %s -p test.param' % folder) doctest.testmod(extraglobs={'data': data}) shutil.rmtree(folder)