Gaussian Likelihoods
This module provides the base classes for Gaussian likelihoods in SOLikeT, including support for combining multiple likelihoods with cross-covariances.
GaussianLikelihood
The base class for all Gaussian likelihoods. Subclasses should implement
the _get_theory() method to compute the theory prediction.
- class soliket.gaussian.GaussianLikelihood(info: Mapping[str, Any] = mappingproxy({}), name: str | None = None, timing: bool | None = None, packages_path: str | None = None, initialize=True, standalone=True)[source]
Bases:
LikelihoodBase class for Gaussian likelihoods in SOLikeT.
This class provides the infrastructure for computing Gaussian log-likelihoods from SACC data files. Subclasses must implement the
_get_theory()method to compute the theory prediction for the data vector.Parameters
- namestr
Name identifier for the likelihood (default: “Gaussian”)
- datapathstr
Path to the SACC file containing data and covariance
- use_spectrastr or list
Which spectra to use. Either “all” or a list of tracer pairs like
[("tracer1", "tracer2")]- ncovsimsint, optional
Number of simulations used to estimate covariance. If provided, applies the Hartlap correction factor to the inverse covariance.
Attributes
- dataGaussianData
The assembled Gaussian data object with covariance
- sacc_datasacc.Sacc
The loaded SACC data object
- xnp.ndarray
The bin centers (ell values)
- ynp.ndarray
The data vector
- covnp.ndarray
The covariance matrix
Examples
To create a custom Gaussian likelihood:
class MyLikelihood(GaussianLikelihood): name = "my_likelihood" _allowable_tracers = ("cmb_temperature", "cmb_polarization") def _get_theory(self, **params): # Compute theory prediction return theory_vector
- _get_theory(**params_values) ndarray[source]
Bin the unbinned theory per tracer combination via
get_binning().
- _get_unbinned_theory(**kwargs) list[ndarray][source]
Unbinned theory spectra on the fine multipole grid, one per tracer combination.
Subclasses that compute a finely-sampled theory and then bin it (e.g. the Limber cross-correlations) override this and inherit binning from the default
_get_theory(). Subclasses that produce binned theory directly override_get_theory()instead.
- _reorder_to_combo_major(sacc_data: Sacc) None[source]
Reorder
sacc_datain place so its data points are grouped by tracer combination, matching the order in which the theory vector is built.sacc.reorderpermutes the data and covariance together, so the data vector and covariance can never desynchronise.
- get_binning(tracer_comb: tuple) tuple[ndarray, ndarray][source]
Bandpower support multipoles and window matrix for a tracer pair.
The result depends only on the (fixed) SACC file, so it is memoised per tracer combination: each likelihood evaluation would otherwise re-derive it twice – once in
_get_unbinned_theory()for the theory’s ell support and once in_get_theory()to bin – each time hitting the SACC index and bandpower-window lookups.The window is looked up by tracers alone (not by data type: the shear cross-correlations are
cl_0e/cl_e0, notcl_00), so a pair carrying several data types would silently yield a window spanning all of them. Rejected here, in the shared lookup, rather than in each caller –_get_unbinned_theory()reads the ell support from here too, and would otherwise spend a Limber calculation on the doubled grid before_get_theory()noticed.
- get_unbinned_theory(**params_values) list[ndarray][source]
The unbinned theory spectra, one array per tracer combination.
Public accessor used by cross-covariance computations that need the theory before bandpower binning.
- logp(**params_values) float[source]
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.
MultiGaussianLikelihood
A likelihood that combines multiple Gaussian likelihoods, optionally accounting for cross-covariances between them.
- class soliket.gaussian.MultiGaussianLikelihood(info=mappingproxy({}), **kwargs)[source]
Bases:
GaussianLikelihoodA likelihood combining multiple Gaussian likelihoods with cross-covariances.
This class enables joint analysis of multiple datasets by combining their data vectors and covariance matrices. Cross-covariances between datasets can be specified via a
CrossCovobject stored in SACC format.Parameters
- componentslist of str
List of likelihood class names to combine, e.g.,
["soliket.mflike.MFLike", "soliket.lensing.LensingLikelihood"]- optionslist of dict
Configuration options for each component likelihood. Each dict should contain at minimum
datapathand any other required parameters.- cross_cov_pathstr, optional
Path to a SACC file containing cross-covariances between components. If not provided, components are assumed independent (zero cross-covariance).
Attributes
- likelihoodslist of Likelihood
The instantiated component likelihoods
- cross_covCrossCov or None
The loaded cross-covariance container
- dataMultiGaussianData
The combined data object with joint covariance
Examples
YAML configuration:
likelihood: soliket.MultiGaussianLikelihood: components: - soliket.mflike.MFLike - soliket.lensing.LensingLikelihood options: - datapath: /path/to/mflike.fits use_spectra: all - datapath: /path/to/lensing.fits cross_cov_path: /path/to/cross_cov.fits
Python usage:
from soliket import MultiGaussianLikelihood info = { "components": ["soliket.mflike.MFLike", "soliket.lensing.LensingLikelihood"], "options": [ {"datapath": "mflike.fits", "use_spectra": "all"}, {"datapath": "lensing.fits"}, ], "cross_cov_path": "cross_cov.fits", } like = MultiGaussianLikelihood(info)
- _get_theory(**kwargs) ndarray[source]
Bin the unbinned theory per tracer combination via
get_binning().
- classmethod get_defaults(return_yaml=False, yaml_expand_defaults=True, input_options=mappingproxy({}))[source]
Return defaults for this component_or_class, with syntax:
option: value [...] params: [...] # if required prior: [...] # if required
If keyword return_yaml is set to True, it returns literally that, whereas if False (default), it returns the corresponding Python dict.
Note that in external components installed as zip_safe=True packages files cannot be accessed directly. In this case using !default .yaml includes currently does not work.
Also note that if you return a dictionary it may be modified (return a deep copy if you want to keep it).
if yaml_expand_defaults then !default: file includes will be expanded
input_options may be a dictionary of input options, e.g. in case default params are dynamically dependent on an input variable
- get_helper_theories() dict[str, Theory][source]
Return dictionary of optional names and helper Theory instances that should be used in conjunction with this component. The helpers can be created here as only called once, and before any other use of helpers.
- Returns:
dictionary of names and Theory instances
- classmethod get_modified_defaults(defaults, input_options=mappingproxy({}))[source]
After defaults dictionary is loaded, you can dynamically modify them here as needed,e.g. to add or remove defaults[‘params’]. Use this when you don’t want the inheritance-recursive nature of get_defaults() or don’t only want to affect class attributes (like get_class_options() does0.
- get_requirements()[source]
Get a dictionary of requirements (or a list of requirement name, option tuples) that are always needed (e.g. must be calculated by another component or provided as input parameters).
- Returns:
dictionary or list of tuples of requirement names and options (or iterable of requirement names if no optional parameters are needed)
- initialize_with_provider(provider: Provider)[source]
Final initialization after parameters, provider and assigned requirements set. The provider is used to get the requirements of this theory using provider.get_X() and provider.get_param(‘Y’).
- Parameters:
provider – the
theory.Providerinstance that should be used by this component to get computed requirements
Usage Example
To combine multiple likelihoods (e.g., CMB TT and lensing) with cross-covariances:
likelihood:
soliket.MultiGaussianLikelihood:
components:
- soliket.mflike.MFLike
- soliket.lensing.LensingLikelihood
options:
- datapath: /path/to/mflike_data.fits
use_spectra: all
- datapath: /path/to/lensing_data.fits
cross_cov_path: /path/to/cross_covariance.fits
The cross_cov_path parameter is optional. If not provided, the likelihoods
are assumed to be independent (zero cross-covariance).
Component likelihoods may apply different scale cuts, so their data vectors need not share the same range: the cross-covariance is automatically trimmed to each probe’s retained bandpowers. Blocks are also aligned to the data by identity (per-bandpower keys) rather than by position, so the order in which the cross-covariance was built need not match the order produced at run time. See Identity alignment below.
CrossCov
A container for storing cross-covariances between likelihood components. Supports saving and loading in SACC format.
- class soliket.gaussian.CrossCov(*args, **kwargs)[source]
Bases:
dictLabelled-block covariance store for multi-component Gaussian likelihoods.
A
CrossCovis a labelled-block store: a dict whose keys are pairs of component names (e.g.("mflike", "lensing")) and whose values are the corresponding covariance blocks. Diagonal keys(name, name)hold a component’s auto-covariance; off-diagonal keys(name1, name2)hold a cross-covariance.add_componentandadd_cross_covarianceare the same underlying store operation — they only differ in whether the block lands on the diagonal or off it.Each block may carry per-axis bandpower identities (
ids), recorded in_block_ids_mapkeyed by the block’s(row, col)tuple. These labels let a block be aligned to a target order regardless of how it was stored: blocks may be supplied in any order, on a full (un-cut, shuffled) range, with no reliance on positional or borrowed ids.MultiGaussianDatacanonicalises the store to the data order at assembly time viato_canonical(), which fuses realignment and scale-cut trimming into one identity gather per axis. Supports saving and loading in SACC format for persistence.See
.claude/plans/2026-06-05-crosscov-labelled-blocks-design.mdfor the design rationale.Examples
Auto blocks via
add_componentand cross blocks viaadd_cross_covariance, optionally labelled with per-axis ids:cross_cov = CrossCov() cross_cov.add_component("mflike", mflike_cov, ids=mflike_ids) cross_cov.add_component("lensing", lensing_cov, ids=lensing_ids) cross_cov.add_cross_covariance( "mflike", "lensing", cross_block, ids1=mflike_ids, ids2=lensing_ids ) cross_cov.save("cross_cov.fits")
Auto-covariances may be omitted; assembly then falls back to each likelihood’s own
cov:cross_cov = CrossCov() cross_cov.add_cross_covariance("mflike", "lensing", cross_block) cross_cov.save("cross_cov.fits")
Loading:
cross_cov = CrossCov.load("cross_cov.fits") block = cross_cov[("mflike", "lensing")]
- add_component(name, cov, ids=None)[source]
Register a component’s auto-covariance (diagonal block).
- add_cross_covariance(name1, name2, cross_cov, ids1=None, ids2=None)[source]
Register the cross term between two components (off-diagonal block).
- property component_names: list[str]
Get ordered list of component names.
- classmethod from_cmb_lensing(mflike, lensing, **overrides)[source]
Compute the CMB-primary x CMB-lensing cross-covariance, labelled by id.
Convenience entry point: pulls fsky, fiducial cosmology/accuracy, the CMB bandpower windows and the kappa binning from the two evaluated likelihoods
mflikeandlensing(e.g.resolve_aliases(model).mflikeand.lensing– the concrete handles, not aSession), runs CAMB and the cross-covariance kernel, and returns a single labelled cross block ready tosave().overrides(fsky/cosmo/accuracy/lmax) are forwarded to the low-levelsoliket.cross_covariance.cmb_lensing_crosscov().Every block (the cross term and both auto-covariances) carries its bandpower identity, so the order in which any of them is built is irrelevant:
to_canonical()realigns each to the data by identity at assembly. The CMB rows use mflike’sspec_metavocabulary(pol, hasYX_xsp, (t1, t2), leff)– exactly whatsoliket.gaussian.gaussian.bandpower_ids()reconstructs for mflike – and the kappa columns reuse the lensing data’s own ids. The full-kappa columns are trimmed to the bins the lensing likelihood keeps, so the cross block and the lensing auto share one column identity.The component auto-covariances are carried alongside the cross block (keyed by the components’ real
GaussianDatanames) so the saved file is a self-contained joint covariance that drops straight into aMultiGaussianLikelihood. Both likelihoods’ model must already be evaluated, andmflikemust exposespec_meta.
- to_canonical(order: dict) ndarray[source]
Assemble the full joint covariance in the given per-component order.
order maps component name -> target ids (canonical; already scale-cut for a trimmed matrix) OR an int size (positional, when the data has no ids). Missing blocks are left as zeros. Realign and trim fuse into one gather per axis.
Usage Modes
Mode 1: Full covariance specification
Use add_component() to register each component with its auto-covariance,
then add_cross_covariance() for off-diagonal blocks:
from soliket.gaussian import CrossCov
cross_cov = CrossCov()
# Add auto-covariances
cross_cov.add_component("mflike", mflike_cov)
cross_cov.add_component("lensing", lensing_cov)
# Add cross-covariance
cross_cov.add_cross_covariance("mflike", "lensing", mflike_lensing_cov)
# Save to SACC format
cross_cov.save("cross_covariance.fits")
Mode 2: Cross-covariance only
If you only want to specify the cross-covariance (using auto-covariances
from individual likelihoods), just use add_cross_covariance():
cross_cov = CrossCov()
cross_cov.add_cross_covariance("mflike", "lensing", mflike_lensing_cov)
cross_cov.save("cross_covariance.fits")
When loaded by MultiGaussianLikelihood, the auto-covariances will be
taken from each individual likelihood’s SACC file.
Loading CrossCov
To load a previously saved cross-covariance:
from soliket.gaussian import CrossCov
cross_cov = CrossCov.load("cross_covariance.fits")
# Access blocks
mflike_lensing_block = cross_cov[("mflike", "lensing")]
Identity alignment
Each component may carry per-bandpower identity keys – a unique label for
every data point (e.g. (field, channel pair, ell)). When present,
MultiGaussianData aligns each covariance block to the data by matching these
keys instead of relying on position, so a cross-covariance keeps working even if
the data is reordered or scale-cut differently at run time. Missing or duplicate
keys raise a clear error rather than silently corrupting the joint covariance.
Identity keys are optional, supplied via ids and persisted by save() /
load():
cross_cov.add_component("mflike", mflike_cov, ids=mflike_ids)
cross_cov.add_component("lensing", lensing_cov, ids=lensing_ids)
cross_cov.add_cross_covariance("mflike", "lensing", mflike_lensing_cov)
SOLikeT GaussianLikelihood subclasses populate these keys automatically from
their SACC file, and MultiGaussianLikelihood also sources them for external
components such as mflike (via soliket.gaussian.gaussian.bandpower_ids).
If keys are omitted, blocks fall back to positional alignment.
GaussianData
Low-level data container for named multivariate Gaussian data.
- class soliket.gaussian.GaussianData(name, x: Sequence, y: Sequence[float], cov: ndarray, ncovsims: int | None = None, indices: ndarray | None = None, ids: Sequence | None = None)[source]
Bases:
objectContainer for named multivariate Gaussian data.
Stores a data vector with its covariance matrix and provides methods for computing the Gaussian log-likelihood.
Parameters
- namestr
Name identifier for the data
- xSequence
Labels or coordinates for each data point (e.g., ell values)
- ySequence[float]
The data vector values
- covnp.ndarray
Covariance matrix with shape (n, n) where n = len(x)
- ncovsimsint, optional
Number of simulations used to estimate covariance. If provided, applies the Hartlap correction factor to the inverse covariance.
- indicesnp.ndarray, optional
Boolean array for trimming cross-covariances when scale cuts are applied
- idssequence, optional
Per-bandpower identity keys over the FULL (pre-cut) range, i.e. one key per element of
indices. LetsMultiGaussianDataalign cross- covariance blocks to the data by identity instead of by position.
Attributes
- inv_covnp.ndarray
Inverse covariance matrix (with Hartlap correction if applicable)
- norm_constfloat
Normalization constant for the Gaussian likelihood
Raises
- ValueError
If dimensions of x, y, and cov are incompatible If covariance matrix has non-positive determinant
MultiGaussianData
Assembles multiple GaussianData objects into a joint data vector
with combined covariance matrix.
- class soliket.gaussian.MultiGaussianData(data_list, cross_covs=None)[source]
Bases:
GaussianDataCombined Gaussian data from multiple components with cross-covariances.
Assembles multiple
GaussianDataobjects into a single joint data vector with a combined covariance matrix that includes both auto-covariances and cross-covariances between components.A thin caller over the
CrossCovstore: thedata_listis the single source of truth for order. Each component’s target order is its data vector’s own ids (already scale-cut), and the cross-covariance blocks are aligned to it by identity viaCrossCov.to_canonical(). Alignment is decided per axis: an identity gather when both the target and the block carry ids; positional alignment only when neither does; and a raise when the data carries ids but a block does not (it refuses to guess, since the data is reordered to canonical order). Missing auto blocks fall back to each likelihood’s owncov, which is already in canonical data order.See
.claude/plans/2026-06-05-crosscov-labelled-blocks-design.mdfor the design rationale.Parameters
- data_listlist of GaussianData
Individual data objects to combine
- cross_covsCrossCov, optional
Labelled-block cross-covariance store. If None, components are assumed independent. Auto-covariances can come from either the CrossCov or the individual GaussianData objects (individual data is used whenever the CrossCov doesn’t contain an auto-covariance for a component).
Attributes
- data_listlist of GaussianData
The original individual data objects
- nameslist of str
Names of all components
- lengthslist of int
Data vector lengths for each component
- labelslist of str
Component name for each element in the combined data vector
Examples
Combining two datasets with cross-covariance:
data1 = GaussianData("mflike", x1, y1, cov1) data2 = GaussianData("lensing", x2, y2, cov2) cross_cov = CrossCov() cross_cov.add_cross_covariance("mflike", "lensing", cross_block) multi_data = MultiGaussianData([data1, data2], cross_cov) # Access combined properties print(multi_data.cov.shape) # (n1 + n2, n1 + n2) loglike = multi_data.loglike(theory_vector)