Likelihoods

Input: specifying likelihoods to explore

Likelihoods are specified under the likelihood block of your input .yaml file, together with their options:

likelihood:
  [likelihood 1]:
     [option 1]: [value 1]
     [...]
  [likelihood 2]:
     [option 1]: [value 1]
     [...]

Likelihood parameters are specified within the params block, as explained in Parameters and priors.

cobaya comes with a number of internal general and cosmological likelihoods. You can also define your external likelihoods with simple Python functions, or by implementing a Python class defined in an external module. If your likelihood is just a simple Python functions, using an external function can be convenient. Any likelihood using data files or with more complex dependencies is best implemented as a new likelihood class.

External likelihood functions

External likelihood functions can be used by using input of the form:

likelihood:
  # Simple way (does not admit additional options)
  my_lik_1: [definition]
  # Alternative way (can also take speeds, etc)
  my_lik_1:
    external: [definition]
    speed: [...]
    [more options]

The [definition] follows the exact same rules as external priors, so check out that section for the details.

The only difference with external priors is that external likelihoods can provide derived parameters. To do that:

  1. In your function, return a tuple of the log-likelihood and a dictionary of derived parameter values {derived_1: value_1, [...]}.

  2. When preparing Cobaya’s input, add to your external likelihood info an option output_params listing the names of the available derived parameters.

For an application, check out the advanced example.

If your external likelihood needs the products of a theory code:

  1. In your function definition, define a keyword argument _self through which at runtime you will get access to an instance of the Cobaya likelihood wrapper of your function.

  2. When preparing Cobaya’s input, add to your external likelihood info an option requires stating the requirements of your likelihood.

  3. At run-time, you can call get_[...] methods of _self.provider to get the requested quantities.

For an application, check out Creating your own cosmological likelihood.

Note

Obviously, _self is a reserved parameter name that you cannot use as an argument in your likelihood definition, except for the purposes explained above.

Note

The input parameters of an external function are guessed from its definition. But in some cases you may prefer not to define your likelihood function with explicit arguments (e.g. if the number of them may vary). In that case, you can manually specify the input parameters via the input_params option of the likelihood definition in your input dictionary.

E.g. the following two snippets are equivalent, but in the second one, one can alter the input parameters programmatically:

# Default: guessed from function signature

def my_like(a0, a1):
    logp =  # some function of `(a0, a1)`
    devived = {"sum_a": a0 + a1}
    return logp, derived

info_like = {"my_likelihood": {
    "external": my_like, "output_params": ["sum_a"]}}
# Manual: no explicit function signature required

# Define the lists of input params
my_input_params = ["a0", "a1"]

def my_like(**kwargs):
    current_input_values = [kwargs[p] for p in my_input_params]
    logp =  # some function of the input params
    derived = {"sum_a": sum(current_input_values)}
    return logp, derived

info_like = {"my_likelihood": {
    "external": my_like,
    "input_params": my_input_params, "output_params": ["sum_a"]}}

Likelihood classes: code conventions and defaults

Each likelihood inherits from the likelihood.Likelihood class (see below).

Internal likelihoods are defined inside the likelihoods directory of the Cobaya source tree, where each subdirectory defines a subpackage containing one or more likelihoods. External likelihood classes can be defined in an external python package.

In addition to the likelihood class itself, each likelihood can have additional files:

  • a [ClassName].yaml file containing allowed options for each likelihood and the default experimental model:

    # Default options
    [option 1]: [value 1]
    [...]
    
    # Experimental model parameters
    params:
      [param 1]:
        prior:
          [prior info]
        [label, ref, etc.]
    prior:
      [prior 1]: [definition]
    

    The options and parameters defined in this file are the only ones recognized by the likelihood, and they are loaded automatically with their default values (options) and priors (parameters) by simply mentioning the likelihood in the input file, where one can re-define any of those options with a different prior, value, etc (see Changing and redefining parameters; inheritance). The same parameter may be defined by different likelihoods; in those cases, it needs to have the same default information (prior, label, etc.) in the defaults file of all the likelihoods using it.

  • An optional [ClassName].bibtex file containing bibtex references associated with each likelihood, if relevant. Inherited likelihoods can be used to share a common .bibtex file, since the bibtex file is use by all descendants unless overridden.

Note

Likelihood class options are inherited from any ancestor class .yaml files or class attributes. So there are some user-defined options that are common to all likelihoods and do not need to be specified in the defaults [ClassName].yaml file, such as the computational speed of the likelihood (see Taking advantage of a speed hierarchy).

It is up to you where to define your likelihood class(es): the __init__ file can define a class [ClassName] directly, or you can define a class in a module.py file inside the likelihood directory (subpackage).

Using an internal likelihood class

Assuming your __init__ file defines the class, or imports it (from .module_name import ClassName), when running Cobaya you can reference the internal likelihood using:

likelihood:
  directory_name.ClassName:
    [option 1]: [value 1]
    [...]

If you defined the class in module_name.py then you would reference it as

likelihood:
  directory_name.module_name.ClassName:
    [option 1]: [value 1]
    [...]

If the class name is the same as the module name it can be omitted.

Custom names and multiple instances of the same internal class

