diff --git a/0.10.0/fitk.html b/0.10.0/fitk.html index f2ca455..5ebc08f 100644 --- a/0.10.0/fitk.html +++ b/0.10.0/fitk.html @@ -99,8 +99,8 @@
1r""" - 2### FITK - Fisher Information ToolKit, version 0.11.dev1 + 2### FITK - Fisher Information ToolKit, version 0.11.1 3 4# What is it? 5 diff --git a/0.11.dev1/fitk/derivatives.html b/0.11.1/fitk/derivatives.html similarity index 99% rename from 0.11.dev1/fitk/derivatives.html rename to 0.11.1/fitk/derivatives.html index bf169b4..8e575fa 100644 --- a/0.11.dev1/fitk/derivatives.html +++ b/0.11.1/fitk/derivatives.html @@ -127,8 +127,8 @@ API Documentation Select an option master + 0.11.1 0.11.0 - 0.11.dev1 0.10.4 0.10.3 0.10.2 diff --git a/0.11.dev1/fitk/graphics.html b/0.11.1/fitk/graphics.html similarity index 99% rename from 0.11.dev1/fitk/graphics.html rename to 0.11.1/fitk/graphics.html index 31dc76d..99f090a 100644 --- a/0.11.dev1/fitk/graphics.html +++ b/0.11.1/fitk/graphics.html @@ -220,8 +220,8 @@ API Documentation Select an option master + 0.11.1 0.11.0 - 0.11.dev1 0.10.4 0.10.3 0.10.2 diff --git a/0.11.dev1/fitk/interfaces.html b/0.11.1/fitk/interfaces.html similarity index 98% rename from 0.11.dev1/fitk/interfaces.html rename to 0.11.1/fitk/interfaces.html index eeac90b..20e64ab 100644 --- a/0.11.dev1/fitk/interfaces.html +++ b/0.11.1/fitk/interfaces.html @@ -63,8 +63,8 @@ Submodules Select an option master + 0.11.1 0.11.0 - 0.11.dev1 0.10.4 0.10.3 0.10.2 @@ -163,6 +163,13 @@ fitk.interfaces.coffe_interfaces +CAMB + + +installable with pip install fitk[camb] +for documentation of available interfaces, see fitk.interfaces.camb_interfaces + + Computation of custom derivatives To define a new interface for computing derivatives, one should define a class @@ -265,68 +272,73 @@ Computation of custom derivatives29 target="_blank" rel="noopener noreferrer">here</a>) 30- for documentation of available interfaces, see `fitk.interfaces.coffe_interfaces` 31 -32### Computation of custom derivatives -33To define a new interface for computing derivatives, one should define a class -34that inherits from `fitk.derivatives.FisherDerivative`, and implements -35either the `signal` or the `covariance` methods (or both); below outlines the -36steps to create an interface of your own using a minimal amount of code: -37 -38```python -39from __future__ import annotations # required for Python 3.7 -40import numpy as np -41from fitk import FisherDerivative +32#### <a href="https://github.com/cmbant/CAMB" target="_blank" rel="noopener noreferrer">CAMB</a> +33 +34- installable with `pip install fitk[camb]` +35- for documentation of available interfaces, see `fitk.interfaces.camb_interfaces` +36 +37### Computation of custom derivatives +38To define a new interface for computing derivatives, one should define a class +39that inherits from `fitk.derivatives.FisherDerivative`, and implements +40either the `signal` or the `covariance` methods (or both); below outlines the +41steps to create an interface of your own using a minimal amount of code: 42 -43# optional: if your code has a Python interface, you should import it here -44import mycode -45 -46class MyFisher(FisherDerivative): -47 # define a signal function, so you can compute -48 # derivatives w.r.t. it -49 def signal( -50 self, -51 *args: tuple[str, float], -52 **kwargs, -53 ): -54 for name, value in args: -55 # go through the parameters and set them -56 ... -57 # do the calculation using the external module, or some other means -58 ... -59 # the returned result _must_ be a 1-dimensional numpy array -60 return np.array([...]) -61 -62 # define a covariance function, so you can compute -63 # derivatives w.r.t. it -64 def covariance( -65 self, -66 *args: tuple[str, float], -67 **kwargs, -68 ): -69 for name, value in args: -70 # go through the parameters and set them -71 ... -72 # do the computation -73 ... -74 # the returned result _must_ be a 2-dimensional, square, numpy array, -75 # with the same number of elements as the output of the `signal` -76 # method -77 return np.array([...]) -78``` -79 -80The following are some general recommendations when creating a new interface: -81 -82- for ease of use, any additional parameters specifying the configuration for -83 the interface should be passed to the constructor (i.e. the ``__init__`` -84 method) -85- extra information (methods, members, custom parameters) should be documented -86 accordingly -87- if the external module could not be imported, rather than directly raising an -88 exception, it is preferable that instantiating a class from that module -89 raises an ``ImportError`` instead. A simple way to accomplish this is to wrap -90 the import in a ``try...except ImportError``, pass the success result (perhaps -91 stored as a boolean) to the constructor of the class, and then only raise the -92 ``ImportError`` there -93""" +43```python +44from __future__ import annotations # required for Python 3.7 +45import numpy as np +46from fitk import FisherDerivative +47 +48# optional: if your code has a Python interface, you should import it here +49import mycode +50 +51class MyFisher(FisherDerivative): +52 # define a signal function, so you can compute +53 # derivatives w.r.t. it +54 def signal( +55 self, +56 *args: tuple[str, float], +57 **kwargs, +58 ): +59 for name, value in args: +60 # go through the parameters and set them +61 ... +62 # do the calculation using the external module, or some other means +63 ... +64 # the returned result _must_ be a 1-dimensional numpy array +65 return np.array([...]) +66 +67 # define a covariance function, so you can compute +68 # derivatives w.r.t. it +69 def covariance( +70 self, +71 *args: tuple[str, float], +72 **kwargs, +73 ): +74 for name, value in args: +75 # go through the parameters and set them +76 ... +77 # do the computation +78 ... +79 # the returned result _must_ be a 2-dimensional, square, numpy array, +80 # with the same number of elements as the output of the `signal` +81 # method +82 return np.array([...]) +83``` +84 +85The following are some general recommendations when creating a new interface: +86 +87- for ease of use, any additional parameters specifying the configuration for +88 the interface should be passed to the constructor (i.e. the ``__init__`` +89 method) +90- extra information (methods, members, custom parameters) should be documented +91 accordingly +92- if the external module could not be imported, rather than directly raising an +93 exception, it is preferable that instantiating a class from that module +94 raises an ``ImportError`` instead. A simple way to accomplish this is to wrap +95 the import in a ``try...except ImportError``, pass the success result (perhaps +96 stored as a boolean) to the constructor of the class, and then only raise the +97 ``ImportError`` there +98"""
pip install fitk[camb]
fitk.interfaces.camb_interfaces
To define a new interface for computing derivatives, one should define a class @@ -265,68 +272,73 @@
52class ClassyBaseDerivative(ABC, FisherDerivative): - 53 r"""Base interface for CLASS.""" - 54 - 55 software_names = "classy" - 56 urls = dict(github="https://github.com/lesgourg/class_public") - 57 version = "1.0.0" - 58 authors = [ - 59 dict( - 60 name="Goran Jelic-Cizmek", - 61 email="goran.jelic-cizmek@unige.ch", - 62 ) - 63 ] - 64 __imported__ = IMPORT_SUCCESS - 65 - 66 def __init__( - 67 self, - 68 *args, - 69 config: Optional[dict] = None, - 70 **kwargs, - 71 ): - 72 """ - 73 Create an instance. - 74 - 75 Parameters - 76 ---------- - 77 config : dict, optional - 78 the CLASS configuration to use. All parameters are accepted. - 79 """ - 80 if not self.__imported__: - 81 raise ImportError( - 82 f"Unable to import the `{self.software_names}` module, " - 83 "please make sure it is installed; " - 84 f"for additional help, please consult one of the following URL(s): {self.urls}" - 85 ) - 86 - 87 self._config = config if config is not None else {} - 88 super().__init__(*args, **kwargs) - 89 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore - 90 - 91 @classmethod - 92 def from_file(cls, path: Union[str, Path]): - 93 r""" - 94 Load a CLASS configuration from a file. - 95 - 96 Parameters - 97 ---------- - 98 path : str or Path - 99 the path to the configuration file -100 """ -101 contents = Path(path).read_text(encoding="utf-8").split("\n") -102 config = ConfigParser(inline_comment_prefixes=("#", ";")) -103 config.optionxform = lambda option: option # type: ignore -104 section = "default" -105 config.read_string(f"[{section}]" + "\n".join(contents)) -106 -107 final_config = { -108 key: value for key, value in dict(config[section]).items() if value -109 } -110 -111 return cls(config=final_config) -112 -113 @property -114 def config(self): -115 """ -116 Return the current configuration used for running CLASS. -117 -118 .. versionchanged:: 0.10.0 -119 member is now read-only -120 """ -121 return self._config -122 -123 def _parse_outputs(self): -124 raw_output = self.config.get("output", "") -125 -126 if isinstance(raw_output, (tuple, list, np.ndarray)): -127 outputs = raw_output -128 else: -129 outputs = {item.strip() for item in raw_output.split(",")} -130 -131 return { -132 "temperature": "tCl" in outputs, -133 "polarization": "pCl" in outputs, -134 } -135 -136 def _run_classy(self, *args): # pylint: disable=method-hidden -137 r""" -138 Run classy and returns the instance of it after computation. -139 -140 Notes -141 ----- -142 The method is cached using <a -143 href="https://docs.python.org/3/library/functools.html#functools.lru_cache" -144 target="_blank" rel="noreferrer noopener">``functools.lru_cache``</a> -145 """ -146 cosmo = classy.Class() # pylint: disable=c-extension-no-member -147 final_kwargs = {**self.config} -148 for name, value in args: -149 final_kwargs[name] = value -150 cosmo.set(final_kwargs) -151 cosmo.compute() -152 -153 return cosmo + 53class ClassyBaseDerivative(ABC, FisherDerivative): + 54 r"""Base interface for CLASS.""" + 55 + 56 software_names = "classy" + 57 urls = dict(github="https://github.com/lesgourg/class_public") + 58 version = "1.0.0" + 59 authors = [ + 60 dict( + 61 name="Goran Jelic-Cizmek", + 62 email="goran.jelic-cizmek@unige.ch", + 63 ) + 64 ] + 65 __imported__ = IMPORT_SUCCESS + 66 + 67 def __init__( + 68 self, + 69 *args, + 70 config: Optional[dict] = None, + 71 **kwargs, + 72 ): + 73 """ + 74 Create an instance. + 75 + 76 Parameters + 77 ---------- + 78 config : dict, optional + 79 the CLASS configuration to use. All parameters are accepted. + 80 """ + 81 if not self.__imported__: + 82 raise ImportError( + 83 f"Unable to import the `{self.software_names}` module, " + 84 "please make sure it is installed; " + 85 f"for additional help, please consult one of the following URL(s): {self.urls}" + 86 ) + 87 + 88 self._config = config if config is not None else {} + 89 super().__init__(*args, **kwargs) + 90 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore + 91 + 92 @classmethod + 93 def from_file(cls, path: Union[str, Path]): + 94 r""" + 95 Load a CLASS configuration from a file. + 96 + 97 Parameters + 98 ---------- + 99 path : str or Path +100 the path to the configuration file +101 """ +102 contents = Path(path).read_text(encoding="utf-8").split("\n") +103 config = ConfigParser(inline_comment_prefixes=("#", ";")) +104 config.optionxform = lambda option: option # type: ignore +105 section = "default" +106 config.read_string(f"[{section}]" + "\n".join(contents)) +107 +108 final_config = { +109 key: value for key, value in dict(config[section]).items() if value +110 } +111 +112 return cls(config=final_config) +113 +114 @property +115 def config(self): +116 """ +117 Return the current configuration used for running CLASS. +118 +119 .. versionchanged:: 0.10.0 +120 member is now read-only +121 """ +122 return self._config +123 +124 def _parse_outputs(self): +125 raw_output = self.config.get("output", "") +126 +127 if isinstance(raw_output, (tuple, list, np.ndarray)): +128 outputs = raw_output +129 else: +130 outputs = {item.strip() for item in raw_output.split(",")} +131 +132 return { +133 "temperature": "tCl" in outputs, +134 "polarization": "pCl" in outputs, +135 "galaxy_counts": "nCl" in outputs or "dCl" in outputs, +136 } +137 +138 def _run_classy(self, *args): # pylint: disable=method-hidden +139 r""" +140 Run classy and returns the instance of it after computation. +141 +142 Notes +143 ----- +144 The method is cached using <a +145 href="https://docs.python.org/3/library/functools.html#functools.lru_cache" +146 target="_blank" rel="noreferrer noopener">``functools.lru_cache``</a> +147 """ +148 cosmo = classy.Class() # pylint: disable=c-extension-no-member +149 final_kwargs = {**self.config} +150 for name, value in args: +151 final_kwargs[name] = value +152 cosmo.set(final_kwargs) +153 cosmo.compute() 154 -155 def _parse_covariance_kwargs(self, **kwargs): -156 """Parse any keyword arguments for the covariance.""" -157 final_kwargs = {} -158 final_kwargs["fsky"] = float(kwargs.pop("fsky", 1)) -159 final_kwargs["delta_ell"] = int( -160 kwargs.pop("delta_ell", 2 / final_kwargs["fsky"]) -161 ) -162 -163 return final_kwargs +155 return cosmo +156 +157 def _parse_covariance_kwargs(self, **kwargs): +158 """Parse any keyword arguments for the covariance.""" +159 final_kwargs = {} +160 final_kwargs["fsky"] = float(kwargs.pop("fsky", 1)) +161 final_kwargs["delta_ell"] = round( +162 kwargs.pop("delta_ell", 2 / final_kwargs["fsky"]) +163 ) +164 +165 return final_kwargs @@ -693,30 +917,30 @@ Notes
53class ClassyBaseDerivative(ABC, FisherDerivative): + 54 r"""Base interface for CLASS.""" + 55 + 56 software_names = "classy" + 57 urls = dict(github="https://github.com/lesgourg/class_public") + 58 version = "1.0.0" + 59 authors = [ + 60 dict( + 61 name="Goran Jelic-Cizmek", + 62 email="goran.jelic-cizmek@unige.ch", + 63 ) + 64 ] + 65 __imported__ = IMPORT_SUCCESS + 66 + 67 def __init__( + 68 self, + 69 *args, + 70 config: Optional[dict] = None, + 71 **kwargs, + 72 ): + 73 """ + 74 Create an instance. + 75 + 76 Parameters + 77 ---------- + 78 config : dict, optional + 79 the CLASS configuration to use. All parameters are accepted. + 80 """ + 81 if not self.__imported__: + 82 raise ImportError( + 83 f"Unable to import the `{self.software_names}` module, " + 84 "please make sure it is installed; " + 85 f"for additional help, please consult one of the following URL(s): {self.urls}" + 86 ) + 87 + 88 self._config = config if config is not None else {} + 89 super().__init__(*args, **kwargs) + 90 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore + 91 + 92 @classmethod + 93 def from_file(cls, path: Union[str, Path]): + 94 r""" + 95 Load a CLASS configuration from a file. + 96 + 97 Parameters + 98 ---------- + 99 path : str or Path +100 the path to the configuration file +101 """ +102 contents = Path(path).read_text(encoding="utf-8").split("\n") +103 config = ConfigParser(inline_comment_prefixes=("#", ";")) +104 config.optionxform = lambda option: option # type: ignore +105 section = "default" +106 config.read_string(f"[{section}]" + "\n".join(contents)) +107 +108 final_config = { +109 key: value for key, value in dict(config[section]).items() if value +110 } +111 +112 return cls(config=final_config) +113 +114 @property +115 def config(self): +116 """ +117 Return the current configuration used for running CLASS. +118 +119 .. versionchanged:: 0.10.0 +120 member is now read-only +121 """ +122 return self._config +123 +124 def _parse_outputs(self): +125 raw_output = self.config.get("output", "") +126 +127 if isinstance(raw_output, (tuple, list, np.ndarray)): +128 outputs = raw_output +129 else: +130 outputs = {item.strip() for item in raw_output.split(",")} +131 +132 return { +133 "temperature": "tCl" in outputs, +134 "polarization": "pCl" in outputs, +135 "galaxy_counts": "nCl" in outputs or "dCl" in outputs, +136 } +137 +138 def _run_classy(self, *args): # pylint: disable=method-hidden +139 r""" +140 Run classy and returns the instance of it after computation. +141 +142 Notes +143 ----- +144 The method is cached using <a +145 href="https://docs.python.org/3/library/functools.html#functools.lru_cache" +146 target="_blank" rel="noreferrer noopener">``functools.lru_cache``</a> +147 """ +148 cosmo = classy.Class() # pylint: disable=c-extension-no-member +149 final_kwargs = {**self.config} +150 for name, value in args: +151 final_kwargs[name] = value +152 cosmo.set(final_kwargs) +153 cosmo.compute() 154 -155 def _parse_covariance_kwargs(self, **kwargs): -156 """Parse any keyword arguments for the covariance.""" -157 final_kwargs = {} -158 final_kwargs["fsky"] = float(kwargs.pop("fsky", 1)) -159 final_kwargs["delta_ell"] = int( -160 kwargs.pop("delta_ell", 2 / final_kwargs["fsky"]) -161 ) -162 -163 return final_kwargs +155 return cosmo +156 +157 def _parse_covariance_kwargs(self, **kwargs): +158 """Parse any keyword arguments for the covariance.""" +159 final_kwargs = {} +160 final_kwargs["fsky"] = float(kwargs.pop("fsky", 1)) +161 final_kwargs["delta_ell"] = round( +162 kwargs.pop("delta_ell", 2 / final_kwargs["fsky"]) +163 ) +164 +165 return final_kwargs
66 def __init__( -67 self, -68 *args, -69 config: Optional[dict] = None, -70 **kwargs, -71 ): -72 """ -73 Create an instance. -74 -75 Parameters -76 ---------- -77 config : dict, optional -78 the CLASS configuration to use. All parameters are accepted. -79 """ -80 if not self.__imported__: -81 raise ImportError( -82 f"Unable to import the `{self.software_names}` module, " -83 "please make sure it is installed; " -84 f"for additional help, please consult one of the following URL(s): {self.urls}" -85 ) -86 -87 self._config = config if config is not None else {} -88 super().__init__(*args, **kwargs) -89 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore + 67 def __init__( +68 self, +69 *args, +70 config: Optional[dict] = None, +71 **kwargs, +72 ): +73 """ +74 Create an instance. +75 +76 Parameters +77 ---------- +78 config : dict, optional +79 the CLASS configuration to use. All parameters are accepted. +80 """ +81 if not self.__imported__: +82 raise ImportError( +83 f"Unable to import the `{self.software_names}` module, " +84 "please make sure it is installed; " +85 f"for additional help, please consult one of the following URL(s): {self.urls}" +86 ) +87 +88 self._config = config if config is not None else {} +89 super().__init__(*args, **kwargs) +90 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore @@ -812,27 +1036,27 @@ Parameters
67 def __init__( +68 self, +69 *args, +70 config: Optional[dict] = None, +71 **kwargs, +72 ): +73 """ +74 Create an instance. +75 +76 Parameters +77 ---------- +78 config : dict, optional +79 the CLASS configuration to use. All parameters are accepted. +80 """ +81 if not self.__imported__: +82 raise ImportError( +83 f"Unable to import the `{self.software_names}` module, " +84 "please make sure it is installed; " +85 f"for additional help, please consult one of the following URL(s): {self.urls}" +86 ) +87 +88 self._config = config if config is not None else {} +89 super().__init__(*args, **kwargs) +90 self._run_classy = lru_cache(maxsize=None)(self._run_classy) # type: ignore
91 @classmethod - 92 def from_file(cls, path: Union[str, Path]): - 93 r""" - 94 Load a CLASS configuration from a file. - 95 - 96 Parameters - 97 ---------- - 98 path : str or Path - 99 the path to the configuration file -100 """ -101 contents = Path(path).read_text(encoding="utf-8").split("\n") -102 config = ConfigParser(inline_comment_prefixes=("#", ";")) -103 config.optionxform = lambda option: option # type: ignore -104 section = "default" -105 config.read_string(f"[{section}]" + "\n".join(contents)) -106 -107 final_config = { -108 key: value for key, value in dict(config[section]).items() if value -109 } -110 -111 return cls(config=final_config) + 92 @classmethod + 93 def from_file(cls, path: Union[str, Path]): + 94 r""" + 95 Load a CLASS configuration from a file. + 96 + 97 Parameters + 98 ---------- + 99 path : str or Path +100 the path to the configuration file +101 """ +102 contents = Path(path).read_text(encoding="utf-8").split("\n") +103 config = ConfigParser(inline_comment_prefixes=("#", ";")) +104 config.optionxform = lambda option: option # type: ignore +105 section = "default" +106 config.read_string(f"[{section}]" + "\n".join(contents)) +107 +108 final_config = { +109 key: value for key, value in dict(config[section]).items() if value +110 } +111 +112 return cls(config=final_config) @@ -889,192 +1113,192 @@ Inherited Members
92 @classmethod + 93 def from_file(cls, path: Union[str, Path]): + 94 r""" + 95 Load a CLASS configuration from a file. + 96 + 97 Parameters + 98 ---------- + 99 path : str or Path +100 the path to the configuration file +101 """ +102 contents = Path(path).read_text(encoding="utf-8").split("\n") +103 config = ConfigParser(inline_comment_prefixes=("#", ";")) +104 config.optionxform = lambda option: option # type: ignore +105 section = "default" +106 config.read_string(f"[{section}]" + "\n".join(contents)) +107 +108 final_config = { +109 key: value for key, value in dict(config[section]).items() if value +110 } +111 +112 return cls(config=final_config)
166class ClassyCMBDerivative(ClassyBaseDerivative): -167 """ -168 Interface for CMB quantities. -169 -170 Interface for computing derivatives using the CMB signal and covariance -171 (temperature, polarization, or both). -172 -173 .. versionadded:: 0.9.2 -174 """ -175 -176 def __init__( -177 self, -178 *args, -179 config: Optional[dict] = None, -180 **kwargs, -181 ): -182 """ -183 Create an instance. -184 -185 Parameters -186 ---------- -187 config : dict, optional -188 the CLASS configuration to use. All parameters are accepted. -189 If not specified, defaults to ``{'output' : 'tCl'}``. -190 If the key ``output`` is missing, it is inserted with a default value -191 ``tCl``. -192 """ -193 super().__init__(*args, config=config, **kwargs) -194 self._config = config if config is not None else {"output": "tCl"} -195 if not self._config.get("output"): -196 self._config["output"] = "tCl" -197 -198 def signal( -199 self, -200 *args: tuple[str, float], -201 **kwargs, -202 ): -203 r""" -204 Compute the CMB :math:`C_\ell` s. -205 -206 Parameters -207 ---------- -208 *args -209 the name(s) and value(s) of the parameter(s) for which we want to -210 compute the derivative -211 -212 Returns -213 ------- -214 result : array_like of float -215 the signal as a numpy array -216 -217 Notes -218 ----- -219 The signal is composed of the following contributions, in this order: -220 -221 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` -222 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` -223 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` -224 and ``pCl`` -225 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* -226 -227 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose -228 :math:`\ell_\mathrm{min} = 2`. -229 -230 The output is ordered as follows: + 168class ClassyCMBDerivative(ClassyBaseDerivative): +169 """ +170 Interface for CMB quantities. +171 +172 Interface for computing derivatives using the CMB signal and covariance +173 (temperature, polarization, or both). +174 +175 .. versionadded:: 0.9.2 +176 """ +177 +178 def __init__( +179 self, +180 *args, +181 config: Optional[dict] = None, +182 **kwargs, +183 ): +184 """ +185 Create an instance. +186 +187 Parameters +188 ---------- +189 config : dict, optional +190 the CLASS configuration to use. All parameters are accepted. +191 If not specified, defaults to ``{'output' : 'tCl'}``. +192 If the key ``output`` is missing, it is inserted with a default value +193 ``tCl``. +194 """ +195 super().__init__(*args, config=config, **kwargs) +196 self._config = config if config is not None else {"output": "tCl"} +197 if not self._config.get("output"): +198 self._config["output"] = "tCl" +199 +200 def signal( +201 self, +202 *args: tuple[str, float], +203 **kwargs, +204 ): +205 r""" +206 Compute the CMB :math:`C_\ell` s. +207 +208 Parameters +209 ---------- +210 *args +211 the name(s) and value(s) of the parameter(s) for which we want to +212 compute the derivative +213 +214 Returns +215 ------- +216 result : array_like of float +217 the signal as a numpy array +218 +219 Notes +220 ----- +221 The signal is composed of the following contributions, in this order: +222 +223 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` +224 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` +225 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` +226 and ``pCl`` +227 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* +228 +229 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose +230 :math:`\ell_\mathrm{min} = 2`. 231 -232 .. math:: -233 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} -234 """ -235 cosmo = self._run_classy(*args) -236 -237 outputs = self._parse_outputs() +232 The output is ordered as follows: +233 +234 .. math:: +235 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} +236 """ +237 cosmo = self._run_classy(*args) 238 -239 result = [] -240 if outputs["temperature"]: -241 result.extend(cosmo.raw_cl()["tt"][2:]) -242 if outputs["polarization"]: -243 result.extend(cosmo.raw_cl()["ee"][2:]) -244 if outputs["temperature"] and outputs["polarization"]: -245 result.extend(cosmo.raw_cl()["te"][2:]) -246 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): -247 result.extend(cosmo.raw_cl()["bb"][2:]) -248 -249 if result: -250 return np.array(result) -251 -252 return NotImplemented +239 outputs = self._parse_outputs() +240 +241 result = [] +242 if outputs["temperature"]: +243 result.extend(cosmo.raw_cl()["tt"][2:]) +244 if outputs["polarization"]: +245 result.extend(cosmo.raw_cl()["ee"][2:]) +246 if outputs["temperature"] and outputs["polarization"]: +247 result.extend(cosmo.raw_cl()["te"][2:]) +248 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): +249 result.extend(cosmo.raw_cl()["bb"][2:]) +250 +251 if result: +252 return np.array(result) 253 -254 def _prefactor_covariance(self, size: int): -255 l_max = int(self.config.get("l_max_scalars", 2500)) -256 matrix = np.diag([2 / (2 * ell + 1) for ell in range(2, l_max + 1)]) -257 return np.tile(matrix, (size, size)) -258 -259 def covariance( -260 self, -261 *args: tuple[str, float], -262 **kwargs, -263 ): -264 r""" -265 Compute the covariance of CMB :math:`C_\ell` s. -266 -267 Parameters -268 ---------- -269 *args -270 the name(s) and fiducial value(s) of the parameter(s) for which we want to -271 compute the covariance -272 -273 **kwargs -274 keyword arguments for the covariance. Supported values are: -275 -276 - ``fsky``: the sky fraction of the survey (default: 1) +254 return NotImplemented +255 +256 def _prefactor_covariance(self, size: int): +257 l_max = int(self.config.get("l_max_scalars", 2500)) +258 matrix = np.diag([2 / (2 * ell + 1) for ell in range(2, l_max + 1)]) +259 return np.tile(matrix, (size, size)) +260 +261 def covariance( +262 self, +263 *args: tuple[str, float], +264 **kwargs, +265 ): +266 r""" +267 Compute the covariance of CMB :math:`C_\ell` s. +268 +269 Parameters +270 ---------- +271 *args +272 the name(s) and fiducial value(s) of the parameter(s) for which we want to +273 compute the covariance +274 +275 **kwargs +276 keyword arguments for the covariance. Supported values are: 277 -278 Returns -279 ------- -280 result : array_like of float -281 the signal as a numpy array -282 -283 Notes -284 ----- -285 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): -286 -287 .. math:: -288 \frac{2}{2 \ell + 1} -289 \begin{pmatrix} -290 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ -291 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ -292 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ -293 0 & 0 & 0 & (X^{BB})^2 -294 \end{pmatrix} -295 -296 See the notes of the ``signal`` method about the order of the outputs in -297 the matrix. -298 -299 The covariance has been taken from <a -300 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer -301 noopener">arXiv:0911.3105</a>, eq. (27). -302 """ -303 cosmo = self._run_classy(*args) -304 -305 outputs = self._parse_outputs() +278 - ``fsky``: the sky fraction of the survey (default: 1) +279 +280 Returns +281 ------- +282 result : array_like of float +283 the signal as a numpy array +284 +285 Notes +286 ----- +287 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): +288 +289 .. math:: +290 \frac{2}{2 \ell + 1} +291 \begin{pmatrix} +292 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ +293 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ +294 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ +295 0 & 0 & 0 & (X^{BB})^2 +296 \end{pmatrix} +297 +298 See the notes of the ``signal`` method about the order of the outputs in +299 the matrix. +300 +301 The covariance has been taken from <a +302 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer +303 noopener">arXiv:0911.3105</a>, eq. (27). +304 """ +305 cosmo = self._run_classy(*args) 306 -307 c_ell = cosmo.raw_cl() -308 c_tt = c_ell.get("tt", [])[2:] -309 c_ee = c_ell.get("ee", [])[2:] -310 c_te = c_ell.get("te", [])[2:] -311 c_bb = c_ell.get("bb", [])[2:] -312 -313 # CMB C_ells -314 if outputs["temperature"] and outputs["polarization"]: -315 result = np.block( -316 [ -317 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], -318 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], -319 [ -320 np.diag(c_tt * c_te), -321 np.diag(c_ee * c_te), -322 np.diag(c_te**2 + c_tt * c_ee) / 2, -323 ], -324 ] -325 ) -326 -327 if np.any(c_bb): -328 result = self._prefactor_covariance(4) * block_diag( -329 result, -330 np.diag(c_bb**2), -331 ) -332 else: -333 result = self._prefactor_covariance(3) * result -334 -335 elif outputs["temperature"]: -336 result = self._prefactor_covariance(1) * np.diag(c_tt**2) -337 -338 elif outputs["polarization"]: -339 if np.any(c_bb): -340 result = self._prefactor_covariance(2) * block_diag( -341 np.diag(c_ee**2), np.diag(c_bb**2) -342 ) -343 else: -344 result = self._prefactor_covariance(1) * np.diag(c_ee**2) -345 -346 else: -347 return NotImplemented -348 -349 final_kwargs = self._parse_covariance_kwargs(**kwargs) +307 outputs = self._parse_outputs() +308 +309 c_ell = cosmo.raw_cl() +310 c_tt = c_ell.get("tt", [])[2:] +311 c_ee = c_ell.get("ee", [])[2:] +312 c_te = c_ell.get("te", [])[2:] +313 c_bb = c_ell.get("bb", [])[2:] +314 +315 # CMB C_ells +316 if outputs["temperature"] and outputs["polarization"]: +317 result = np.block( +318 [ +319 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], +320 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], +321 [ +322 np.diag(c_tt * c_te), +323 np.diag(c_ee * c_te), +324 np.diag(c_te**2 + c_tt * c_ee) / 2, +325 ], +326 ] +327 ) +328 +329 if np.any(c_bb): +330 result = self._prefactor_covariance(4) * block_diag( +331 result, +332 np.diag(c_bb**2), +333 ) +334 else: +335 result = self._prefactor_covariance(3) * result +336 +337 elif outputs["temperature"]: +338 result = self._prefactor_covariance(1) * np.diag(c_tt**2) +339 +340 elif outputs["polarization"]: +341 if np.any(c_bb): +342 result = self._prefactor_covariance(2) * block_diag( +343 np.diag(c_ee**2), np.diag(c_bb**2) +344 ) +345 else: +346 result = self._prefactor_covariance(1) * np.diag(c_ee**2) +347 +348 else: +349 return NotImplemented 350 -351 return result / final_kwargs["fsky"] +351 final_kwargs = self._parse_covariance_kwargs(**kwargs) +352 +353 return result / final_kwargs["fsky"] @@ -1097,27 +1321,27 @@ Inherited Members
168class ClassyCMBDerivative(ClassyBaseDerivative): +169 """ +170 Interface for CMB quantities. +171 +172 Interface for computing derivatives using the CMB signal and covariance +173 (temperature, polarization, or both). +174 +175 .. versionadded:: 0.9.2 +176 """ +177 +178 def __init__( +179 self, +180 *args, +181 config: Optional[dict] = None, +182 **kwargs, +183 ): +184 """ +185 Create an instance. +186 +187 Parameters +188 ---------- +189 config : dict, optional +190 the CLASS configuration to use. All parameters are accepted. +191 If not specified, defaults to ``{'output' : 'tCl'}``. +192 If the key ``output`` is missing, it is inserted with a default value +193 ``tCl``. +194 """ +195 super().__init__(*args, config=config, **kwargs) +196 self._config = config if config is not None else {"output": "tCl"} +197 if not self._config.get("output"): +198 self._config["output"] = "tCl" +199 +200 def signal( +201 self, +202 *args: tuple[str, float], +203 **kwargs, +204 ): +205 r""" +206 Compute the CMB :math:`C_\ell` s. +207 +208 Parameters +209 ---------- +210 *args +211 the name(s) and value(s) of the parameter(s) for which we want to +212 compute the derivative +213 +214 Returns +215 ------- +216 result : array_like of float +217 the signal as a numpy array +218 +219 Notes +220 ----- +221 The signal is composed of the following contributions, in this order: +222 +223 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` +224 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` +225 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` +226 and ``pCl`` +227 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* +228 +229 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose +230 :math:`\ell_\mathrm{min} = 2`. 231 -232 .. math:: -233 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} -234 """ -235 cosmo = self._run_classy(*args) -236 -237 outputs = self._parse_outputs() +232 The output is ordered as follows: +233 +234 .. math:: +235 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} +236 """ +237 cosmo = self._run_classy(*args) 238 -239 result = [] -240 if outputs["temperature"]: -241 result.extend(cosmo.raw_cl()["tt"][2:]) -242 if outputs["polarization"]: -243 result.extend(cosmo.raw_cl()["ee"][2:]) -244 if outputs["temperature"] and outputs["polarization"]: -245 result.extend(cosmo.raw_cl()["te"][2:]) -246 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): -247 result.extend(cosmo.raw_cl()["bb"][2:]) -248 -249 if result: -250 return np.array(result) -251 -252 return NotImplemented +239 outputs = self._parse_outputs() +240 +241 result = [] +242 if outputs["temperature"]: +243 result.extend(cosmo.raw_cl()["tt"][2:]) +244 if outputs["polarization"]: +245 result.extend(cosmo.raw_cl()["ee"][2:]) +246 if outputs["temperature"] and outputs["polarization"]: +247 result.extend(cosmo.raw_cl()["te"][2:]) +248 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): +249 result.extend(cosmo.raw_cl()["bb"][2:]) +250 +251 if result: +252 return np.array(result) 253 -254 def _prefactor_covariance(self, size: int): -255 l_max = int(self.config.get("l_max_scalars", 2500)) -256 matrix = np.diag([2 / (2 * ell + 1) for ell in range(2, l_max + 1)]) -257 return np.tile(matrix, (size, size)) -258 -259 def covariance( -260 self, -261 *args: tuple[str, float], -262 **kwargs, -263 ): -264 r""" -265 Compute the covariance of CMB :math:`C_\ell` s. -266 -267 Parameters -268 ---------- -269 *args -270 the name(s) and fiducial value(s) of the parameter(s) for which we want to -271 compute the covariance -272 -273 **kwargs -274 keyword arguments for the covariance. Supported values are: -275 -276 - ``fsky``: the sky fraction of the survey (default: 1) +254 return NotImplemented +255 +256 def _prefactor_covariance(self, size: int): +257 l_max = int(self.config.get("l_max_scalars", 2500)) +258 matrix = np.diag([2 / (2 * ell + 1) for ell in range(2, l_max + 1)]) +259 return np.tile(matrix, (size, size)) +260 +261 def covariance( +262 self, +263 *args: tuple[str, float], +264 **kwargs, +265 ): +266 r""" +267 Compute the covariance of CMB :math:`C_\ell` s. +268 +269 Parameters +270 ---------- +271 *args +272 the name(s) and fiducial value(s) of the parameter(s) for which we want to +273 compute the covariance +274 +275 **kwargs +276 keyword arguments for the covariance. Supported values are: 277 -278 Returns -279 ------- -280 result : array_like of float -281 the signal as a numpy array -282 -283 Notes -284 ----- -285 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): -286 -287 .. math:: -288 \frac{2}{2 \ell + 1} -289 \begin{pmatrix} -290 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ -291 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ -292 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ -293 0 & 0 & 0 & (X^{BB})^2 -294 \end{pmatrix} -295 -296 See the notes of the ``signal`` method about the order of the outputs in -297 the matrix. -298 -299 The covariance has been taken from <a -300 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer -301 noopener">arXiv:0911.3105</a>, eq. (27). -302 """ -303 cosmo = self._run_classy(*args) -304 -305 outputs = self._parse_outputs() +278 - ``fsky``: the sky fraction of the survey (default: 1) +279 +280 Returns +281 ------- +282 result : array_like of float +283 the signal as a numpy array +284 +285 Notes +286 ----- +287 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): +288 +289 .. math:: +290 \frac{2}{2 \ell + 1} +291 \begin{pmatrix} +292 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ +293 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ +294 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ +295 0 & 0 & 0 & (X^{BB})^2 +296 \end{pmatrix} +297 +298 See the notes of the ``signal`` method about the order of the outputs in +299 the matrix. +300 +301 The covariance has been taken from <a +302 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer +303 noopener">arXiv:0911.3105</a>, eq. (27). +304 """ +305 cosmo = self._run_classy(*args) 306 -307 c_ell = cosmo.raw_cl() -308 c_tt = c_ell.get("tt", [])[2:] -309 c_ee = c_ell.get("ee", [])[2:] -310 c_te = c_ell.get("te", [])[2:] -311 c_bb = c_ell.get("bb", [])[2:] -312 -313 # CMB C_ells -314 if outputs["temperature"] and outputs["polarization"]: -315 result = np.block( -316 [ -317 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], -318 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], -319 [ -320 np.diag(c_tt * c_te), -321 np.diag(c_ee * c_te), -322 np.diag(c_te**2 + c_tt * c_ee) / 2, -323 ], -324 ] -325 ) -326 -327 if np.any(c_bb): -328 result = self._prefactor_covariance(4) * block_diag( -329 result, -330 np.diag(c_bb**2), -331 ) -332 else: -333 result = self._prefactor_covariance(3) * result -334 -335 elif outputs["temperature"]: -336 result = self._prefactor_covariance(1) * np.diag(c_tt**2) -337 -338 elif outputs["polarization"]: -339 if np.any(c_bb): -340 result = self._prefactor_covariance(2) * block_diag( -341 np.diag(c_ee**2), np.diag(c_bb**2) -342 ) -343 else: -344 result = self._prefactor_covariance(1) * np.diag(c_ee**2) -345 -346 else: -347 return NotImplemented -348 -349 final_kwargs = self._parse_covariance_kwargs(**kwargs) +307 outputs = self._parse_outputs() +308 +309 c_ell = cosmo.raw_cl() +310 c_tt = c_ell.get("tt", [])[2:] +311 c_ee = c_ell.get("ee", [])[2:] +312 c_te = c_ell.get("te", [])[2:] +313 c_bb = c_ell.get("bb", [])[2:] +314 +315 # CMB C_ells +316 if outputs["temperature"] and outputs["polarization"]: +317 result = np.block( +318 [ +319 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], +320 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], +321 [ +322 np.diag(c_tt * c_te), +323 np.diag(c_ee * c_te), +324 np.diag(c_te**2 + c_tt * c_ee) / 2, +325 ], +326 ] +327 ) +328 +329 if np.any(c_bb): +330 result = self._prefactor_covariance(4) * block_diag( +331 result, +332 np.diag(c_bb**2), +333 ) +334 else: +335 result = self._prefactor_covariance(3) * result +336 +337 elif outputs["temperature"]: +338 result = self._prefactor_covariance(1) * np.diag(c_tt**2) +339 +340 elif outputs["polarization"]: +341 if np.any(c_bb): +342 result = self._prefactor_covariance(2) * block_diag( +343 np.diag(c_ee**2), np.diag(c_bb**2) +344 ) +345 else: +346 result = self._prefactor_covariance(1) * np.diag(c_ee**2) +347 +348 else: +349 return NotImplemented 350 -351 return result / final_kwargs["fsky"] +351 final_kwargs = self._parse_covariance_kwargs(**kwargs) +352 +353 return result / final_kwargs["fsky"]
176 def __init__( -177 self, -178 *args, -179 config: Optional[dict] = None, -180 **kwargs, -181 ): -182 """ -183 Create an instance. -184 -185 Parameters -186 ---------- -187 config : dict, optional -188 the CLASS configuration to use. All parameters are accepted. -189 If not specified, defaults to ``{'output' : 'tCl'}``. -190 If the key ``output`` is missing, it is inserted with a default value -191 ``tCl``. -192 """ -193 super().__init__(*args, config=config, **kwargs) -194 self._config = config if config is not None else {"output": "tCl"} -195 if not self._config.get("output"): -196 self._config["output"] = "tCl" + 178 def __init__( +179 self, +180 *args, +181 config: Optional[dict] = None, +182 **kwargs, +183 ): +184 """ +185 Create an instance. +186 +187 Parameters +188 ---------- +189 config : dict, optional +190 the CLASS configuration to use. All parameters are accepted. +191 If not specified, defaults to ``{'output' : 'tCl'}``. +192 If the key ``output`` is missing, it is inserted with a default value +193 ``tCl``. +194 """ +195 super().__init__(*args, config=config, **kwargs) +196 self._config = config if config is not None else {"output": "tCl"} +197 if not self._config.get("output"): +198 self._config["output"] = "tCl" @@ -1147,61 +1371,61 @@ Parameters
178 def __init__( +179 self, +180 *args, +181 config: Optional[dict] = None, +182 **kwargs, +183 ): +184 """ +185 Create an instance. +186 +187 Parameters +188 ---------- +189 config : dict, optional +190 the CLASS configuration to use. All parameters are accepted. +191 If not specified, defaults to ``{'output' : 'tCl'}``. +192 If the key ``output`` is missing, it is inserted with a default value +193 ``tCl``. +194 """ +195 super().__init__(*args, config=config, **kwargs) +196 self._config = config if config is not None else {"output": "tCl"} +197 if not self._config.get("output"): +198 self._config["output"] = "tCl"
198 def signal( -199 self, -200 *args: tuple[str, float], -201 **kwargs, -202 ): -203 r""" -204 Compute the CMB :math:`C_\ell` s. -205 -206 Parameters -207 ---------- -208 *args -209 the name(s) and value(s) of the parameter(s) for which we want to -210 compute the derivative -211 -212 Returns -213 ------- -214 result : array_like of float -215 the signal as a numpy array -216 -217 Notes -218 ----- -219 The signal is composed of the following contributions, in this order: -220 -221 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` -222 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` -223 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` -224 and ``pCl`` -225 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* -226 -227 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose -228 :math:`\ell_\mathrm{min} = 2`. -229 -230 The output is ordered as follows: + 200 def signal( +201 self, +202 *args: tuple[str, float], +203 **kwargs, +204 ): +205 r""" +206 Compute the CMB :math:`C_\ell` s. +207 +208 Parameters +209 ---------- +210 *args +211 the name(s) and value(s) of the parameter(s) for which we want to +212 compute the derivative +213 +214 Returns +215 ------- +216 result : array_like of float +217 the signal as a numpy array +218 +219 Notes +220 ----- +221 The signal is composed of the following contributions, in this order: +222 +223 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` +224 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` +225 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` +226 and ``pCl`` +227 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* +228 +229 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose +230 :math:`\ell_\mathrm{min} = 2`. 231 -232 .. math:: -233 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} -234 """ -235 cosmo = self._run_classy(*args) -236 -237 outputs = self._parse_outputs() +232 The output is ordered as follows: +233 +234 .. math:: +235 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} +236 """ +237 cosmo = self._run_classy(*args) 238 -239 result = [] -240 if outputs["temperature"]: -241 result.extend(cosmo.raw_cl()["tt"][2:]) -242 if outputs["polarization"]: -243 result.extend(cosmo.raw_cl()["ee"][2:]) -244 if outputs["temperature"] and outputs["polarization"]: -245 result.extend(cosmo.raw_cl()["te"][2:]) -246 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): -247 result.extend(cosmo.raw_cl()["bb"][2:]) -248 -249 if result: -250 return np.array(result) -251 -252 return NotImplemented +239 outputs = self._parse_outputs() +240 +241 result = [] +242 if outputs["temperature"]: +243 result.extend(cosmo.raw_cl()["tt"][2:]) +244 if outputs["polarization"]: +245 result.extend(cosmo.raw_cl()["ee"][2:]) +246 if outputs["temperature"] and outputs["polarization"]: +247 result.extend(cosmo.raw_cl()["te"][2:]) +248 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): +249 result.extend(cosmo.raw_cl()["bb"][2:]) +250 +251 if result: +252 return np.array(result) +253 +254 return NotImplemented @@ -1254,99 +1478,99 @@ Notes
200 def signal( +201 self, +202 *args: tuple[str, float], +203 **kwargs, +204 ): +205 r""" +206 Compute the CMB :math:`C_\ell` s. +207 +208 Parameters +209 ---------- +210 *args +211 the name(s) and value(s) of the parameter(s) for which we want to +212 compute the derivative +213 +214 Returns +215 ------- +216 result : array_like of float +217 the signal as a numpy array +218 +219 Notes +220 ----- +221 The signal is composed of the following contributions, in this order: +222 +223 - temperature :math:`C_\ell` s if ``output`` contains ``tCl`` +224 - E-mode :math:`C_\ell` s if ``output`` contains ``pCl`` +225 - cross-correlations between T and E-modes if ``output`` contains ``tCl`` +226 and ``pCl`` +227 - B-mode :math:`C_\ell` s if ``output`` contains ``pCl`` *and they are non-zero* +228 +229 Note that :math:`\ell = \\{0, 1\\}` are not part of the output, i.e. we impose +230 :math:`\ell_\mathrm{min} = 2`. 231 -232 .. math:: -233 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} -234 """ -235 cosmo = self._run_classy(*args) -236 -237 outputs = self._parse_outputs() +232 The output is ordered as follows: +233 +234 .. math:: +235 \mathbf{S} = \\{C_\ell^{TT}, C_\ell^{EE}, C_\ell^{TE}, C_\ell^{BB} \\} +236 """ +237 cosmo = self._run_classy(*args) 238 -239 result = [] -240 if outputs["temperature"]: -241 result.extend(cosmo.raw_cl()["tt"][2:]) -242 if outputs["polarization"]: -243 result.extend(cosmo.raw_cl()["ee"][2:]) -244 if outputs["temperature"] and outputs["polarization"]: -245 result.extend(cosmo.raw_cl()["te"][2:]) -246 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): -247 result.extend(cosmo.raw_cl()["bb"][2:]) -248 -249 if result: -250 return np.array(result) -251 -252 return NotImplemented +239 outputs = self._parse_outputs() +240 +241 result = [] +242 if outputs["temperature"]: +243 result.extend(cosmo.raw_cl()["tt"][2:]) +244 if outputs["polarization"]: +245 result.extend(cosmo.raw_cl()["ee"][2:]) +246 if outputs["temperature"] and outputs["polarization"]: +247 result.extend(cosmo.raw_cl()["te"][2:]) +248 if outputs["polarization"] and np.any(cosmo.raw_cl()["bb"]): +249 result.extend(cosmo.raw_cl()["bb"][2:]) +250 +251 if result: +252 return np.array(result) +253 +254 return NotImplemented
259 def covariance( -260 self, -261 *args: tuple[str, float], -262 **kwargs, -263 ): -264 r""" -265 Compute the covariance of CMB :math:`C_\ell` s. -266 -267 Parameters -268 ---------- -269 *args -270 the name(s) and fiducial value(s) of the parameter(s) for which we want to -271 compute the covariance -272 -273 **kwargs -274 keyword arguments for the covariance. Supported values are: -275 -276 - ``fsky``: the sky fraction of the survey (default: 1) + 261 def covariance( +262 self, +263 *args: tuple[str, float], +264 **kwargs, +265 ): +266 r""" +267 Compute the covariance of CMB :math:`C_\ell` s. +268 +269 Parameters +270 ---------- +271 *args +272 the name(s) and fiducial value(s) of the parameter(s) for which we want to +273 compute the covariance +274 +275 **kwargs +276 keyword arguments for the covariance. Supported values are: 277 -278 Returns -279 ------- -280 result : array_like of float -281 the signal as a numpy array -282 -283 Notes -284 ----- -285 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): -286 -287 .. math:: -288 \frac{2}{2 \ell + 1} -289 \begin{pmatrix} -290 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ -291 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ -292 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ -293 0 & 0 & 0 & (X^{BB})^2 -294 \end{pmatrix} -295 -296 See the notes of the ``signal`` method about the order of the outputs in -297 the matrix. -298 -299 The covariance has been taken from <a -300 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer -301 noopener">arXiv:0911.3105</a>, eq. (27). -302 """ -303 cosmo = self._run_classy(*args) -304 -305 outputs = self._parse_outputs() +278 - ``fsky``: the sky fraction of the survey (default: 1) +279 +280 Returns +281 ------- +282 result : array_like of float +283 the signal as a numpy array +284 +285 Notes +286 ----- +287 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): +288 +289 .. math:: +290 \frac{2}{2 \ell + 1} +291 \begin{pmatrix} +292 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ +293 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ +294 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ +295 0 & 0 & 0 & (X^{BB})^2 +296 \end{pmatrix} +297 +298 See the notes of the ``signal`` method about the order of the outputs in +299 the matrix. +300 +301 The covariance has been taken from <a +302 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer +303 noopener">arXiv:0911.3105</a>, eq. (27). +304 """ +305 cosmo = self._run_classy(*args) 306 -307 c_ell = cosmo.raw_cl() -308 c_tt = c_ell.get("tt", [])[2:] -309 c_ee = c_ell.get("ee", [])[2:] -310 c_te = c_ell.get("te", [])[2:] -311 c_bb = c_ell.get("bb", [])[2:] -312 -313 # CMB C_ells -314 if outputs["temperature"] and outputs["polarization"]: -315 result = np.block( -316 [ -317 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], -318 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], -319 [ -320 np.diag(c_tt * c_te), -321 np.diag(c_ee * c_te), -322 np.diag(c_te**2 + c_tt * c_ee) / 2, -323 ], -324 ] -325 ) -326 -327 if np.any(c_bb): -328 result = self._prefactor_covariance(4) * block_diag( -329 result, -330 np.diag(c_bb**2), -331 ) -332 else: -333 result = self._prefactor_covariance(3) * result -334 -335 elif outputs["temperature"]: -336 result = self._prefactor_covariance(1) * np.diag(c_tt**2) -337 -338 elif outputs["polarization"]: -339 if np.any(c_bb): -340 result = self._prefactor_covariance(2) * block_diag( -341 np.diag(c_ee**2), np.diag(c_bb**2) -342 ) -343 else: -344 result = self._prefactor_covariance(1) * np.diag(c_ee**2) -345 -346 else: -347 return NotImplemented -348 -349 final_kwargs = self._parse_covariance_kwargs(**kwargs) +307 outputs = self._parse_outputs() +308 +309 c_ell = cosmo.raw_cl() +310 c_tt = c_ell.get("tt", [])[2:] +311 c_ee = c_ell.get("ee", [])[2:] +312 c_te = c_ell.get("te", [])[2:] +313 c_bb = c_ell.get("bb", [])[2:] +314 +315 # CMB C_ells +316 if outputs["temperature"] and outputs["polarization"]: +317 result = np.block( +318 [ +319 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], +320 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], +321 [ +322 np.diag(c_tt * c_te), +323 np.diag(c_ee * c_te), +324 np.diag(c_te**2 + c_tt * c_ee) / 2, +325 ], +326 ] +327 ) +328 +329 if np.any(c_bb): +330 result = self._prefactor_covariance(4) * block_diag( +331 result, +332 np.diag(c_bb**2), +333 ) +334 else: +335 result = self._prefactor_covariance(3) * result +336 +337 elif outputs["temperature"]: +338 result = self._prefactor_covariance(1) * np.diag(c_tt**2) +339 +340 elif outputs["polarization"]: +341 if np.any(c_bb): +342 result = self._prefactor_covariance(2) * block_diag( +343 np.diag(c_ee**2), np.diag(c_bb**2) +344 ) +345 else: +346 result = self._prefactor_covariance(1) * np.diag(c_ee**2) +347 +348 else: +349 return NotImplemented 350 -351 return result / final_kwargs["fsky"] +351 final_kwargs = self._parse_covariance_kwargs(**kwargs) +352 +353 return result / final_kwargs["fsky"] @@ -1414,6 +1638,496 @@ Inherited Members
261 def covariance( +262 self, +263 *args: tuple[str, float], +264 **kwargs, +265 ): +266 r""" +267 Compute the covariance of CMB :math:`C_\ell` s. +268 +269 Parameters +270 ---------- +271 *args +272 the name(s) and fiducial value(s) of the parameter(s) for which we want to +273 compute the covariance +274 +275 **kwargs +276 keyword arguments for the covariance. Supported values are: 277 -278 Returns -279 ------- -280 result : array_like of float -281 the signal as a numpy array -282 -283 Notes -284 ----- -285 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): -286 -287 .. math:: -288 \frac{2}{2 \ell + 1} -289 \begin{pmatrix} -290 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ -291 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ -292 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ -293 0 & 0 & 0 & (X^{BB})^2 -294 \end{pmatrix} -295 -296 See the notes of the ``signal`` method about the order of the outputs in -297 the matrix. -298 -299 The covariance has been taken from <a -300 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer -301 noopener">arXiv:0911.3105</a>, eq. (27). -302 """ -303 cosmo = self._run_classy(*args) -304 -305 outputs = self._parse_outputs() +278 - ``fsky``: the sky fraction of the survey (default: 1) +279 +280 Returns +281 ------- +282 result : array_like of float +283 the signal as a numpy array +284 +285 Notes +286 ----- +287 The covariance is the following block-matrix (with the notation :math:`X = C_\ell`): +288 +289 .. math:: +290 \frac{2}{2 \ell + 1} +291 \begin{pmatrix} +292 (X^{TT})^2 & (X^{TE})^2 & X^{TT} X^{TE} & 0 \\\\ +293 (X^{TE})^2 & (X^{EE})^2 & X^{EE} X^{TE} & 0 \\\\ +294 X^{TT} X^{TE} & X^{EE} X^{TE} & [(X^{TE})^2 + X^{TT} X^{EE}] / 2 & 0 \\\\ +295 0 & 0 & 0 & (X^{BB})^2 +296 \end{pmatrix} +297 +298 See the notes of the ``signal`` method about the order of the outputs in +299 the matrix. +300 +301 The covariance has been taken from <a +302 href="https://arxiv.org/abs/0911.3105" target="_blank" rel="noreferrer +303 noopener">arXiv:0911.3105</a>, eq. (27). +304 """ +305 cosmo = self._run_classy(*args) 306 -307 c_ell = cosmo.raw_cl() -308 c_tt = c_ell.get("tt", [])[2:] -309 c_ee = c_ell.get("ee", [])[2:] -310 c_te = c_ell.get("te", [])[2:] -311 c_bb = c_ell.get("bb", [])[2:] -312 -313 # CMB C_ells -314 if outputs["temperature"] and outputs["polarization"]: -315 result = np.block( -316 [ -317 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], -318 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], -319 [ -320 np.diag(c_tt * c_te), -321 np.diag(c_ee * c_te), -322 np.diag(c_te**2 + c_tt * c_ee) / 2, -323 ], -324 ] -325 ) -326 -327 if np.any(c_bb): -328 result = self._prefactor_covariance(4) * block_diag( -329 result, -330 np.diag(c_bb**2), -331 ) -332 else: -333 result = self._prefactor_covariance(3) * result -334 -335 elif outputs["temperature"]: -336 result = self._prefactor_covariance(1) * np.diag(c_tt**2) -337 -338 elif outputs["polarization"]: -339 if np.any(c_bb): -340 result = self._prefactor_covariance(2) * block_diag( -341 np.diag(c_ee**2), np.diag(c_bb**2) -342 ) -343 else: -344 result = self._prefactor_covariance(1) * np.diag(c_ee**2) -345 -346 else: -347 return NotImplemented -348 -349 final_kwargs = self._parse_covariance_kwargs(**kwargs) +307 outputs = self._parse_outputs() +308 +309 c_ell = cosmo.raw_cl() +310 c_tt = c_ell.get("tt", [])[2:] +311 c_ee = c_ell.get("ee", [])[2:] +312 c_te = c_ell.get("te", [])[2:] +313 c_bb = c_ell.get("bb", [])[2:] +314 +315 # CMB C_ells +316 if outputs["temperature"] and outputs["polarization"]: +317 result = np.block( +318 [ +319 [np.diag(c_tt**2), np.diag(c_te**2), np.diag(c_tt * c_te)], +320 [np.diag(c_te**2), np.diag(c_ee**2), np.diag(c_ee * c_te)], +321 [ +322 np.diag(c_tt * c_te), +323 np.diag(c_ee * c_te), +324 np.diag(c_te**2 + c_tt * c_ee) / 2, +325 ], +326 ] +327 ) +328 +329 if np.any(c_bb): +330 result = self._prefactor_covariance(4) * block_diag( +331 result, +332 np.diag(c_bb**2), +333 ) +334 else: +335 result = self._prefactor_covariance(3) * result +336 +337 elif outputs["temperature"]: +338 result = self._prefactor_covariance(1) * np.diag(c_tt**2) +339 +340 elif outputs["polarization"]: +341 if np.any(c_bb): +342 result = self._prefactor_covariance(2) * block_diag( +343 np.diag(c_ee**2), np.diag(c_bb**2) +344 ) +345 else: +346 result = self._prefactor_covariance(1) * np.diag(c_ee**2) +347 +348 else: +349 return NotImplemented 350 -351 return result / final_kwargs["fsky"] +351 final_kwargs = self._parse_covariance_kwargs(**kwargs) +352 +353 return result / final_kwargs["fsky"]
356class ClassyGalaxyCountsDerivative(ClassyBaseDerivative): +357 r""" +358 Interface for galaxy number count quantities. +359 +360 Interface for computing derivatives using the galaxy number count signal +361 and covariance. +362 """ +363 +364 def __init__( +365 self, +366 *args, +367 config: Optional[dict] = None, +368 **kwargs, +369 ): +370 """ +371 Create an instance. +372 +373 Parameters +374 ---------- +375 config : dict, optional +376 the CLASS configuration to use. All parameters are accepted. +377 If not specified, defaults to `{'output' : 'nCl'}`. +378 If the key `output` is missing, it is inserted with a default value +379 `nCl`. +380 """ +381 super().__init__(*args, config=config, **kwargs) +382 self._config = config if config is not None else {"output": "nCl"} +383 if not self._config.get("output"): +384 self._config["output"] = "nCl" +385 +386 def _parse_redshifts(self): +387 r""" +388 Parse the redshifts from the configuration and return them. +389 """ +390 raw_redshifts = self.config.get("selection_mean", "") +391 +392 if isinstance(raw_redshifts, (tuple, list, np.ndarray)): +393 redshifts = raw_redshifts if raw_redshifts else [1.0] +394 else: +395 redshifts = ( +396 [item.strip() for item in raw_redshifts.split(",")] +397 if raw_redshifts +398 else [1.0] +399 ) +400 +401 return redshifts +402 +403 def _cross_correlations(self) -> int: +404 r""" +405 Return which cross-correlations the output contains. +406 """ +407 return int(self.config.get("non_diagonal", 0)) +408 +409 def _compute_angular_power_spectrum(self, *args): +410 r""" +411 Compute the $C_\ell$s. +412 +413 Computes the angular power spectrum (the $C_\ell$s) and returns the +414 number of angular power spectra, the number of redshift bins, and the +415 angular power spectra as a dictionary with keys `(ell, index_z1, +416 index_z2)` +417 """ +418 cosmo = self._run_classy(*args) +419 +420 outputs = self._parse_outputs() +421 +422 z_size = len(self._parse_redshifts()) +423 +424 if outputs["galaxy_counts"]: +425 # the output from CLASS +426 c_ells = cosmo.density_cl()["dd"] +427 +428 # how many angular power spectra do we have +429 ell_size = len(c_ells[0]) +430 +431 # the angular power spectra as a dictionary +432 c_ells_dict = {} +433 +434 # if we have cross-correlations, we need to handle them specially +435 if self._cross_correlations() >= 1 and z_size > 1: +436 for ell in range(2, ell_size): +437 counter = 0 +438 for i in range(z_size): +439 for j in range(i, z_size): +440 c_ells_dict[(ell, i, j)] = c_ells_dict[ +441 (ell, j, i) +442 ] = c_ells[counter][ell] +443 counter += 1 +444 else: +445 c_ells_dict = { +446 (ell, i, i): c_ells[i][ell] +447 for i in range(z_size) +448 for ell in range(2, ell_size) +449 } +450 +451 return ell_size, z_size, c_ells_dict +452 +453 return NotImplemented +454 +455 def signal( +456 self, +457 *args: tuple[str, float], +458 **kwargs, +459 ): +460 r""" +461 Compute the signal ($C_\ell$s) of galaxy number counts. +462 +463 Parameters +464 ---------- +465 *args +466 the name(s) and fiducial value(s) of the parameter(s) for which we +467 want to compute the covariance +468 +469 Notes +470 ----- +471 The coordinates used are $(z_1, z_2, \ell)$, in that increasing order. +472 Note that $\ell = \\{0, 1\\}$ are not part of the output, i.e. we +473 impose $\ell_\mathrm{min} = 2$. +474 """ +475 *_, c_ells = self._compute_angular_power_spectrum(*args) +476 +477 if c_ells is NotImplemented: +478 return c_ells +479 +480 return np.array([c_ells[key] for key in sorted(c_ells)]) +481 +482 def covariance( +483 self, +484 *args: tuple[str, float], +485 **kwargs, +486 ): +487 r""" +488 Compute the covariance of the $C_\ell$s of galaxy number counts. +489 +490 Parameters +491 ---------- +492 *args +493 the name(s) and fiducial value(s) of the parameter(s) for which we +494 want to compute the covariance +495 +496 **kwargs +497 keyword arguments for the covariance. Supported values are: +498 - `fsky`: the sky coverage of the survey (default: 1) +499 - `delta_ell`: the bin width in multipole space (default: 2 / `fsky`) +500 +501 Notes +502 ----- +503 The covariance is computed as: +504 +505 $$ +506 \mathsf{C}[(ij), (pq), \ell, \ell'] = \delta_{\ell, \ell'} +507 \frac{ +508 C_\ell(i, p) C_\ell(j, q) + C_\ell(i, q) C_\ell(j, p) +509 } +510 { +511 f_\mathrm{sky} \Delta \ell (2 \ell + 1) +512 } +513 $$ +514 +515 The covariance is block diagonal in $\ell$, that is: +516 $$ +517 \begin{pmatrix} +518 \mathsf{C}[(ij), (pq), \ell = 2] & 0 & \ldots & 0\\\ +519 0 & \mathsf{C}[(ij), (pq), \ell = 3] & \ldots & 0\\\ +520 \vdots & \vdots & \ddots & \vdots\\\ +521 0 & 0 & \ldots & \mathsf{C}[(ij), (pq), \ell = \ell_\mathrm{max}] +522 \end{pmatrix} +523 $$ +524 +525 Note that the covariance may be singular if the cross-correlations +526 between redshift bins are not zero (i.e. if using a non-zero value for +527 the `non_diagonal` parameter). +528 """ +529 ell_size, z_size, c_ells = self._compute_angular_power_spectrum(*args) +530 +531 if c_ells is NotImplemented: +532 return c_ells +533 +534 if self._cross_correlations(): +535 blocks = [] +536 # we skip ell=0 and ell=1 as CLASS sets them to zero anyway +537 for ell in range(2, ell_size): +538 # array containing the covariance for fixed ell +539 covariance_fixed_ell = np.zeros([z_size] * 4) +540 for i1, i2, j1, j2 in product( # pylint: disable=invalid-name +541 range(z_size), repeat=4 +542 ): +543 covariance_fixed_ell[i1, i2, j1, j2] = ( +544 c_ells[(ell, i1, j1)] * c_ells[(ell, i2, j2)] +545 + c_ells[(ell, i1, j2)] * c_ells[(ell, i2, j1)] +546 ) +547 blocks.append( +548 np.reshape(covariance_fixed_ell, (z_size * z_size, z_size * z_size)) +549 / (2 * ell + 1) +550 ) +551 +552 result = block_diag(*blocks) +553 +554 else: +555 result = np.diag(np.array([2 * c_ells[key] ** 2 for key in sorted(c_ells)])) +556 +557 final_kwargs = self._parse_covariance_kwargs(**kwargs) +558 +559 return result / final_kwargs["fsky"] / final_kwargs["delta_ell"] +
Interface for galaxy number count quantities.
Interface for computing derivatives using the galaxy number count signal +and covariance.
364 def __init__( +365 self, +366 *args, +367 config: Optional[dict] = None, +368 **kwargs, +369 ): +370 """ +371 Create an instance. +372 +373 Parameters +374 ---------- +375 config : dict, optional +376 the CLASS configuration to use. All parameters are accepted. +377 If not specified, defaults to `{'output' : 'nCl'}`. +378 If the key `output` is missing, it is inserted with a default value +379 `nCl`. +380 """ +381 super().__init__(*args, config=config, **kwargs) +382 self._config = config if config is not None else {"output": "nCl"} +383 if not self._config.get("output"): +384 self._config["output"] = "nCl" +
Create an instance.
{'output' : 'nCl'}
output
nCl
455 def signal( +456 self, +457 *args: tuple[str, float], +458 **kwargs, +459 ): +460 r""" +461 Compute the signal ($C_\ell$s) of galaxy number counts. +462 +463 Parameters +464 ---------- +465 *args +466 the name(s) and fiducial value(s) of the parameter(s) for which we +467 want to compute the covariance +468 +469 Notes +470 ----- +471 The coordinates used are $(z_1, z_2, \ell)$, in that increasing order. +472 Note that $\ell = \\{0, 1\\}$ are not part of the output, i.e. we +473 impose $\ell_\mathrm{min} = 2$. +474 """ +475 *_, c_ells = self._compute_angular_power_spectrum(*args) +476 +477 if c_ells is NotImplemented: +478 return c_ells +479 +480 return np.array([c_ells[key] for key in sorted(c_ells)]) +
Compute the signal ($C_\ell$s) of galaxy number counts.
The coordinates used are $(z_1, z_2, \ell)$, in that increasing order. +Note that $\ell = \{0, 1\}$ are not part of the output, i.e. we +impose $\ell_\mathrm{min} = 2$.
482 def covariance( +483 self, +484 *args: tuple[str, float], +485 **kwargs, +486 ): +487 r""" +488 Compute the covariance of the $C_\ell$s of galaxy number counts. +489 +490 Parameters +491 ---------- +492 *args +493 the name(s) and fiducial value(s) of the parameter(s) for which we +494 want to compute the covariance +495 +496 **kwargs +497 keyword arguments for the covariance. Supported values are: +498 - `fsky`: the sky coverage of the survey (default: 1) +499 - `delta_ell`: the bin width in multipole space (default: 2 / `fsky`) +500 +501 Notes +502 ----- +503 The covariance is computed as: +504 +505 $$ +506 \mathsf{C}[(ij), (pq), \ell, \ell'] = \delta_{\ell, \ell'} +507 \frac{ +508 C_\ell(i, p) C_\ell(j, q) + C_\ell(i, q) C_\ell(j, p) +509 } +510 { +511 f_\mathrm{sky} \Delta \ell (2 \ell + 1) +512 } +513 $$ +514 +515 The covariance is block diagonal in $\ell$, that is: +516 $$ +517 \begin{pmatrix} +518 \mathsf{C}[(ij), (pq), \ell = 2] & 0 & \ldots & 0\\\ +519 0 & \mathsf{C}[(ij), (pq), \ell = 3] & \ldots & 0\\\ +520 \vdots & \vdots & \ddots & \vdots\\\ +521 0 & 0 & \ldots & \mathsf{C}[(ij), (pq), \ell = \ell_\mathrm{max}] +522 \end{pmatrix} +523 $$ +524 +525 Note that the covariance may be singular if the cross-correlations +526 between redshift bins are not zero (i.e. if using a non-zero value for +527 the `non_diagonal` parameter). +528 """ +529 ell_size, z_size, c_ells = self._compute_angular_power_spectrum(*args) +530 +531 if c_ells is NotImplemented: +532 return c_ells +533 +534 if self._cross_correlations(): +535 blocks = [] +536 # we skip ell=0 and ell=1 as CLASS sets them to zero anyway +537 for ell in range(2, ell_size): +538 # array containing the covariance for fixed ell +539 covariance_fixed_ell = np.zeros([z_size] * 4) +540 for i1, i2, j1, j2 in product( # pylint: disable=invalid-name +541 range(z_size), repeat=4 +542 ): +543 covariance_fixed_ell[i1, i2, j1, j2] = ( +544 c_ells[(ell, i1, j1)] * c_ells[(ell, i2, j2)] +545 + c_ells[(ell, i1, j2)] * c_ells[(ell, i2, j1)] +546 ) +547 blocks.append( +548 np.reshape(covariance_fixed_ell, (z_size * z_size, z_size * z_size)) +549 / (2 * ell + 1) +550 ) +551 +552 result = block_diag(*blocks) +553 +554 else: +555 result = np.diag(np.array([2 * c_ells[key] ** 2 for key in sorted(c_ells)])) +556 +557 final_kwargs = self._parse_covariance_kwargs(**kwargs) +558 +559 return result / final_kwargs["fsky"] / final_kwargs["delta_ell"] +
Compute the covariance of the $C_\ell$s of galaxy number counts.
fsky
delta_ell
The covariance is computed as:
$$ + \mathsf{C}[(ij), (pq), \ell, \ell'] = \delta_{\ell, \ell'} + \frac{ + C_\ell(i, p) C_\ell(j, q) + C_\ell(i, q) C_\ell(j, p) + } + { + f_\mathrm{sky} \Delta \ell (2 \ell + 1) + } +$$
The covariance is block diagonal in $\ell$, that is: +$$ + \begin{pmatrix} + \mathsf{C}[(ij), (pq), \ell = 2] & 0 & \ldots & 0\\ + 0 & \mathsf{C}[(ij), (pq), \ell = 3] & \ldots & 0\\ + \vdots & \vdots & \ddots & \vdots\\ + 0 & 0 & \ldots & \mathsf{C}[(ij), (pq), \ell = \ell_\mathrm{max}] + \end{pmatrix} +$$
Note that the covariance may be singular if the cross-correlations +between redshift bins are not zero (i.e. if using a non-zero value for +the non_diagonal parameter).
non_diagonal