To rename an internal likelihood to your liking, or to use simultaneously several instances of the same internal likelihood class (e.g. with different options), give each instance a name of your choice and specify the original likelihood name using the class keyword:

likelihood:
  my_like_1:
    class: directory_name.ClassName
    [option 1]: [value 1]
    [...]
  my_like_2:
    class: directory_name.ClassName
    [option 1]: [value 1]
    [...]

Using an external likelihood class

If you have a package called mycodes, containing a likelihood class called MyLike in mycodes.mylikes, when running Cobaya you can use the input

likelihood:
  mycodes.mylikes.MyLike:
    [option 1]: [value 1]
    [...]

This is assuming that mycodes is on your Python path (e.g. it is an installed package). You can also specify an explicit path for the module, e.g.

likelihood:
  mycodes.mylikes.MyLike:
    python_path: /path/to/mycodes_dir
    [option 1]: [value 1]
    [...]

If MyLike is imported by your package __init__ you can also simply reference it as mycodes.MyLike.

Implementing your own likelihood class

For your likelihood class you just need to define a few standard class methods:

  • A logp() method taking a dictionary of (sampled) parameter values and returning a log-likelihood.

  • An (optional) initialize() method preparing any computation, importing any necessary code, etc.

  • An (optional) get_requirements() method returning dictionary of requests from a theory code component, if needed.

The latter two methods are standard for all Likelihood and Theory components, and are inherited from the base Theory class. There are also a number of other methods that you can also implement for more advanced usage.

Options defined in the [ClassName].yaml are automatically accessible as attributes of your likelihood class at runtime, with values that can be overridden by the input .yaml file used for the run. If you prefer you can also define class attributes directly, rather than using a .yaml file (private class attributes that cannot be changed by input parameters should start with an underscore). If you define parameters in the .yaml you may also want to define their type in the Python source by adding an annotation, which will make it easier to perform automated checks on your code.

For an example class implementation and how to support data file auto-installation, check out Creating your own cosmological likelihood class. There is also a simple external likelihood package and a real-word cosmology example in the sample external CMB likelihood. You could also use the Gaussian mixtures likelihood as a guide.

A likelihood package becomes an internal likelihood if you move it into Cobaya’s likelihoods directory, but usually this is not necessary. Keeping your likelihood in a separate packages makes it easier to separately update the codes , and you can their easily publicly distribute your likelihood package (just having Cobaya as a package requirement).

If your likelihood depends on the calculation of some specific observable (typically depending on input parameters), you may want to split the calculation of the observable into a separate Theory class. This would allow different likelihoods to share the same theory calculation result, and also allow speed optimization if computing the likelihood is much faster than calculating the observables that it depends on. To understand how to make your own Theory and Likelihood classes, and handle dependencies between then, see Creating theory classes and dependencies.

Likelihood module

Synopsis:

Likelihood class and likelihood collection

Author:

Jesus Torrado and Antony Lewis

This module defines the main Likelihood class, from which every likelihood usually inherits, and the LikelihoodCollection class, which groups and manages all the individual likelihoods.

Likelihoods inherit from Theory, adding an additional method to return the likelihood. As with all theories, likelihoods cache results, and the property LikelihoodInterface.current_logp() is used by model.Model to calculate the total likelihood. The default Likelihood implementation does the actual calculation of the log likelihood in the logp function, which is then called by Likelihood.calculate() to save the result into the current state.

Subclasses typically just provide the logp function to define their likelihood result, and use get_requirements() to specify which inputs are needed from other theory codes (or likelihoods). Other methods of the Theory base class can be used as and when needed.

Likelihood class

class likelihood.Likelihood(info=mappingproxy({}), name=None, timing=None, packages_path=None, initialize=True, standalone=True)

Base class for likelihoods. Extends from LikelihoodInterface and the general Theory class by adding functions to return likelihoods functions (logp function for a given point).

logp(**params_values)

Computes and returns the log likelihood value. Takes as keyword arguments the parameter values. To get the derived parameters, pass a _derived keyword with an empty dictionary.

Alternatively you can just implement calculate() and save the log likelihood into state[‘logp’]; this may be more convenient if you also need to also calculate other quantities.

marginal(directions=None, params_values=None)

(For analytic likelihoods only.) Computes the marginal likelihood. If nothing is specified, returns the total marginal likelihood. If some directions are specified (as a list, tuple or array), returns the marginal likelihood pdf over those directions evaluated at the given parameter values.

calculate(state, want_derived=True, **params_values_dict)

Calculates the likelihood and any derived parameters or needs. Return False is the calculation fails.

Likelihood interface

class likelihood.LikelihoodInterface

Interface function for likelihoods. Can descend from a Theory class and this to make a likelihood (where the calculate() method stores state[‘logp’] for the current parameters), or likelihoods can directly inherit from Likelihood instead.

The current_logp property returns the current state’s logp as a scalar, and does not normally need to be changed.

property current_logp: float

Gets log likelihood for the current point.

Returns:

log likelihood from the current state as a scalar

LikelihoodCollection class

class likelihood.LikelihoodCollection(info_likelihood, packages_path=None, timing=None, theory=None)

Bases: ComponentCollection

A dictionary storing experimental likelihood Likelihood instances index by their names.