diff --git a/firecrown/app/sacc/_utils.py b/firecrown/app/sacc/_utils.py index 80f1079c0..2f16fe311 100644 --- a/firecrown/app/sacc/_utils.py +++ b/firecrown/app/sacc/_utils.py @@ -18,7 +18,7 @@ ) -def mean_std_tracer(tracer: mdt.InferredGalaxyZDist): +def mean_std_tracer(tracer: mdt.TomographicBin): """Compute the mean and standard deviation of a tracer. :param tracer: The galaxy redshift distribution tracer to analyze. diff --git a/firecrown/generators/_inferred_galaxy_zdist.py b/firecrown/generators/_inferred_galaxy_zdist.py index 56a91b985..d5a7e5606 100644 --- a/firecrown/generators/_inferred_galaxy_zdist.py +++ b/firecrown/generators/_inferred_galaxy_zdist.py @@ -12,7 +12,7 @@ from scipy.special import erf, erfc, gamma # pylint: disable=no-name-in-module from firecrown.metadata_functions import make_measurements, make_measurements_dict -from firecrown.metadata_types import Galaxies, InferredGalaxyZDist, Measurement +from firecrown.metadata_types import Galaxies, TomographicBin, Measurement class BinsType(TypedDict): @@ -360,7 +360,7 @@ def binned_distribution( z: npt.NDArray, name: str, measurements: set[Measurement], - ) -> InferredGalaxyZDist: + ) -> TomographicBin: """Generate the inferred galaxy redshift distribution in bins. :param zpl: The lower bound of the integration @@ -411,7 +411,7 @@ def _P(z, _): / norma ) - return InferredGalaxyZDist( + return TomographicBin( bin_name=name, z=z_knots, dndz=dndz, measurements=measurements ) @@ -463,7 +463,7 @@ def serialize_measurements(cls, value: set[Measurement]) -> list[dict]: """Serialize the Measurement.""" return make_measurements_dict(value) - def generate(self, zdist: ZDistLSSTSRD) -> InferredGalaxyZDist: + def generate(self, zdist: ZDistLSSTSRD) -> TomographicBin: """Generate the inferred galaxy redshift distribution in bins.""" return zdist.binned_distribution( zpl=self.zpl, @@ -489,7 +489,7 @@ class ZDistLSSTSRDBinCollection(BaseModel): autoknots_reltol: float = 1.0e-4 autoknots_abstol: float = 1.0e-15 - def generate(self) -> list[InferredGalaxyZDist]: + def generate(self) -> list[TomographicBin]: """Generate the inferred galaxy redshift distributions in bins.""" zdist = ZDistLSSTSRD( alpha=self.alpha, diff --git a/firecrown/likelihood/_cmb.py b/firecrown/likelihood/_cmb.py index 0bd2fec59..5f14cfb63 100644 --- a/firecrown/likelihood/_cmb.py +++ b/firecrown/likelihood/_cmb.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, ConfigDict, PrivateAttr from firecrown.likelihood._base import Source, Tracer -from firecrown.metadata_types import InferredGalaxyZDist, TypeSource +from firecrown.metadata_types import ProjectedField, CMBLensing, TypeSource from firecrown.modeling_tools import ModelingTools @@ -113,14 +113,15 @@ def model_post_init(self, _, /) -> None: """Initialize the CMBConvergenceFactory.""" self._cache: dict[int, CMBConvergence] = {} - def create(self, inferred_galaxy_zdist: InferredGalaxyZDist) -> CMBConvergence: + def create(self, cmb_tracer: ProjectedField) -> CMBConvergence: """Create a CMBConvergence object with the given inferred galaxy z distribution. - :param inferred_galaxy_zdist: the inferred galaxy redshift distribution + :param cmb_tracer: the inferred galaxy redshift distribution :return: a fully initialized CMBConvergence object """ + assert isinstance(cmb_tracer, CMBLensing) # Use the bin_name as the tracer identifier - sacc_tracer = inferred_galaxy_zdist.bin_name + sacc_tracer = cmb_tracer.bin_name tracer_id = hash(sacc_tracer) if tracer_id in self._cache: diff --git a/firecrown/likelihood/_two_point.py b/firecrown/likelihood/_two_point.py index ee4c056ef..6c2b165fd 100644 --- a/firecrown/likelihood/_two_point.py +++ b/firecrown/likelihood/_two_point.py @@ -37,7 +37,6 @@ CMB_TYPES, GALAXY_LENS_TYPES, GALAXY_SOURCE_TYPES, - InferredGalaxyZDist, Measurement, TracerNames, TwoPointCorrelationSpace, @@ -280,6 +279,11 @@ def _from_metadata_single_base( :return: A TwoPoint statistic. """ + # metadata.XY.x/Y are typed as Tracer (protocol). In this code path we + # expect concrete TomographicBin instances (with redshift arrays). Use + # runtime isinstance checks and raise an informative error if this is + # not the case — this both documents the assumption and narrows the + # type for the type checker. source0 = use_source_factory( metadata.XY.x, metadata.XY.x_measurement, tp_factory ) @@ -863,21 +867,19 @@ def from_metadata( def use_source_factory( - inferred_galaxy_zdist: InferredGalaxyZDist, + tomographic_bin: mdt.ProjectedField, measurement: Measurement, tp_factory: TwoPointFactory, ) -> WeakLensing | NumberCounts | CMBConvergence: """Apply the factory to the inferred galaxy redshift distribution.""" - if measurement not in inferred_galaxy_zdist.measurements: + if measurement not in tomographic_bin.measurements: raise ValueError( f"Measurement {measurement} not found in inferred galaxy redshift " - f"distribution {inferred_galaxy_zdist.bin_name}!" + f"distribution {tomographic_bin.bin_name}!" ) - source_factory = tp_factory.get_factory( - measurement, inferred_galaxy_zdist.type_source - ) - source = source_factory.create(inferred_galaxy_zdist) + source_factory = tp_factory.get_factory(measurement, tomographic_bin.type_source) + source = source_factory.create(tomographic_bin) return source diff --git a/firecrown/likelihood/_weak_lensing.py b/firecrown/likelihood/_weak_lensing.py index 218b03643..a075d012e 100644 --- a/firecrown/likelihood/_weak_lensing.py +++ b/firecrown/likelihood/_weak_lensing.py @@ -26,7 +26,7 @@ SourceGalaxySystematic, Tracer, ) -from firecrown.metadata_types import InferredGalaxyZDist, TypeSource +from firecrown.metadata_types import ProjectedField, TomographicBin, TypeSource from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ParamsMap @@ -462,14 +462,17 @@ def __init__( @classmethod def create_ready( cls, - inferred_zdist: InferredGalaxyZDist, + tomographic_bin: TomographicBin, systematics: None | list[SourceGalaxySystematic[WeakLensingArgs]] = None, ) -> WeakLensing: """Create a WeakLensing object with the given tracer name and scale.""" - obj = cls(sacc_tracer=inferred_zdist.bin_name, systematics=systematics) + obj = cls(sacc_tracer=tomographic_bin.bin_name, systematics=systematics) # pylint: disable=unexpected-keyword-arg obj.tracer_args = WeakLensingArgs( - scale=obj.scale, z=inferred_zdist.z, dndz=inferred_zdist.dndz, ia_bias=None + scale=obj.scale, + z=tomographic_bin.z, + dndz=tomographic_bin.dndz, + ia_bias=None, ) # pylint: enable=unexpected-keyword-arg @@ -580,7 +583,7 @@ class MultiplicativeShearBiasFactory(BaseModel): def create(self, bin_name: str) -> MultiplicativeShearBias: """Create a MultiplicativeShearBias object. - :param inferred_zdist: The inferred galaxy redshift distribution for + :param tomographic_bin: The inferred galaxy redshift distribution for the created MultiplicativeShearBias object. :return: The created MultiplicativeShearBias object. """ @@ -609,7 +612,7 @@ class LinearAlignmentSystematicFactory(BaseModel): def create(self, bin_name: str) -> LinearAlignmentSystematic: """Create a LinearAlignmentSystematic object. - :param inferred_zdist: The inferred galaxy redshift distribution for + :param tomographic_bin: The inferred galaxy redshift distribution for the created LinearAlignmentSystematic object. :return: The created LinearAlignmentSystematic object. """ @@ -637,7 +640,7 @@ class TattAlignmentSystematicFactory(BaseModel): def create(self, bin_name: str) -> TattAlignmentSystematic: """Create a TattAlignmentSystematic object. - :param inferred_zdist: The inferred galaxy redshift distribution for + :param tomographic_bin: The inferred galaxy redshift distribution for the created TattAlignmentSystematic object. :return: The created TattAlignmentSystematic object. """ @@ -687,19 +690,20 @@ def model_post_init(self, _, /) -> None: for wl_systematic_factory in self.global_systematics ] - def create(self, inferred_zdist: InferredGalaxyZDist) -> WeakLensing: + def create(self, tomographic_bin: ProjectedField) -> WeakLensing: """Create a WeakLensing object with the given tracer name and scale.""" - inferred_zdist_id = id(inferred_zdist) + assert isinstance(tomographic_bin, TomographicBin) + inferred_zdist_id = id(tomographic_bin) if inferred_zdist_id in self._cache: return self._cache[inferred_zdist_id] systematics: list[SourceGalaxySystematic[WeakLensingArgs]] = [ - systematic_factory.create(inferred_zdist.bin_name) + systematic_factory.create(tomographic_bin.bin_name) for systematic_factory in self.per_bin_systematics ] systematics.extend(self._global_systematics_instances) - wl = WeakLensing.create_ready(inferred_zdist, systematics) + wl = WeakLensing.create_ready(tomographic_bin, systematics) self._cache[inferred_zdist_id] = wl return wl diff --git a/firecrown/likelihood/number_counts/_factories.py b/firecrown/likelihood/number_counts/_factories.py index 8492b5415..11880e1c3 100644 --- a/firecrown/likelihood/number_counts/_factories.py +++ b/firecrown/likelihood/number_counts/_factories.py @@ -20,7 +20,7 @@ ) from firecrown.likelihood._base import SourceGalaxySystematic from firecrown.likelihood.number_counts._args import NumberCountsArgs -from firecrown.metadata_types import InferredGalaxyZDist, TypeSource +from firecrown.metadata_types import ProjectedField, TomographicBin, TypeSource class LinearBiasSystematicFactory(BaseModel): @@ -155,24 +155,25 @@ def model_post_init(self, _, /) -> None: for nc_systematic_factory in self.global_systematics ] - def create(self, inferred_zdist: InferredGalaxyZDist) -> NumberCounts: + def create(self, tomographic_bin: ProjectedField) -> NumberCounts: """Create a NumberCounts object with the given tracer name and scale. - :param inferred_zdist: the inferred redshift distribution + :param tomographic_bin: the inferred redshift distribution :return: a fully initialized NumberCounts object """ - inferred_zdist_id = id(inferred_zdist) + assert isinstance(tomographic_bin, TomographicBin) + inferred_zdist_id = id(tomographic_bin) if inferred_zdist_id in self._cache: return self._cache[inferred_zdist_id] systematics: list[SourceGalaxySystematic[NumberCountsArgs]] = [ - systematic_factory.create(inferred_zdist.bin_name) + systematic_factory.create(tomographic_bin.bin_name) for systematic_factory in self.per_bin_systematics ] systematics.extend(self._global_systematics_instances) nc = NumberCounts.create_ready( - inferred_zdist, systematics=systematics, has_rsd=self.include_rsd + tomographic_bin, systematics=systematics, has_rsd=self.include_rsd ) self._cache[inferred_zdist_id] = nc diff --git a/firecrown/likelihood/number_counts/_source.py b/firecrown/likelihood/number_counts/_source.py index fbc583c6d..5f403625b 100644 --- a/firecrown/likelihood/number_counts/_source.py +++ b/firecrown/likelihood/number_counts/_source.py @@ -16,7 +16,7 @@ SourceGalaxySystematic, Tracer, ) -from firecrown.metadata_types import InferredGalaxyZDist +from firecrown.metadata_types import TomographicBin from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ( DerivedParameter, @@ -69,7 +69,7 @@ def __init__( @classmethod def create_ready( cls, - inferred_zdist: InferredGalaxyZDist, + tomographic_bin: TomographicBin, has_rsd: bool = False, derived_scale: bool = False, scale: float = 1.0, @@ -80,7 +80,7 @@ def create_ready( This is the recommended way to create a NumberCounts object. It creates a fully initialized object. - :param inferred_zdist: the inferred redshift distribution + :param tomographic_bin: the inferred redshift distribution :param has_rsd: whether to include RSD in the tracer :param derived_scale: whether to include a derived parameter for the scale of the tracer @@ -89,7 +89,7 @@ def create_ready( :return: a fully initialized NumberCounts object """ obj = cls( - sacc_tracer=inferred_zdist.bin_name, + sacc_tracer=tomographic_bin.bin_name, systematics=systematics, has_rsd=has_rsd, derived_scale=derived_scale, @@ -98,8 +98,8 @@ def create_ready( # pylint: disable=unexpected-keyword-arg obj.tracer_args = NumberCountsArgs( scale=obj.scale, - z=inferred_zdist.z, - dndz=inferred_zdist.dndz, + z=tomographic_bin.z, + dndz=tomographic_bin.dndz, bias=None, mag_bias=None, ) diff --git a/firecrown/metadata_functions/_combination_utils.py b/firecrown/metadata_functions/_combination_utils.py index 0c032ecdd..bb6143b0d 100644 --- a/firecrown/metadata_functions/_combination_utils.py +++ b/firecrown/metadata_functions/_combination_utils.py @@ -9,8 +9,6 @@ from itertools import product, chain -import numpy as np - import firecrown.metadata_types as mdt @@ -39,7 +37,7 @@ def _validate_list_of_inferred_galaxy_zdists( def make_all_photoz_bin_combinations( - inferred_galaxy_zdists: list[mdt.InferredGalaxyZDist], + inferred_galaxy_zdists: list[mdt.TomographicBin], ) -> list[mdt.TwoPointXY]: """Create all possible two-point correlation combinations for galaxy bins. @@ -83,7 +81,7 @@ def make_all_photoz_bin_combinations( def make_all_photoz_bin_combinations_with_cmb( - inferred_galaxy_zdists: list[mdt.InferredGalaxyZDist], + inferred_galaxy_zdists: list[mdt.TomographicBin], cmb_tracer_name: str = "cmb_convergence", include_cmb_auto: bool = False, ) -> list[mdt.TwoPointXY]: @@ -113,7 +111,7 @@ def make_all_photoz_bin_combinations_with_cmb( def make_cmb_galaxy_combinations_only( - inferred_galaxy_zdists: list[mdt.InferredGalaxyZDist], + inferred_galaxy_zdists: list[mdt.TomographicBin], cmb_tracer_name: str = "cmb_convergence", include_cmb_auto: bool = False, ) -> list[mdt.TwoPointXY]: @@ -136,19 +134,24 @@ def make_cmb_galaxy_combinations_only( """ _validate_list_of_inferred_galaxy_zdists(inferred_galaxy_zdists) # Create a mock mdt.CMB "bin" - cmb_bin = mdt.InferredGalaxyZDist( + cmb_bin = mdt.CMBLensing( bin_name=cmb_tracer_name, - z=np.array([1100.0]), - dndz=np.array([1.0]), + z_lss=1100.0, measurements={mdt.CMB.CONVERGENCE}, type_source=mdt.TypeSource.DEFAULT, ) cmb_galaxy_combinations = [] + x: mdt.ProjectedField + y: mdt.ProjectedField for galaxy_bin in inferred_galaxy_zdists: - galaxy_bin_type = list(product([galaxy_bin], list(galaxy_bin.measurements))) - cmb_bin_type = list(product([cmb_bin], list(cmb_bin.measurements))) + galaxy_bin_type: list[tuple[mdt.ProjectedField, mdt.Measurement]] = list( + product([galaxy_bin], list(galaxy_bin.measurements)) + ) + cmb_bin_type: list[tuple[mdt.ProjectedField, mdt.Measurement]] = list( + product([cmb_bin], list(cmb_bin.measurements)) + ) for (x, m1), (y, m2) in chain( product(galaxy_bin_type, cmb_bin_type), product(cmb_bin_type, galaxy_bin_type), diff --git a/firecrown/metadata_functions/_extraction.py b/firecrown/metadata_functions/_extraction.py index 74ac99bf9..445a50ef8 100644 --- a/firecrown/metadata_functions/_extraction.py +++ b/firecrown/metadata_functions/_extraction.py @@ -20,7 +20,7 @@ def extract_all_tracers_inferred_galaxy_zdists( sacc_data: sacc.Sacc, allow_mixed_types: bool = False, -) -> list[mdt.InferredGalaxyZDist]: +) -> list[mdt.TomographicBin]: """Extracts the two-point function metadata from a Sacc object. The Sacc object contains a set of tracers (one-dimensional bins) and data @@ -44,7 +44,7 @@ def extract_all_tracers_inferred_galaxy_zdists( ) return [ - mdt.InferredGalaxyZDist( + mdt.TomographicBin( bin_name=tracer.name, z=tracer.z, dndz=tracer.nz, diff --git a/firecrown/metadata_functions/_matching.py b/firecrown/metadata_functions/_matching.py index 647174856..4a079c19a 100644 --- a/firecrown/metadata_functions/_matching.py +++ b/firecrown/metadata_functions/_matching.py @@ -1,5 +1,7 @@ """Utilities for matching tracer names and measurements.""" +from typing import Mapping + import firecrown.metadata_types as mdt from firecrown.metadata_functions._type_defs import ( TwoPointRealIndex, @@ -12,8 +14,8 @@ def _make_two_point_xy_error_message( a: mdt.Measurement, b: mdt.Measurement, tracer_names: mdt.TracerNames, - igz1: mdt.InferredGalaxyZDist, - igz2: mdt.InferredGalaxyZDist, + pf1: mdt.ProjectedField, + pf2: mdt.ProjectedField, ) -> str: """Generate a detailed error message for SACC naming convention violations. @@ -30,8 +32,8 @@ def _make_two_point_xy_error_message( Data type: {data_type} Expected measurements: ({a}, {b}) - Tracer '{tracer_names[0]}' has measurements: {igz1.measurements} - Tracer '{tracer_names[1]}' has measurements: {igz2.measurements} + Tracer '{tracer_names[0]}' has measurements: {pf1.measurements} + Tracer '{tracer_names[1]}' has measurements: {pf2.measurements} According to the SACC convention, the order of measurement types in the data type string must match the order of tracers. The measurement type '{a}' should be associated @@ -44,7 +46,7 @@ def _make_two_point_xy_error_message( def make_two_point_xy( - inferred_galaxy_zdists_dict: dict[str, mdt.InferredGalaxyZDist], + projected_fields_dict: Mapping[str, mdt.ProjectedField], tracer_names: mdt.TracerNames, data_type: str, ) -> mdt.TwoPointXY: @@ -53,8 +55,7 @@ def make_two_point_xy( The mdt.TwoPointXY object is built from the inferred galaxy z distributions, the data type, and the tracer names. - :param inferred_galaxy_zdists_dict: a dictionary of inferred galaxy z - distributions. + :param projected_fields_dict: a dictionary of projected fields. :param tracer_names: a tuple of tracer names. :param data_type: the data type. @@ -65,8 +66,8 @@ def make_two_point_xy( a, b = mdt.MEASURED_TYPE_STRING_MAP[data_type] try: - igz1 = inferred_galaxy_zdists_dict[tracer_names[0]] - igz2 = inferred_galaxy_zdists_dict[tracer_names[1]] + igz1 = projected_fields_dict[tracer_names[0]] + igz2 = projected_fields_dict[tracer_names[1]] except KeyError as e: raise ValueError( f"Tracer '{e.args[0]}' not found in inferred galaxy z distributions." diff --git a/firecrown/metadata_types/__init__.py b/firecrown/metadata_types/__init__.py index 6b1354333..2fb57c0cd 100644 --- a/firecrown/metadata_types/__init__.py +++ b/firecrown/metadata_types/__init__.py @@ -8,7 +8,14 @@ measurement_is_compatible, measurements_types, ) -from firecrown.metadata_types._inferred_galaxy_zdist import InferredGalaxyZDist +from firecrown.metadata_types._two_point_tracers import ( + TomographicBin, + ProjectedField, + CMBLensing, + InferredGalaxyZDist, +) + +# Backwards compatibility: expose the legacy name from firecrown.metadata_types._measurements import ( ALL_MEASUREMENTS, ALL_MEASUREMENT_TYPES, @@ -46,7 +53,7 @@ SourceLensBinPairSelector, ThreeTwoBinPairSelector, TypeSourceBinPairSelector, - TomographicBinPair, + ProjectedFieldPair, register_bin_pair_selector, ) from firecrown.metadata_types._sacc_type_string import MEASURED_TYPE_STRING_MAP @@ -82,8 +89,11 @@ "TracerNames", "TRACER_NAMES_TOTAL", "TypeSource", - # Inferred galaxy zdist + # Tomographic bin / legacy name + "TomographicBin", "InferredGalaxyZDist", + "ProjectedField", + "CMBLensing", # Two-point types "TwoPointXY", "TwoPointHarmonic", @@ -118,7 +128,7 @@ "SourceLensBinPairSelector", "ThreeTwoBinPairSelector", "TypeSourceBinPairSelector", - "TomographicBinPair", + "ProjectedFieldPair", "MeasurementPair", "register_bin_pair_selector", ] diff --git a/firecrown/metadata_types/_inferred_galaxy_zdist.py b/firecrown/metadata_types/_inferred_galaxy_zdist.py deleted file mode 100644 index 249a117b3..000000000 --- a/firecrown/metadata_types/_inferred_galaxy_zdist.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Inferred galaxy redshift distribution types.""" - -from dataclasses import dataclass - -import numpy as np - -from firecrown.metadata_types._measurements import ALL_MEASUREMENT_TYPES, Measurement -from firecrown.metadata_types._utils import TypeSource -from firecrown.utils import YAMLSerializable - - -@dataclass(frozen=True, kw_only=True) -class InferredGalaxyZDist(YAMLSerializable): - """The class used to store the redshift resolution data for a sacc file. - - The sacc file is a complicated set of tracers (bins) and surveys. This class is - used to store the redshift resolution data for a single photometric bin. - """ - - bin_name: str - z: np.ndarray - dndz: np.ndarray - measurements: set[Measurement] - type_source: TypeSource = TypeSource.DEFAULT - - def __post_init__(self) -> None: - """Validate the redshift resolution data. - - - Make sure the z and dndz arrays have the same shape; - - The measurement must be of type Measurement. - - The bin_name should not be empty. - """ - if self.z.shape != self.dndz.shape: - raise ValueError("The z and dndz arrays should have the same shape.") - - for measurement in self.measurements: - if not isinstance(measurement, ALL_MEASUREMENT_TYPES): - raise ValueError("The measurement should be a Measurement.") - - if self.bin_name == "": - raise ValueError("The bin_name should not be empty.") - - def __eq__(self, other): - """Equality test for InferredGalaxyZDist. - - Two InferredGalaxyZDist are equal if they have equal bin_name, z, dndz, and - measurement. - """ - assert isinstance(other, InferredGalaxyZDist) - return ( - self.bin_name == other.bin_name - and np.array_equal(self.z, other.z) - and np.array_equal(self.dndz, other.dndz) - and self.measurements == other.measurements - ) - - @property - def measurement_list(self) -> list[Measurement]: - """Get the measurements as a sorted list.""" - return sorted(self.measurements) diff --git a/firecrown/metadata_types/_pair_selector.py b/firecrown/metadata_types/_pair_selector.py index 6d9a6ec34..4996c9947 100644 --- a/firecrown/metadata_types/_pair_selector.py +++ b/firecrown/metadata_types/_pair_selector.py @@ -29,7 +29,7 @@ ) from pydantic_core import PydanticUndefined, core_schema -from firecrown.metadata_types._inferred_galaxy_zdist import InferredGalaxyZDist +from firecrown.metadata_types._two_point_tracers import ProjectedField from firecrown.metadata_types._measurements import ( GALAXY_LENS_TYPES, GALAXY_SOURCE_TYPES, @@ -67,7 +67,7 @@ def register_bin_pair_selector(cls: type["BinPairSelector"]) -> type["BinPairSel # Type aliases for clarity -TomographicBinPair = tuple[InferredGalaxyZDist, InferredGalaxyZDist] +ProjectedFieldPair = tuple[ProjectedField, ProjectedField] """A pair of tomographic bin distributions to be correlated.""" MeasurementPair = tuple[Measurement, Measurement] @@ -77,7 +77,7 @@ def register_bin_pair_selector(cls: type["BinPairSelector"]) -> type["BinPairSel class BinPairSelector(BaseModel): """Base class for filtering pairs of tomographic bins in two-point measurements. - A BinPairSelector determines which pairs of `InferredGalaxyZDist` bins should be + A BinPairSelector determines which pairs of `ProjectedField` bins should be included when constructing `TwoPointXY` objects. Concrete implementations define specific selection criteria (e.g., auto-correlations only, specific bin names, measurement types, etc.). @@ -91,10 +91,10 @@ class BinPairSelector(BaseModel): kind: str @abstractmethod - def keep(self, zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if the pair should be kept. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param m: Pair of Measurement objects. :return: True if the pair should be kept, False otherwise. @@ -177,16 +177,16 @@ class AndBinPairSelector(BinPairSelector): kind: str = "and" pair_selectors: list[SerializeAsAny[BinPairSelector]] - def keep(self, zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if all of the bin pair selectors pass. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param m: Pair of Measurement objects. :return: True if all bin pair selectors pass, False otherwise. """ return all( - pair_selector.keep(zdist, m) for pair_selector in self.pair_selectors + pair_selector.keep(field_pair, m) for pair_selector in self.pair_selectors ) def model_post_init(self, _, /) -> None: @@ -215,16 +215,16 @@ class OrBinPairSelector(BinPairSelector): kind: str = "or" pair_selectors: list[SerializeAsAny[BinPairSelector]] - def keep(self, zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if any of the bin pair selectors pass. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param m: Pair of Measurement objects. :return: True if any bin pair selector passes, False otherwise. """ return any( - pair_selector.keep(zdist, m) for pair_selector in self.pair_selectors + pair_selector.keep(field_pair, m) for pair_selector in self.pair_selectors ) def model_post_init(self, _, /) -> None: @@ -253,15 +253,15 @@ class NotBinPairSelector(BinPairSelector): kind: str = "not" pair_selector: SerializeAsAny[BinPairSelector] - def keep(self, zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return the negation of the contained pair selector's result. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param m: Pair of Measurement objects. :return: True if the contained pair selector returns False, False otherwise. """ - return not self.pair_selector.keep(zdist, m) + return not self.pair_selector.keep(field_pair, m) class BadSelector(BinPairSelector): @@ -269,7 +269,7 @@ class BadSelector(BinPairSelector): kind: str = "bad-selector" - def keep(self, _zdist: TomographicBinPair, _m: MeasurementPair) -> bool: + def keep(self, _field_pair: ProjectedFieldPair, _m: MeasurementPair) -> bool: """Raise NotImplementedError always. :raise NotImplementedError: Always raised since this selector should not be @@ -283,10 +283,10 @@ class CompositeSelector(BinPairSelector): _impl: BinPairSelector = BadSelector() - def keep(self, zdist: TomographicBinPair, m: MeasurementPair): + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair): """Delegate to the underlying selector implementation.""" assert isinstance(self._impl, BinPairSelector) - return self._impl.keep(zdist, m) + return self._impl.keep(field_pair, m) @register_bin_pair_selector @@ -327,18 +327,18 @@ def validate_names(cls, value: list[list[str]]) -> list[tuple[str, str]]: assert len(names) == 2 return [(names[0], names[1]) for names in value] - def keep(self, zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if the bin name pair matches any configured pair. Note: Matching is currently order-dependent. To achieve symmetric matching, include both (name1, name2) and (name2, name1) in the names list. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param m: Pair of Measurement objects (unused). :return: True if the bin name pair matches any configured name pair. """ - return (zdist[0].bin_name, zdist[1].bin_name) in self.names + return (field_pair[0].bin_name, field_pair[1].bin_name) in self.names @register_bin_pair_selector @@ -351,15 +351,15 @@ class AutoNameBinPairSelector(BinPairSelector): kind: str = "auto-name" - def keep(self, zdist: TomographicBinPair, _m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, _m: MeasurementPair) -> bool: """Return True if both bins have the same bin name. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param _m: Pair of Measurement objects (unused). :return: True if both bins have the same name, False otherwise. """ - return zdist[0].bin_name == zdist[1].bin_name + return field_pair[0].bin_name == field_pair[1].bin_name @register_bin_pair_selector @@ -386,10 +386,10 @@ class AutoMeasurementBinPairSelector(BinPairSelector): kind: str = "auto-measurement" - def keep(self, _zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, _field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if both measurements are the same. - :param _zdist: Pair of InferredGalaxyZDist objects (unused). + :param _field_pair: Pair of TomographicBin objects (unused). :param m: Pair of Measurement objects. :return: True if both measurements are identical, False otherwise. @@ -457,10 +457,10 @@ class LeftMeasurementBinPairSelector(BinPairSelector): kind: str = "left-measurement" measurement_set: set[Measurement] - def keep(self, _zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, _field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if the left measurement is in the configured set. - :param _zdist: Pair of InferredGalaxyZDist objects (unused). + :param _field_pair: Pair of TomographicBin objects (unused). :param m: Pair of Measurement objects. :return: True if the left measurement is in the set, False otherwise. @@ -483,10 +483,10 @@ class RightMeasurementBinPairSelector(BinPairSelector): kind: str = "right-measurement" measurement_set: set[Measurement] - def keep(self, _zdist: TomographicBinPair, m: MeasurementPair) -> bool: + def keep(self, _field_pair: ProjectedFieldPair, m: MeasurementPair) -> bool: """Return True if the right measurement is in the configured set. - :param _zdist: Pair of InferredGalaxyZDist objects (unused). + :param _field_pair: Pair of TomographicBin objects (unused). :param m: Pair of Measurement objects. :return: True if the right measurement is in the set, False otherwise. @@ -597,14 +597,14 @@ class NameDiffBinPairSelector(BinPairSelector): same_name_prefix: bool = True neighbors_diff: int | list[int] = 1 - def keep(self, zdist: TomographicBinPair, _m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, _m: MeasurementPair) -> bool: """Return True if bin name indices differ by an allowed amount. Both bin names must match the pattern . The numeric parts are extracted and their difference is checked against the allowed values. If same_name_prefix is set, the text parts must also satisfy the constraint. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param _m: Pair of Measurement objects (unused). :return: True if bins are neighbors, False otherwise. @@ -616,8 +616,8 @@ def keep(self, zdist: TomographicBinPair, _m: MeasurementPair) -> bool: else self.neighbors_diff ) if not ( - (match1 := pattern.match(zdist[0].bin_name)) - and (match2 := pattern.match(zdist[1].bin_name)) + (match1 := pattern.match(field_pair[0].bin_name)) + and (match2 := pattern.match(field_pair[1].bin_name)) ): return False if self.same_name_prefix and (match1["text"] != match2["text"]): @@ -768,14 +768,14 @@ class TypeSourceBinPairSelector(BinPairSelector): kind: str = "type-source" type_source: TypeSource - def keep(self, zdist: TomographicBinPair, _m: MeasurementPair) -> bool: + def keep(self, field_pair: ProjectedFieldPair, _m: MeasurementPair) -> bool: """Return True if both bins have the same matching type-source. - :param zdist: Pair of InferredGalaxyZDist objects. + :param field_pair: Pair of TomographicBin objects. :param _m: Pair of Measurement objects (unused). :return: True if both bins have matching type-sources, False otherwise. """ - return (zdist[0].type_source == zdist[1].type_source) and ( - self.type_source == zdist[0].type_source + return (field_pair[0].type_source == field_pair[1].type_source) and ( + self.type_source == field_pair[0].type_source ) diff --git a/firecrown/metadata_types/_two_point_tracers.py b/firecrown/metadata_types/_two_point_tracers.py new file mode 100644 index 000000000..cc33c8bc5 --- /dev/null +++ b/firecrown/metadata_types/_two_point_tracers.py @@ -0,0 +1,174 @@ +"""Tomographic bin / tracer types. + +This module defines a Protocol for generic tracers and a concrete +implementation previously known as ``TomographicBin``. The concrete +class has been renamed to ``TomographicBin``; an alias ``TomographicBin`` +is provided for backwards compatibility. +""" + +from dataclasses import dataclass, field +from typing import Protocol, runtime_checkable + +import numpy as np + +from firecrown.metadata_types._measurements import Galaxies, CMB, Measurement +from firecrown.metadata_types._utils import TypeSource +from firecrown.utils import YAMLSerializable + + +@runtime_checkable +class ProjectedField(Protocol): + """Protocol describing the minimal tracer interface used across Firecrown. + + Implementations must provide these attributes so that code can operate on + different kinds of tracers (not only galaxy redshift distributions). + + The protocol is intentionally defined in terms of instance attributes, + which makes it directly compatible with frozen dataclasses such as + ``TomographicBin`` and other simple data containers. + + :var bin_name: String identifier for the tracer or tomographic bin. + :var measurements: Set of Measurement values supported by the tracer. + :var type_source: TypeSource describing how the tracer was obtained. + """ + + @property + def bin_name(self) -> str: # pragma: no cover - Protocol stub + """The name of the tracer or tomographic bin.""" + + @property + def measurements(self) -> set[Measurement]: # pragma: no cover - Protocol stub + """The set of Measurement types supported by the tracer.""" + + @property + def type_source(self) -> TypeSource: # pragma: no cover - Protocol stub + """The source of the tracer type information.""" + + +@dataclass(frozen=True, kw_only=True) +class TomographicBin(YAMLSerializable): + """Concrete tomographic bin holding redshift distribution arrays. + + This class is the renamed replacement for the legacy + ``TomographicBin`` type. It is intentionally frozen and lightweight. + """ + + bin_name: str + z: np.ndarray + dndz: np.ndarray + measurements: set[Measurement] + type_source: TypeSource = TypeSource.DEFAULT + + def __post_init__(self) -> None: + """Validate the redshift resolution data. + + - Make sure the z and dndz arrays have the same shape; + - The measurement entries must be members of Measurement enum; + - The bin_name should not be empty. + """ + if self.z.shape != self.dndz.shape: + raise ValueError("The z and dndz arrays should have the same shape.") + + for measurement in self.measurements: + if not isinstance(measurement, Galaxies): + raise ValueError("The measurement should be a Galaxies Measurement.") + + if self.bin_name == "": + raise ValueError("The bin_name should not be empty.") + + def __eq__(self, other: object) -> bool: + """Equality test for TomographicBin. + + Two TomographicBin objects are equal if they have equal bin_name, z, + dndz, and measurements. + + If ``other`` is another concrete ``TomographicBin`` instance we perform + the full array and set comparisons. If ``other`` implements the + :class:`Tracer` protocol but is not a ``TomographicBin``, return False + (they are distinct implementations). For unrelated types return + ``NotImplemented`` so Python can try the reflected operation. + """ + if isinstance(other, TomographicBin): + return ( + self.bin_name == other.bin_name + and np.array_equal(self.z, other.z) + and np.array_equal(self.dndz, other.dndz) + and self.measurements == other.measurements + ) + + # Another object that satisfies the Tracer protocol: intentionally + # considered not equal to this concrete TomographicBin implementation. + if isinstance(other, ProjectedField): + return False + + # Allow Python to attempt other.__eq__(self) + return NotImplemented + + @property + def measurement_list(self) -> list[Measurement]: + """Get the measurements as a sorted list.""" + return sorted(self.measurements) + # Exact same concrete type: compare contents + + +# Backwards compatibility: keep the old name available +InferredGalaxyZDist = TomographicBin + + +@dataclass(frozen=True, kw_only=True) +class CMBLensing(YAMLSerializable): + """Tracer type describing CMB lensing. + + This minimal tracer exposes the same public interface as other tracers + (``bin_name``, ``measurements``, ``type_source``) and additionally + stores the redshift of the last-scattering surface as ``z_lss``. + + The only measurement supported by this tracer by default is + :data:`firecrown.metadata_types._measurements.CMB.CONVERGENCE`. + """ + + bin_name: str + z_lss: float + measurements: set[Measurement] = field(default_factory=lambda: {CMB.CONVERGENCE}) + type_source: TypeSource = TypeSource.DEFAULT + + def __post_init__(self) -> None: + """Basic validation for the CMB lensing tracer. + + - Ensure bin_name is not empty; + - Ensure z_lss is a finite non-negative number; and + - Ensure measurements contain valid Measurement members. + """ + if self.bin_name == "": + raise ValueError("The bin_name should not be empty.") + + if not isinstance(self.z_lss, (int, float)) or not np.isfinite(self.z_lss): + raise ValueError("z_lss must be a finite float value.") + + if self.z_lss <= 0: + raise ValueError("z_lss must be positive.") + + for measurement in self.measurements: + if not isinstance(measurement, CMB): + raise ValueError("The measurement should be a CMB Measurement.") + + def __eq__(self, other: object) -> bool: + """Equality semantics for CMBLensing. + + Two CMBLensing instances are equal if they have the same bin_name, + z_lss, measurements and type_source. If ``other`` implements the + Tracer protocol but is not a CMBLensing instance, we return False. + For unrelated types, return NotImplemented. + """ + if isinstance(other, CMBLensing): + return ( + self.bin_name == other.bin_name + and self.z_lss == other.z_lss + and self.measurements == other.measurements + and self.type_source == other.type_source + ) + + if isinstance(other, ProjectedField): + return False + + return NotImplemented diff --git a/firecrown/metadata_types/_two_point_types.py b/firecrown/metadata_types/_two_point_types.py index 3702b9968..2072b57d1 100644 --- a/firecrown/metadata_types/_two_point_types.py +++ b/firecrown/metadata_types/_two_point_types.py @@ -9,7 +9,7 @@ from pydantic_core import core_schema from firecrown.metadata_types._compatibility import measurement_is_compatible -from firecrown.metadata_types._inferred_galaxy_zdist import InferredGalaxyZDist +from firecrown.metadata_types._two_point_tracers import ProjectedField from firecrown.metadata_types._measurements import ( HARMONIC_ONLY_MEASUREMENTS, REAL_ONLY_MEASUREMENTS, @@ -43,8 +43,10 @@ class TwoPointXY(YAMLSerializable): the Measurement enum (e.g., for Galaxies: shape measurements before counts). """ - x: InferredGalaxyZDist - y: InferredGalaxyZDist + # Use the generic ProjectedField protocol so other fields implementations can be + # used + x: ProjectedField + y: ProjectedField x_measurement: Measurement y_measurement: Measurement diff --git a/pytest.ini b/pytest.ini index afd647582..acc5590fa 100644 --- a/pytest.ini +++ b/pytest.ini @@ -23,3 +23,4 @@ filterwarnings = ignore:.*`trapz` is deprecated.*:DeprecationWarning:fastpt.* ignore:.*Empty index selected.*:UserWarning:sacc.* ignore:.*Conversion of an array with ndim > 0 to a scalar is deprecated.*:DeprecationWarning:scipy.* + ignore:.*Attribute \`SACCNAME\` of type \ cannot be written to HDF5 files.* diff --git a/tests/app/sacc/test_utils.py b/tests/app/sacc/test_utils.py index bc6a51440..30219cba1 100644 --- a/tests/app/sacc/test_utils.py +++ b/tests/app/sacc/test_utils.py @@ -10,7 +10,7 @@ @pytest.fixture(name="mock_tracer") -def fixture_mock_tracer() -> mdt.InferredGalaxyZDist: +def fixture_mock_tracer() -> mdt.TomographicBin: """Create mock tracer with Gaussian distribution.""" z = np.linspace(0.0, 2.0, 100) mean = 1.0 @@ -18,7 +18,7 @@ def fixture_mock_tracer() -> mdt.InferredGalaxyZDist: dndz = np.exp(-0.5 * ((z - mean) / sigma) ** 2) dndz /= np.trapezoid(dndz, z) - return mdt.InferredGalaxyZDist( + return mdt.TomographicBin( bin_name="bin0", z=z, dndz=dndz, @@ -30,9 +30,7 @@ def fixture_mock_tracer() -> mdt.InferredGalaxyZDist: class TestMeanStdTracer: """Tests for mean_std_tracer function.""" - def test_mean_std_tracer_gaussian( - self, mock_tracer: mdt.InferredGalaxyZDist - ) -> None: + def test_mean_std_tracer_gaussian(self, mock_tracer: mdt.TomographicBin) -> None: """Test mean_std_tracer with Gaussian distribution.""" mean, std = mean_std_tracer(mock_tracer) @@ -47,7 +45,7 @@ def test_mean_std_tracer_uniform(self) -> None: dndz = np.ones_like(z) dndz /= np.trapezoid(dndz, z) - tracer = mdt.InferredGalaxyZDist( + tracer = mdt.TomographicBin( bin_name="uniform", z=z, dndz=dndz, @@ -68,7 +66,7 @@ def test_mean_std_tracer_delta_function(self) -> None: dndz[center_idx] = 1.0 dndz /= np.trapezoid(dndz, z) - tracer = mdt.InferredGalaxyZDist( + tracer = mdt.TomographicBin( bin_name="delta", z=z, dndz=dndz, @@ -90,7 +88,7 @@ def test_mean_std_tracer_skewed_distribution(self) -> None: dndz = np.exp(-0.5 * ((z - 1.3) / 0.3) ** 2) dndz /= np.trapezoid(dndz, z) - tracer = mdt.InferredGalaxyZDist( + tracer = mdt.TomographicBin( bin_name="skewed", z=z, dndz=dndz, diff --git a/tests/conftest.py b/tests/conftest.py index 52ccb251c..8e0e479e3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,6 +8,7 @@ import ast from itertools import product import sys +from typing import assert_never import pytest import pyccl @@ -23,14 +24,17 @@ from firecrown.connector.mapping import MappingCosmoSIS, mapping_builder from firecrown.modeling_tools import ModelingTools from firecrown.metadata_types import ( - Measurement, + ALL_MEASUREMENTS, + Clusters, + CMB, + CMBLensing, Galaxies, - InferredGalaxyZDist, + Measurement, + TomographicBin, TracerNames, TwoPointHarmonic, - TwoPointXY, TwoPointReal, - ALL_MEASUREMENTS, + TwoPointXY, ) from firecrown.metadata_types._compatibility import ( measurement_is_compatible_harmonic, @@ -43,7 +47,6 @@ import firecrown.likelihood.number_counts as nc import firecrown.likelihood._two_point as tp import firecrown.likelihood._cmb as cmb -from firecrown.metadata_types import Clusters, CMB # Helper function for creating AST ClassDef nodes across Python versions if sys.version_info >= (3, 12): @@ -229,19 +232,19 @@ def fixture_tools_with_vanilla_cosmology() -> ModelingTools: @pytest.fixture(name="harmonic_bin_1") -def fixture_harmonic_bin_1(request) -> InferredGalaxyZDist: - """Generate an InferredGalaxyZDist object with 5 bins.""" +def fixture_harmonic_bin_1(request) -> TomographicBin: + """Generate an TomographicBin object with 5 bins.""" return request.param -def _make_harmonic_bin(name: str, z_mean: float, m: Measurement) -> InferredGalaxyZDist: - """Generate an InferredGalaxyZDist object with 5 bins.""" +def _make_harmonic_bin(name: str, z_mean: float, m: Measurement) -> TomographicBin: + """Generate an TomographicBin object with 5 bins.""" z = np.linspace(0.0, 1.0, 256) # Necessary to match the default lensing kernel size z_sigma = 0.05 dndz = np.exp(-0.5 * (z - z_mean) ** 2 / z_sigma**2) / ( np.sqrt(2 * np.pi) * z_sigma ) - x = InferredGalaxyZDist(bin_name=name, z=z, dndz=dndz, measurements={m}) + x = TomographicBin(bin_name=name, z=z, dndz=dndz, measurements={m}) return x @@ -281,36 +284,34 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: @pytest.fixture( name="all_harmonic_bins", ) -def fixture_all_harmonic_bins() -> list[InferredGalaxyZDist]: - """Generate a list of InferredGalaxyZDist objects with 4 bins.""" +def fixture_all_harmonic_bins() -> list[TomographicBin]: + """Generate a list of TomographicBin objects with 5 bins.""" z = np.linspace(0.0, 1.0, 256) dndzs = [ np.exp(-0.5 * (z - 0.5) ** 2 / 0.05**2) / (np.sqrt(2 * np.pi) * 0.05), np.exp(-0.5 * (z - 0.6) ** 2 / 0.05**2) / (np.sqrt(2 * np.pi) * 0.05), ] return [ - InferredGalaxyZDist( - bin_name=f"bin_{i + 1}", - z=z, - dndz=dndzs[i], - measurements={Galaxies.COUNTS, Galaxies.SHEAR_E}, + TomographicBin( + bin_name=f"bin_{int(m)}_{i + 1}", z=z, dndz=dndzs[i], measurements={m} ) for i in range(2) + for m in [Galaxies.COUNTS, Galaxies.SHEAR_E] ] @pytest.fixture( name="many_harmonic_bins", ) -def make_many_harmonic_bins() -> list[InferredGalaxyZDist]: - """Generate a list of InferredGalaxyZDist objects with 5 bins.""" +def make_many_harmonic_bins() -> list[TomographicBin]: + """Generate a list of TomographicBin objects with 5 bins.""" z = np.linspace(0.0, 1.0, 256) dndzs = [ np.exp(-0.5 * (z - mu) ** 2 / 0.05**2) / (np.sqrt(2 * np.pi) * 0.05) for mu in np.linspace(0.5, 0.9, 5) ] return [ - InferredGalaxyZDist( + TomographicBin( bin_name=f"bin_{i + 1}", z=z, dndz=dndzs[i], @@ -330,9 +331,9 @@ def make_many_harmonic_bins() -> list[InferredGalaxyZDist]: ], ids=["counts", "shear_t", "shear_minus", "shear_plus"], ) -def fixture_real_bin_1(request) -> InferredGalaxyZDist: - """Generate an InferredGalaxyZDist object with 5 bins.""" - x = InferredGalaxyZDist( +def fixture_real_bin_1(request) -> TomographicBin: + """Generate an TomographicBin object with 5 bins.""" + x = TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 5), dndz=np.array([0.1, 0.5, 0.2, 0.3, 0.4]), @@ -351,9 +352,9 @@ def fixture_real_bin_1(request) -> InferredGalaxyZDist: ], ids=["counts", "shear_t", "shear_minus", "shear_plus"], ) -def fixture_real_bin_2(request) -> InferredGalaxyZDist: - """Generate an InferredGalaxyZDist object with 3 bins.""" - x = InferredGalaxyZDist( +def fixture_real_bin_2(request) -> TomographicBin: + """Generate an TomographicBin object with 3 bins.""" + x = TomographicBin( bin_name="bin_2", z=np.linspace(0, 1, 3), dndz=np.array([0.1, 0.5, 0.4]), @@ -365,10 +366,10 @@ def fixture_real_bin_2(request) -> InferredGalaxyZDist: @pytest.fixture( name="all_real_bins", ) -def fixture_all_real_bins() -> list[InferredGalaxyZDist]: - """Generate a list of InferredGalaxyZDist objects with 5 bins.""" +def fixture_all_real_bins() -> list[TomographicBin]: + """Generate a list of TomographicBin objects with 5 bins.""" return [ - InferredGalaxyZDist( + TomographicBin( bin_name=f"bin_{i + 1}", z=np.linspace(0, 1, 5), dndz=np.array([0.1, 0.5, 0.2, 0.3, 0.4]), @@ -397,8 +398,8 @@ def fixture_window_1() -> ( @pytest.fixture(name="harmonic_two_point_xy") def fixture_harmonic_two_point_xy( - harmonic_bin_1: InferredGalaxyZDist, - harmonic_bin_2: InferredGalaxyZDist, + harmonic_bin_1: TomographicBin, + harmonic_bin_2: TomographicBin, ) -> TwoPointXY: """Generate a TwoPointXY object with 100 ells.""" m1 = list(harmonic_bin_1.measurements)[0] @@ -413,8 +414,8 @@ def fixture_harmonic_two_point_xy( @pytest.fixture(name="real_two_point_xy") def fixture_real_two_point_xy( - real_bin_1: InferredGalaxyZDist, - real_bin_2: InferredGalaxyZDist, + real_bin_1: TomographicBin, + real_bin_2: TomographicBin, ) -> TwoPointXY: """Generate a TwoPointXY object with 100 ells.""" m1 = list(real_bin_1.measurements)[0] @@ -1302,6 +1303,19 @@ def fixture_tp_factory( ) +@pytest.fixture(name="tp_factory_missing_counts") +def fixture_tp_factory_missing_counts( + wl_factory: wl.WeakLensingFactory, +) -> tp.TwoPointFactory: + """Generate a TwoPointFactory object.""" + return tp.TwoPointFactory( + correlation_space=tp.TwoPointCorrelationSpace.REAL, + weak_lensing_factories=[wl_factory], + number_counts_factories=[], + cmb_factories=[cmb.CMBConvergenceFactory()], + ) + + # Optimized fixtures that eliminate "incompatible measurements" skips @@ -1404,14 +1418,14 @@ def fixture_optimized_real_two_point_xy(optimized_real_measurement_pair) -> TwoP """ m1, m2 = optimized_real_measurement_pair - bin_1 = InferredGalaxyZDist( + bin_1 = TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 5), dndz=np.array([0.1, 0.5, 0.2, 0.3, 0.4]), measurements={m1}, ) - bin_2 = InferredGalaxyZDist( + bin_2 = TomographicBin( bin_name="bin_2", z=np.linspace(0, 1, 3), dndz=np.array([0.1, 0.5, 0.4]), @@ -1436,19 +1450,44 @@ def fixture_optimized_harmonic_two_point_xy( # Use different z-distribution for harmonic space z = np.linspace(0.0, 1.0, 256) # Match default lensing kernel size - bin_1 = InferredGalaxyZDist( - bin_name="bin_1", - z=z, - dndz=np.exp(-0.5 * (z - 0.5) ** 2 / 0.05**2) / (np.sqrt(2 * np.pi) * 0.05), - measurements={m1}, - ) - - bin_2 = InferredGalaxyZDist( - bin_name="bin_2", - z=z, - dndz=np.exp(-0.5 * (z - 0.6) ** 2 / 0.05**2) / (np.sqrt(2 * np.pi) * 0.05), - measurements={m2}, - ) + bin_1: TomographicBin | CMBLensing + bin_2: TomographicBin | CMBLensing + + match m1: + case Galaxies(): + bin_1 = TomographicBin( + bin_name="bin_1", + z=z, + dndz=np.exp(-0.5 * (z - 0.5) ** 2 / 0.05**2) + / (np.sqrt(2 * np.pi) * 0.05), + measurements={m1}, + ) + case CMB(): + bin_1 = CMBLensing( + bin_name="bin_1", + z_lss=1100.0, # CMB lensing source redshift + measurements={m1}, + ) + case _ as unreachable: + assert_never(unreachable) + + match m2: + case Galaxies(): + bin_2 = TomographicBin( + bin_name="bin_2", + z=z, + dndz=np.exp(-0.5 * (z - 0.6) ** 2 / 0.05**2) + / (np.sqrt(2 * np.pi) * 0.05), + measurements={m2}, + ) + case CMB(): + bin_2 = CMBLensing( + bin_name="bin_2", + z_lss=1100.0, # CMB lensing source redshift + measurements={m2}, + ) + case _ as unreachable: + assert_never(unreachable) return TwoPointXY(x=bin_1, y=bin_2, x_measurement=m1, y_measurement=m2) diff --git a/tests/fctools/test_generate_symbol_map.py b/tests/fctools/test_generate_symbol_map.py index c75bd0255..b30c8b68e 100644 --- a/tests/fctools/test_generate_symbol_map.py +++ b/tests/fctools/test_generate_symbol_map.py @@ -7,6 +7,7 @@ import json from types import ModuleType +import pytest from typer.testing import CliRunner from firecrown.fctools.generate_symbol_map import ( @@ -390,6 +391,12 @@ def test_get_all_symbols_with_generators(): runner = CliRunner() +@pytest.mark.filterwarnings( + r"ignore:.*firecrown\.ccl_factory is deprecated.*:DeprecationWarning" +) +@pytest.mark.filterwarnings( + r"ignore:.*firecrown\.parameters module is deprecated.*:DeprecationWarning" +) def test_main_outputs_valid_json(): """Test that main command outputs valid JSON to stdout.""" result = runner.invoke(app, []) diff --git a/tests/generators/test_inferred_galaxy_zdist.py b/tests/generators/test_inferred_galaxy_zdist.py index ed11b7a11..c2ff931f4 100644 --- a/tests/generators/test_inferred_galaxy_zdist.py +++ b/tests/generators/test_inferred_galaxy_zdist.py @@ -1,4 +1,4 @@ -"""Tests for the module firecrown.generators.inferred_galaxy_zdist.""" +"""Tests for the module firecrown.generators.tomographic_bin.""" from typing import Any from itertools import pairwise, product diff --git a/tests/likelihood/gauss_family/statistic/source/test_source.py b/tests/likelihood/gauss_family/statistic/source/test_source.py index a610f6154..bfaa499f8 100644 --- a/tests/likelihood/gauss_family/statistic/source/test_source.py +++ b/tests/likelihood/gauss_family/statistic/source/test_source.py @@ -206,7 +206,7 @@ def test_weak_lensing_source_create_ready(sacc_galaxy_cells_src0_src0): src0 = next((obj for obj in all_tracers if obj.bin_name == "src0"), None) assert src0 is not None - source_ready = wl.WeakLensing.create_ready(inferred_zdist=src0) + source_ready = wl.WeakLensing.create_ready(tomographic_bin=src0) source_read = wl.WeakLensing(sacc_tracer="src0") source_read.read(sacc_data) @@ -223,7 +223,7 @@ def test_weak_lensing_source_factory(sacc_galaxy_cells_src0_src0): assert src0 is not None wl_factory = wl.WeakLensingFactory(per_bin_systematics=[], global_systematics=[]) - source_ready = wl_factory.create(inferred_zdist=src0) + source_ready = wl_factory.create(tomographic_bin=src0) source_read = wl.WeakLensing(sacc_tracer="src0") source_read.read(sacc_data) @@ -240,9 +240,9 @@ def test_weak_lensing_source_factory_cache(sacc_galaxy_cells_src0_src0): assert src0 is not None wl_factory = wl.WeakLensingFactory(per_bin_systematics=[], global_systematics=[]) - source_ready = wl_factory.create(inferred_zdist=src0) + source_ready = wl_factory.create(tomographic_bin=src0) - assert source_ready is wl_factory.create(inferred_zdist=src0) + assert source_ready is wl_factory.create(tomographic_bin=src0) @pytest.mark.parametrize("include_z_dependence", [True, False]) @@ -264,7 +264,7 @@ def test_weak_lensing_source_factory_global_systematics( wl_factory = wl.WeakLensingFactory( per_bin_systematics=[], global_systematics=global_systematics ) - source_ready = wl_factory.create(inferred_zdist=src0) + source_ready = wl_factory.create(tomographic_bin=src0) # pylint: disable=protected-access source_read = wl.WeakLensing( @@ -312,7 +312,7 @@ def test_number_counts_source_create_ready(sacc_galaxy_cells_lens0_lens0): lens0 = next((obj for obj in all_tracers if obj.bin_name == "lens0"), None) assert lens0 is not None - source_ready = nc.NumberCounts.create_ready(inferred_zdist=lens0) + source_ready = nc.NumberCounts.create_ready(tomographic_bin=lens0) source_read = nc.NumberCounts(sacc_tracer="lens0") source_read.read(sacc_data) @@ -329,7 +329,7 @@ def test_number_counts_source_factory(sacc_galaxy_cells_lens0_lens0): assert lens0 is not None nc_factory = nc.NumberCountsFactory(per_bin_systematics=[], global_systematics=[]) - source_ready = nc_factory.create(inferred_zdist=lens0) + source_ready = nc_factory.create(tomographic_bin=lens0) source_read = nc.NumberCounts(sacc_tracer="lens0") source_read.read(sacc_data) @@ -346,9 +346,9 @@ def test_number_counts_source_factory_cache(sacc_galaxy_cells_lens0_lens0): assert lens0 is not None nc_factory = nc.NumberCountsFactory(per_bin_systematics=[], global_systematics=[]) - source_ready = nc_factory.create(inferred_zdist=lens0) + source_ready = nc_factory.create(tomographic_bin=lens0) - assert source_ready is nc_factory.create(inferred_zdist=lens0) + assert source_ready is nc_factory.create(tomographic_bin=lens0) def test_number_counts_source_factory_global_systematics(sacc_galaxy_cells_lens0_lens0): @@ -363,7 +363,7 @@ def test_number_counts_source_factory_global_systematics(sacc_galaxy_cells_lens0 per_bin_systematics=[], global_systematics=global_systematics, ) - source_ready = nc_factory.create(inferred_zdist=lens0) + source_ready = nc_factory.create(tomographic_bin=lens0) # pylint: disable=protected-access source_read = nc.NumberCounts( diff --git a/tests/likelihood/gauss_family/statistic/test_two_point.py b/tests/likelihood/gauss_family/statistic/test_two_point.py index 380f07a43..59cd36fc4 100644 --- a/tests/likelihood/gauss_family/statistic/test_two_point.py +++ b/tests/likelihood/gauss_family/statistic/test_two_point.py @@ -40,7 +40,7 @@ ) from firecrown.metadata_types import ( Galaxies, - InferredGalaxyZDist, + TomographicBin, GALAXY_LENS_TYPES, GALAXY_SOURCE_TYPES, ) @@ -450,7 +450,7 @@ def test_two_point_lens0_lens0_data_and_conf_warn(sacc_galaxy_xis_lens0_lens0) - def test_use_source_factory( - harmonic_bin_1: InferredGalaxyZDist, tp_factory: TwoPointFactory + harmonic_bin_1: TomographicBin, tp_factory: TwoPointFactory ) -> None: measurement = list(harmonic_bin_1.measurements)[0] source = use_source_factory(harmonic_bin_1, measurement, tp_factory) @@ -467,7 +467,7 @@ def test_use_source_factory( def test_use_source_factory_invalid_measurement( - harmonic_bin_1: InferredGalaxyZDist, tp_factory: TwoPointFactory + harmonic_bin_1: TomographicBin, tp_factory: TwoPointFactory ) -> None: with pytest.raises( ValueError, diff --git a/tests/likelihood/lkdir/lkmodule.py b/tests/likelihood/lkdir/lkmodule.py index 3e1060c09..19c9a79b0 100644 --- a/tests/likelihood/lkdir/lkmodule.py +++ b/tests/likelihood/lkdir/lkmodule.py @@ -6,7 +6,7 @@ from firecrown.updatable import DerivedParameterCollection, DerivedParameter from firecrown.likelihood._likelihood import Likelihood, NamedParameters from firecrown.modeling_tools import ModelingTools -from firecrown import parameters +from firecrown import updatable class EmptyLikelihood(Likelihood): @@ -58,7 +58,7 @@ def __init__(self, params: NamedParameters): parameter_prefix value and creates a sampler parameter called "sampler_param0". """ super().__init__(parameter_prefix=params.get_string("parameter_prefix")) - self.sampler_param0 = parameters.register_new_updatable_parameter( + self.sampler_param0 = updatable.register_new_updatable_parameter( default_value=1.0 ) diff --git a/tests/likelihood/test_weak_lensing.py b/tests/likelihood/test_weak_lensing.py index 9befae3cc..31a75964c 100644 --- a/tests/likelihood/test_weak_lensing.py +++ b/tests/likelihood/test_weak_lensing.py @@ -8,7 +8,7 @@ import pyccl.nl_pt import firecrown.likelihood._weak_lensing as wl -from firecrown.metadata_types import InferredGalaxyZDist, Galaxies +from firecrown.metadata_types import TomographicBin, Galaxies from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ParamsMap @@ -257,7 +257,7 @@ def test_init(self): def test_create_ready(self): """Test WeakLensing.create_ready class method.""" - zdist = InferredGalaxyZDist( + zdist = TomographicBin( bin_name="test_bin", z=np.linspace(0.1, 2.0, 50), dndz=np.exp(-(((np.linspace(0.1, 2.0, 50) - 0.5) / 0.2) ** 2)), @@ -456,7 +456,7 @@ def test_weak_lensing_factory(): global_systematics=[wl.LinearAlignmentSystematicFactory()], ) - zdist = InferredGalaxyZDist( + zdist = TomographicBin( bin_name="test_bin", z=np.linspace(0.1, 2.0, 50), dndz=np.exp(-(((np.linspace(0.1, 2.0, 50) - 0.5) / 0.2) ** 2)), diff --git a/tests/metadata/test_bin_pair_selector.py b/tests/metadata/test_bin_pair_selector.py index 7fc538232..6f38e064b 100644 --- a/tests/metadata/test_bin_pair_selector.py +++ b/tests/metadata/test_bin_pair_selector.py @@ -24,7 +24,7 @@ class MissingBinPairSelector(mt.BinPairSelector): """BinPairSelector with missing kind.""" def keep( - self, _zdist: mt.TomographicBinPair, _m: mt.MeasurementPair + self, _field_pair: mt.ProjectedFieldPair, _m: mt.MeasurementPair ) -> bool: return True @@ -41,7 +41,7 @@ class Duplicate1BinPairSelector(mt.BinPairSelector): kind: str = "foo" def keep( - self, _zdist: mt.TomographicBinPair, _m: mt.MeasurementPair + self, _field_pair: mt.ProjectedFieldPair, _m: mt.MeasurementPair ) -> bool: return True @@ -52,7 +52,7 @@ class Duplicate2BinPairSelector(mt.BinPairSelector): kind: str = "foo" def keep( - self, _zdist: mt.TomographicBinPair, _m: mt.MeasurementPair + self, _field_pair: mt.ProjectedFieldPair, _m: mt.MeasurementPair ) -> bool: return True @@ -69,8 +69,8 @@ def test_pair_selector_auto(all_harmonic_bins): all_harmonic_bins, auto_pair_selector ) # AutoBinPairSelector should create all auto-combinations - # There is two measurements per bin in all_harmonic_bins - assert len(two_point_xy_combinations) == 2 * len(all_harmonic_bins) + # There is one measurement per bin in all_harmonic_bins + assert len(two_point_xy_combinations) == len(all_harmonic_bins) for two_point_xy in two_point_xy_combinations: assert two_point_xy.x == two_point_xy.y assert two_point_xy.x_measurement == two_point_xy.y_measurement @@ -131,15 +131,18 @@ def test_pair_selector_auto_source_lens(all_harmonic_bins): def test_pair_selector_named(all_harmonic_bins): - named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1", "bin_2")]) + named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1_1", "bin_1_2")]) two_point_xy_combinations = make_binned_two_point_filtered( all_harmonic_bins, named_pair_selector ) # NamedBinPairSelector should create all named combinations - assert len(two_point_xy_combinations) == 3 + assert len(two_point_xy_combinations) == 1 for two_point_xy in two_point_xy_combinations: - assert {two_point_xy.x.bin_name, two_point_xy.y.bin_name} == {"bin_1", "bin_2"} + assert {two_point_xy.x.bin_name, two_point_xy.y.bin_name} == { + "bin_1_1", + "bin_1_2", + } def test_pair_selector_type_source(all_harmonic_bins): @@ -173,15 +176,18 @@ def test_pair_selector_type_source(all_harmonic_bins): def test_pair_selector_not_named(all_harmonic_bins): - named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1", "bin_2")]) + named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1_1", "bin_1_2")]) two_point_xy_combinations = make_binned_two_point_filtered( all_harmonic_bins, ~named_pair_selector ) # NamedBinPairSelector should create all named combinations - assert len(two_point_xy_combinations) == 7 + assert len(two_point_xy_combinations) == 9 for two_point_xy in two_point_xy_combinations: - assert [two_point_xy.x.bin_name, two_point_xy.y.bin_name] != ["bin_1", "bin_2"] + assert [two_point_xy.x.bin_name, two_point_xy.y.bin_name] != [ + "bin_1_1", + "bin_1_2", + ] def test_pair_selector_first_neighbor(many_harmonic_bins): @@ -672,8 +678,8 @@ def test_auto_bin_pair_selector(all_harmonic_bins): all_harmonic_bins, auto_pair_selector ) # AutoBinPairSelector should create all auto-combinations - # There are two measurements per bin in all_harmonic_bins - assert len(two_point_xy_combinations) == 2 * len(all_harmonic_bins) + # There are one measurement per bin in all_harmonic_bins + assert len(two_point_xy_combinations) == len(all_harmonic_bins) for two_point_xy in two_point_xy_combinations: assert two_point_xy.x == two_point_xy.y assert two_point_xy.x_measurement == two_point_xy.y_measurement @@ -998,24 +1004,26 @@ def test_cross_selectors_are_inverses(): # CrossName vs AutoName auto_name = mt.AutoNameBinPairSelector() cross_name = mt.CrossNameBinPairSelector() - for zdist, measurements in test_cases: - assert auto_name.keep(zdist, measurements) != cross_name.keep( - zdist, measurements + for field_pair, measurements in test_cases: + assert auto_name.keep(field_pair, measurements) != cross_name.keep( + field_pair, measurements ) # CrossMeasurement vs AutoMeasurement auto_measurement = mt.AutoMeasurementBinPairSelector() cross_measurement = mt.CrossMeasurementBinPairSelector() - for zdist, measurements in test_cases: - assert auto_measurement.keep(zdist, measurements) != cross_measurement.keep( - zdist, measurements - ) + for field_pair, measurements in test_cases: + assert auto_measurement.keep( + field_pair, measurements + ) != cross_measurement.keep(field_pair, measurements) # CrossBin vs AutoBin auto_bin = mt.AutoBinPairSelector() cross_bin = mt.CrossBinPairSelector() - for zdist, measurements in test_cases: - assert auto_bin.keep(zdist, measurements) != cross_bin.keep(zdist, measurements) + for field_pair, measurements in test_cases: + assert auto_bin.keep(field_pair, measurements) != cross_bin.keep( + field_pair, measurements + ) # ============================================================================ diff --git a/tests/metadata/test_cmb_tracer.py b/tests/metadata/test_cmb_tracer.py new file mode 100644 index 000000000..8bcf454b5 --- /dev/null +++ b/tests/metadata/test_cmb_tracer.py @@ -0,0 +1,66 @@ +import numpy as np +import pytest + +from firecrown.metadata_types import CMBLensing, TomographicBin +from firecrown.metadata_types import CMB, Galaxies + + +def make_tomobin() -> TomographicBin: + z = np.array([0.0, 0.5, 1.0]) + dndz = np.array([0.1, 0.2, 0.7]) + return TomographicBin( + bin_name="src0", z=z, dndz=dndz, measurements={Galaxies.COUNTS} + ) + + +def test_cmblensing_basic(): + cmb = CMBLensing(bin_name="cmb0", z_lss=1090.0) + assert cmb.bin_name == "cmb0" + assert cmb.z_lss == 1090.0 + assert cmb.measurements == {CMB.CONVERGENCE} + + +def test_cmblensing_validation(): + with pytest.raises(ValueError, match="bin_name should not be empty"): + CMBLensing(bin_name="", z_lss=1090.0) + + with pytest.raises(ValueError, match="z_lss must be a finite float value."): + CMBLensing(bin_name="cmb0", z_lss=float("nan")) + + with pytest.raises(ValueError, match="z_lss must be positive"): + CMBLensing(bin_name="cmb0", z_lss=-1.0) + + with pytest.raises( + ValueError, match="The measurement should be a CMB Measurement." + ): + CMBLensing(bin_name="cmb0", z_lss=1100.0, measurements={Galaxies.COUNTS}) + + +def test_tomographicbin_equality_and_cmb_mismatch(): + tb1 = make_tomobin() + tb2 = TomographicBin( + bin_name="src0", + z=np.array([0.0, 0.5, 1.0]), + dndz=np.array([0.1, 0.2, 0.7]), + measurements={Galaxies.COUNTS}, + ) + + assert tb1 == tb2 + + cmb = CMBLensing(bin_name="cmb0", z_lss=1090.0) + # Different tracer implementations should not be equal + assert (tb1 == cmb) is False + assert (cmb == tb1) is False + + # Unrelated types should not be considered equal + assert (tb1 == object()) is False + assert (cmb == object()) is False + + +def test_cmblensing_equality(): + c1 = CMBLensing(bin_name="cmb0", z_lss=1090.0) + c2 = CMBLensing(bin_name="cmb0", z_lss=1090.0) + assert c1 == c2 + + c3 = CMBLensing(bin_name="cmb1", z_lss=1090.0) + assert c1 != c3 diff --git a/tests/metadata/test_data_functions.py b/tests/metadata/test_data_functions.py index b06972003..c17755c2d 100644 --- a/tests/metadata/test_data_functions.py +++ b/tests/metadata/test_data_functions.py @@ -10,8 +10,8 @@ from firecrown.metadata_types import ( TwoPointReal, TwoPointHarmonic, - InferredGalaxyZDist, TwoPointFilterMethod, + TomographicBin, Galaxies, ) from firecrown.metadata_functions import make_all_photoz_bin_combinations @@ -28,7 +28,7 @@ @pytest.fixture(name="harmonic_bins") def fixture_harmonic_bins( - all_harmonic_bins: list[InferredGalaxyZDist], + all_harmonic_bins: list[TomographicBin], ) -> list[TwoPointMeasurement]: """Create a list of TwoPointMeasurement with harmonic metadata.""" all_xy = make_all_photoz_bin_combinations(all_harmonic_bins) @@ -48,7 +48,7 @@ def fixture_harmonic_bins( @pytest.fixture(name="harmonic_window_bins") def fixture_harmonic_window_bins( - all_harmonic_bins: list[InferredGalaxyZDist], + all_harmonic_bins: list[TomographicBin], ) -> list[TwoPointMeasurement]: """Create a list of TwoPointMeasurement with harmonic metadata.""" all_xy = make_all_photoz_bin_combinations(all_harmonic_bins) @@ -76,7 +76,7 @@ def fixture_harmonic_window_bins( @pytest.fixture(name="real_bins") def fixture_real_bins( - all_real_bins: list[InferredGalaxyZDist], + all_real_bins: list[TomographicBin], ) -> list[TwoPointMeasurement]: """Create a list of TwoPointMeasurement with real metadata.""" all_xy = make_all_photoz_bin_combinations(all_real_bins) @@ -405,7 +405,7 @@ def test_two_point_harmonic_window_bin_filter_collection_call( def test_two_point_harmonic_bin_filter_collection_call_require( - harmonic_bin_1: InferredGalaxyZDist, + harmonic_bin_1: TomographicBin, ) -> None: harmonic_filter_collection_no_empty = TwoPointBinFilterCollection( filters=[ @@ -431,7 +431,7 @@ def test_two_point_harmonic_bin_filter_collection_call_require( def test_two_point_harmonic_bin_filter_collection_call_no_empty( - harmonic_bin_1: InferredGalaxyZDist, + harmonic_bin_1: TomographicBin, ) -> None: cm = list(harmonic_bin_1.measurements)[0] harmonic_filter_collection_no_empty = TwoPointBinFilterCollection( @@ -460,7 +460,7 @@ def test_two_point_harmonic_bin_filter_collection_call_no_empty( def test_two_point_harmonic_bin_filter_collection_call_empty( - harmonic_bin_1: InferredGalaxyZDist, + harmonic_bin_1: TomographicBin, ) -> None: cm = list(harmonic_bin_1.measurements)[0] harmonic_filter_collection_no_empty = TwoPointBinFilterCollection( @@ -519,7 +519,7 @@ def test_two_point_real_bin_filter_collection_call( def test_two_point_real_bin_filter_collection_call_require( - real_bin_1: InferredGalaxyZDist, + real_bin_1: TomographicBin, ) -> None: cm = list(real_bin_1.measurements)[0] real_filter_collection_no_empty = TwoPointBinFilterCollection( @@ -544,7 +544,7 @@ def test_two_point_real_bin_filter_collection_call_require( def test_two_point_real_bin_filter_collection_call_no_empty( - real_bin_1: InferredGalaxyZDist, + real_bin_1: TomographicBin, ) -> None: cm = list(real_bin_1.measurements)[0] real_filter_collection_no_empty = TwoPointBinFilterCollection( @@ -575,7 +575,7 @@ def test_two_point_real_bin_filter_collection_call_no_empty( def test_two_point_real_bin_filter_collection_call_empty( - real_bin_1: InferredGalaxyZDist, + real_bin_1: TomographicBin, ) -> None: cm = list(real_bin_1.measurements)[0] real_filter_collection_no_empty = TwoPointBinFilterCollection( @@ -647,7 +647,7 @@ def test_bin_filter_methods( bin_col = TwoPointBinFilterCollection( filters=[ TwoPointBinFilter.from_args_auto( - "bin_1", Galaxies.COUNTS, 5, 10, method=method + "bin_5_1", Galaxies.COUNTS, 5, 10, method=method ) ] ) @@ -658,19 +658,19 @@ def test_bin_filter_methods( def test_raise_with_two_bins_same_name(): # Here we test if make_all_photoz_bin_combinations raises an error when # there are two bins with the same name and measurement. - igz1 = InferredGalaxyZDist( + igz1 = TomographicBin( bin_name="bin_1", dndz=np.linspace(0.0, 2.0, 100), z=np.linspace(0.0, 2.0, 100), measurements={Galaxies.COUNTS}, ) - igz2 = InferredGalaxyZDist( + igz2 = TomographicBin( bin_name="bin_2", dndz=np.linspace(0.0, 2.0, 100), z=np.linspace(0.0, 2.0, 100), measurements={Galaxies.COUNTS}, ) - igz3 = InferredGalaxyZDist( + igz3 = TomographicBin( bin_name="bin_3", dndz=np.linspace(0.0, 2.0, 100), z=np.linspace(0.0, 2.0, 100), diff --git a/tests/metadata/test_metadata_two_point.py b/tests/metadata/test_metadata_two_point.py index 7cdaf27cc..1dab0fbce 100644 --- a/tests/metadata/test_metadata_two_point.py +++ b/tests/metadata/test_metadata_two_point.py @@ -2,6 +2,7 @@ Tests for the module firecrown.metadata_types and firecrown.metadata_functions. """ +import dataclasses import pytest import sacc import numpy as np @@ -10,12 +11,16 @@ ALL_MEASUREMENTS, Clusters, CMB, + CMBLensing, Galaxies, - InferredGalaxyZDist, + Measurement, + ProjectedField, + TomographicBin, TracerNames, TwoPointHarmonic, - TwoPointXY, TwoPointReal, + TwoPointXY, + TypeSource, ) from firecrown.metadata_functions import ( make_two_point_xy, @@ -39,7 +44,7 @@ def test_inferred_galaxy_z_dist(): - z_dist = InferredGalaxyZDist( + z_dist = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -57,7 +62,7 @@ def test_inferred_galaxy_z_dist_bad_shape(): with pytest.raises( ValueError, match="The z and dndz arrays should have the same shape." ): - InferredGalaxyZDist( + TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(101), @@ -66,8 +71,10 @@ def test_inferred_galaxy_z_dist_bad_shape(): def test_inferred_galaxy_z_dist_bad_type(): - with pytest.raises(ValueError, match="The measurement should be a Measurement."): - InferredGalaxyZDist( + with pytest.raises( + ValueError, match="The measurement should be a Galaxies Measurement." + ): + TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -77,7 +84,7 @@ def test_inferred_galaxy_z_dist_bad_type(): def test_inferred_galaxy_z_dist_bad_name(): with pytest.raises(ValueError, match="The bin_name should not be empty."): - InferredGalaxyZDist( + TomographicBin( bin_name="", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -86,13 +93,13 @@ def test_inferred_galaxy_z_dist_bad_name(): def test_two_point_xy_gal_gal(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -108,13 +115,13 @@ def test_two_point_xy_gal_gal(): def test_two_point_xy_gal_gal_invalid_x_measurement(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -130,13 +137,13 @@ def test_two_point_xy_gal_gal_invalid_x_measurement(): def test_two_point_xy_gal_gal_invalid_y_measurement(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -152,13 +159,12 @@ def test_two_point_xy_gal_gal_invalid_y_measurement(): def test_two_point_xy_cmb_gal(): - x = InferredGalaxyZDist( + x = CMBLensing( bin_name="b_name1", - z=np.linspace(0, 1, 100), - dndz=np.ones(100), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -174,13 +180,13 @@ def test_two_point_xy_cmb_gal(): def test_two_point_xy_invalid(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -196,13 +202,13 @@ def test_two_point_xy_invalid(): def test_two_point_harmonic(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -221,13 +227,13 @@ def test_two_point_harmonic(): def test_two_point_harmonic_invalid_ells(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -245,13 +251,13 @@ def test_two_point_harmonic_invalid_ells(): def test_two_point_harmonic_invalid_type(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -350,13 +356,13 @@ def test_two_point_cwindow_invalid(): weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -374,13 +380,13 @@ def test_two_point_cwindow_invalid(): def test_two_point_cwindow_invalid_window(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -402,13 +408,13 @@ def test_two_point_cwindow_invalid_window_shape(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400, dtype=np.float64) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -428,13 +434,13 @@ def test_two_point_cwindow_window_ell_not_match(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -454,13 +460,13 @@ def test_two_point_cwindow_missing_window_ells(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -480,13 +486,13 @@ def test_two_point_cwindow_window_ells_wrong_shape(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -512,13 +518,13 @@ def test_two_point_cwindow_window_ells_wrong_len(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -543,13 +549,13 @@ def test_two_point_cwindow_window_ells_wrong_len(): def test_two_point_cwindow_no_window_with_window_ells(): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -567,13 +573,13 @@ def test_two_point_cwindow_no_window_with_window_ells(): def test_two_point_real(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -592,13 +598,13 @@ def test_two_point_real(): def test_two_point_real_invalid(): - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -643,11 +649,11 @@ def test_measurement_serialization(): assert t == recovered -def test_inferred_galaxy_zdist_serialization(harmonic_bin_1: InferredGalaxyZDist): +def test_inferred_galaxy_zdist_serialization(harmonic_bin_1: TomographicBin): s = harmonic_bin_1.to_yaml() # Take a look at how hideous the generated string # is. - recovered = InferredGalaxyZDist.from_yaml(s) + recovered = TomographicBin.from_yaml(s) assert harmonic_bin_1 == recovered @@ -801,13 +807,20 @@ def test_two_point_from_metadata_xi_theta(optimized_real_two_point_xy, tp_factor def test_two_point_from_metadata_cells_unsupported_type(tp_factory): ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) - x = InferredGalaxyZDist( + + @dataclasses.dataclass(frozen=True, kw_only=True) + class DummyClusterBin: + """A dummy cluster bin for testing unsupported types.""" + + bin_name: str + measurements: set[Measurement] + type_source: TypeSource = TypeSource("cluster") + + x = DummyClusterBin( bin_name="b_name1", - z=np.linspace(0, 1, 100), - dndz=np.ones(100), measurements={Clusters.COUNTS}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -837,13 +850,12 @@ def fixture_tp_factory_with_cmb(): def test_two_point_from_metadata_cmb_supported(tp_factory_with_cmb): """Test that CMB measurements work when CMB factory is provided.""" ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) - x = InferredGalaxyZDist( + x = CMBLensing( bin_name="b_name1", - z=np.linspace(0, 1, 100), - dndz=np.ones(100), measurements={CMB.CONVERGENCE}, + z_lss=1100.0, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="b_name2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -866,23 +878,23 @@ def test_two_point_from_metadata_cmb_supported(tp_factory_with_cmb): def test_make_two_point_xy_valid_galaxies(): """Test make_two_point_xy with valid galaxy measurements.""" - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="shear_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="shear_bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - inferred_dict = {"shear_bin_0": x, "shear_bin_1": y} + tomographic_dict: dict[str, ProjectedField] = {"shear_bin_0": x, "shear_bin_1": y} tracer_names = TracerNames("shear_bin_0", "shear_bin_1") data_type = "galaxy_shear_cl_ee" - xy = make_two_point_xy(inferred_dict, tracer_names, data_type) + xy = make_two_point_xy(tomographic_dict, tracer_names, data_type) assert xy.x == x assert xy.y == y @@ -892,23 +904,25 @@ def test_make_two_point_xy_valid_galaxies(): def test_make_two_point_xy_valid_cmb_galaxy(): """Test make_two_point_xy with CMB-galaxy measurements.""" - cmb = InferredGalaxyZDist( + cmb = CMBLensing( bin_name="cmb_convergence", - z=np.array([1100.0]), - dndz=np.array([1.0]), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) - galaxy = InferredGalaxyZDist( + galaxy = TomographicBin( bin_name="galaxy_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - inferred_dict = {"cmb_convergence": cmb, "galaxy_bin_0": galaxy} + tomographic_dict: dict[str, ProjectedField] = { + "cmb_convergence": cmb, + "galaxy_bin_0": galaxy, + } tracer_names = TracerNames("cmb_convergence", "galaxy_bin_0") data_type = harmonic(CMB.CONVERGENCE, Galaxies.COUNTS) - xy = make_two_point_xy(inferred_dict, tracer_names, data_type) + xy = make_two_point_xy(tomographic_dict, tracer_names, data_type) assert xy.x == cmb assert xy.y == galaxy @@ -918,25 +932,27 @@ def test_make_two_point_xy_valid_cmb_galaxy(): def test_make_two_point_xy_valid_cmb_galaxy_needs_swap(): """Test make_two_point_xy with CMB-galaxy measurements.""" - cmb = InferredGalaxyZDist( + cmb = CMBLensing( bin_name="cmb_convergence", - z=np.array([1100.0]), - dndz=np.array([1.0]), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) - galaxy = InferredGalaxyZDist( + galaxy = TomographicBin( bin_name="galaxy_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ) - inferred_dict = {"cmb_convergence": cmb, "galaxy_bin_0": galaxy} + tomographic_dict: dict[str, ProjectedField] = { + "cmb_convergence": cmb, + "galaxy_bin_0": galaxy, + } tracer_names = TracerNames("galaxy_bin_0", "cmb_convergence") data_type = harmonic(CMB.CONVERGENCE, Galaxies.COUNTS) # Even though the order is swapped, this should still work this behavior will be # removed in the future. It is kept for backwards compatibility and to avoid # breaking existing data files. - xy = make_two_point_xy(inferred_dict, tracer_names, data_type) + xy = make_two_point_xy(tomographic_dict, tracer_names, data_type) assert xy.x == cmb assert xy.y == galaxy @@ -951,20 +967,20 @@ def test_make_two_point_xy_missing_tracer_zdist(): when a requested tracer name is not in the inferred galaxy z distributions dictionary. """ - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="shear_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) # Only provide one tracer in the dictionary - inferred_dict = {"shear_bin_0": x} + tomographic_dict: dict[str, ProjectedField] = {"shear_bin_0": x} # But request two tracers (second one doesn't exist) tracer_names = TracerNames("shear_bin_0", "shear_bin_1") data_type = harmonic(Galaxies.SHEAR_E, Galaxies.SHEAR_E) with pytest.raises(ValueError) as exc_info: - make_two_point_xy(inferred_dict, tracer_names, data_type) + make_two_point_xy(tomographic_dict, tracer_names, data_type) error_msg = str(exc_info.value) assert "shear_bin_1" in error_msg @@ -973,24 +989,24 @@ def test_make_two_point_xy_missing_tracer_zdist(): def test_make_two_point_xy_missing_x_measurement(): """Test make_two_point_xy when first tracer lacks required measurement.""" - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="shear_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, # Has SHEAR_T but needs SHEAR_E ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="shear_bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - inferred_dict = {"shear_bin_0": x, "shear_bin_1": y} + tomographic_dict: dict[str, ProjectedField] = {"shear_bin_0": x, "shear_bin_1": y} tracer_names = TracerNames("shear_bin_0", "shear_bin_1") data_type = harmonic(Galaxies.SHEAR_E, Galaxies.SHEAR_E) with pytest.raises(ValueError) as exc_info: - make_two_point_xy(inferred_dict, tracer_names, data_type) + make_two_point_xy(tomographic_dict, tracer_names, data_type) error_msg = str(exc_info.value) assert "Tracer measurements do not match the SACC naming convention" in error_msg @@ -1004,24 +1020,24 @@ def test_make_two_point_xy_missing_x_measurement(): def test_make_two_point_xy_missing_y_measurement(): """Test make_two_point_xy when second tracer lacks required measurement.""" - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="shear_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="shear_bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, # Has SHEAR_T but needs SHEAR_E ) - inferred_dict = {"shear_bin_0": x, "shear_bin_1": y} + tomographic_dict: dict[str, ProjectedField] = {"shear_bin_0": x, "shear_bin_1": y} tracer_names = TracerNames("shear_bin_0", "shear_bin_1") data_type = harmonic(Galaxies.SHEAR_E, Galaxies.SHEAR_E) with pytest.raises(ValueError) as exc_info: - make_two_point_xy(inferred_dict, tracer_names, data_type) + make_two_point_xy(tomographic_dict, tracer_names, data_type) error_msg = str(exc_info.value) assert "Tracer measurements do not match the SACC naming convention" in error_msg @@ -1034,24 +1050,24 @@ def test_make_two_point_xy_missing_y_measurement(): def test_make_two_point_xy_both_measurements_missing(): """Test make_two_point_xy when both tracers lack required measurements.""" - x = InferredGalaxyZDist( + x = TomographicBin( bin_name="counts_bin_0", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, # Has COUNTS, needs SHEAR_E ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="shear_bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, # Has SHEAR_T, needs SHEAR_E ) - inferred_dict = {"counts_bin_0": x, "shear_bin_1": y} + tomographic_dict: dict[str, ProjectedField] = {"counts_bin_0": x, "shear_bin_1": y} tracer_names = TracerNames("counts_bin_0", "shear_bin_1") data_type = harmonic(Galaxies.SHEAR_E, Galaxies.SHEAR_E) with pytest.raises(ValueError) as exc_info: - make_two_point_xy(inferred_dict, tracer_names, data_type) + make_two_point_xy(tomographic_dict, tracer_names, data_type) error_msg = str(exc_info.value) assert "Tracer measurements do not match the SACC naming convention" in error_msg @@ -1062,24 +1078,23 @@ def test_make_two_point_xy_both_measurements_missing(): def test_make_two_point_xy_sacc_convention_explanation(): """Test that error message includes SACC convention explanation.""" - x = InferredGalaxyZDist( + x = CMBLensing( bin_name="cmb_bin", - z=np.array([1100.0]), - dndz=np.array([1.0]), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) - y = InferredGalaxyZDist( + y = TomographicBin( bin_name="galaxy_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, # Has SHEAR_E but needs COUNTS ) - inferred_dict = {"cmb_bin": x, "galaxy_bin": y} + tomographic_dict: dict[str, ProjectedField] = {"cmb_bin": x, "galaxy_bin": y} tracer_names = TracerNames("cmb_bin", "galaxy_bin") data_type = harmonic(CMB.CONVERGENCE, Galaxies.COUNTS) with pytest.raises(ValueError) as exc_info: - make_two_point_xy(inferred_dict, tracer_names, data_type) + make_two_point_xy(tomographic_dict, tracer_names, data_type) error_msg = str(exc_info.value) # Check for convention explanation diff --git a/tests/metadata/test_metadata_two_point_sacc.py b/tests/metadata/test_metadata_two_point_sacc.py index 17efb7c4d..1af3a4a5e 100644 --- a/tests/metadata/test_metadata_two_point_sacc.py +++ b/tests/metadata/test_metadata_two_point_sacc.py @@ -19,13 +19,14 @@ CMB, Galaxies, GALAXY_SOURCE_TYPES, - InferredGalaxyZDist, LensBinPairSelector, NamedBinPairSelector, SourceBinPairSelector, TracerNames, TwoPointHarmonic, TwoPointReal, + TomographicBin, + CMBLensing, TypeSource, ) from firecrown.metadata_types._sacc_type_string import ( @@ -1072,13 +1073,13 @@ def test_make_all_photoz_bin_combinations_with_cmb_basic(): """Test basic functionality of make_all_photoz_bin_combinations_with_cmb.""" # Create test galaxy bins galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ), - InferredGalaxyZDist( + TomographicBin( bin_name="bin_2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1114,7 +1115,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_basic(): def test_make_all_photoz_bin_combinations_with_cmb_with_auto(): """Test make_all_photoz_bin_combinations_with_cmb with CMB auto-correlation.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1148,7 +1149,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_with_auto(): def test_make_all_photoz_bin_combinations_with_cmb_custom_tracer_name(): """Test make_all_photoz_bin_combinations_with_cmb with custom CMB tracer name.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1182,13 +1183,13 @@ def test_make_all_photoz_bin_combinations_with_cmb_custom_tracer_name(): def test_make_all_photoz_bin_combinations_with_cmb_measurement_compatibility(): """Test that only compatible measurements create CMB-galaxy cross-correlations.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="counts_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, # Compatible with CMB.CONVERGENCE ), - InferredGalaxyZDist( + TomographicBin( bin_name="shear_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1241,7 +1242,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_empty_input(): def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties(): """Test that the created CMB bin has correct properties.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1258,8 +1259,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties(): cmb_bin = cmb_combo.x assert cmb_bin.bin_name == "cmb_convergence" - assert np.array_equal(cmb_bin.z, np.array([1100.0])) - assert np.array_equal(cmb_bin.dndz, np.array([1.0])) + assert isinstance(cmb_bin, CMBLensing) assert cmb_bin.measurements == {CMB.CONVERGENCE} assert cmb_bin.type_source == TypeSource.DEFAULT @@ -1268,7 +1268,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties(): def test_make_all_photoz_bin_combinations_with_cmb_parametrized(include_auto: bool): """Parametrized test for CMB auto-correlation inclusion.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1299,7 +1299,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_parametrized(include_auto: bo def test_make_all_photoz_bin_combinations_with_cmb_multiple_measurements(): """Test with galaxy bins that have multiple measurement types.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="multi_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1344,13 +1344,13 @@ def test_make_cmb_galaxy_combinations_only_basic(): """Test basic functionality of make_cmb_galaxy_combinations_only.""" # Create test galaxy bins galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ), - InferredGalaxyZDist( + TomographicBin( bin_name="bin_2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1398,7 +1398,7 @@ def test_make_cmb_galaxy_combinations_only_basic(): def test_make_cmb_galaxy_combinations_only_custom_tracer_name(): """Test make_cmb_galaxy_combinations_only with custom CMB tracer name.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1432,13 +1432,13 @@ def test_make_cmb_galaxy_combinations_only_custom_tracer_name(): def test_make_cmb_galaxy_combinations_only_measurement_compatibility(): """Test that only compatible measurements create CMB-galaxy cross-correlations.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="counts_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, # Compatible with CMB.CONVERGENCE ), - InferredGalaxyZDist( + TomographicBin( bin_name="shear_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1479,7 +1479,7 @@ def test_make_cmb_galaxy_combinations_only_incompatible_measurements_0(): # Create a galaxy bin with a measurement that might not be compatible # (This test depends on what measurements are actually incompatible) galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="test_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1503,7 +1503,7 @@ def test_make_cmb_galaxy_combinations_only_incompatible_measurements_0(): def test_make_cmb_galaxy_combinations_only_cmb_bin_properties(): """Test that the created CMB bin has correct properties.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1520,8 +1520,7 @@ def test_make_cmb_galaxy_combinations_only_cmb_bin_properties(): cmb_bin = cmb_combo.x assert cmb_bin.bin_name == "cmb_convergence" - assert np.array_equal(cmb_bin.z, np.array([1100.0])) - assert np.array_equal(cmb_bin.dndz, np.array([1.0])) + assert isinstance(cmb_bin, CMBLensing) assert cmb_bin.measurements == {CMB.CONVERGENCE} assert cmb_bin.type_source == TypeSource.DEFAULT @@ -1529,7 +1528,7 @@ def test_make_cmb_galaxy_combinations_only_cmb_bin_properties(): def test_make_cmb_galaxy_combinations_only_multiple_measurements(): """Test with galaxy bins that have multiple measurement types.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="multi_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1560,7 +1559,7 @@ def test_make_cmb_galaxy_combinations_only_multiple_measurements(): def test_make_cmb_galaxy_combinations_only_symmetric_pairs(): """Test that symmetric pairs are created for each compatible measurement.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="test_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1592,7 +1591,7 @@ def test_make_cmb_galaxy_combinations_only_symmetric_pairs(): def test_make_cmb_galaxy_combinations_only_single_galaxy_bin(): """Test with a single galaxy bin.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="single_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1616,7 +1615,7 @@ def test_make_cmb_galaxy_combinations_only_single_galaxy_bin(): def test_make_cmb_galaxy_combinations_only_parametrized_names(cmb_name: str): """Parametrized test for different CMB tracer names.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1641,13 +1640,13 @@ def test_make_cmb_galaxy_combinations_only_vs_with_cmb(): make_all_photoz_bin_combinations_with_cmb without galaxy combinations.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="bin_1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, ), - InferredGalaxyZDist( + TomographicBin( bin_name="bin_2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1692,13 +1691,13 @@ def test_make_cmb_galaxy_combinations_only_vs_with_cmb(): def test_make_all_photoz_bin_combinations_with_cmb_incompatible_measurements(): """Test that incompatible measurements are skipped in CMB-galaxy combinations.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.COUNTS}, # Compatible with CMB.CONVERGENCE ), - InferredGalaxyZDist( + TomographicBin( bin_name="another_compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1745,13 +1744,13 @@ def test_make_all_photoz_bin_combinations_with_cmb_incompatible_measurements(): def test_make_cmb_galaxy_combinations_only_incompatible_measurements(): """Test that incompatible measurements are skipped in CMB-galaxy combinations.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_E}, # Compatible with CMB.CONVERGENCE ), - InferredGalaxyZDist( + TomographicBin( bin_name="another_compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1786,13 +1785,13 @@ def test_make_all_photoz_bin_combinations_with_cmb_all_incompatible(): # Since we can't find truly incompatible measurements, let's test with # measurements that we know ARE compatible and adjust expectations galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin1", z=np.linspace(0, 1, 100), dndz=np.ones(100), measurements={Galaxies.SHEAR_T}, # Actually compatible with CMB.CONVERGENCE ), - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin2", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1826,7 +1825,7 @@ def test_make_cmb_galaxy_combinations_only_all_incompatible(): # Since we can't find truly incompatible measurements, let's test with # measurements that we know ARE compatible and adjust expectations galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), @@ -1843,7 +1842,7 @@ def test_make_cmb_galaxy_combinations_only_all_incompatible(): def test_make_all_photoz_bin_combinations_with_cmb_empty(): """Test behavior when given an empty list of galaxy bins.""" galaxy_bins = [ - InferredGalaxyZDist( + TomographicBin( bin_name="compatible_bin", z=np.linspace(0, 1, 100), dndz=np.ones(100), diff --git a/tests/test_ccl_factory_deprecated.py b/tests/test_ccl_factory_deprecated.py index ca9f8647c..9d2dd5b03 100644 --- a/tests/test_ccl_factory_deprecated.py +++ b/tests/test_ccl_factory_deprecated.py @@ -30,6 +30,8 @@ def test_ccl_factory_deprecation_warning(): # pylint: disable=import-outside-toplevel,unused-import import firecrown.ccl_factory # noqa: F401 + assert firecrown.ccl_factory is not None # Verify import succeeded + def test_ccl_factory_deprecation_warning_content(): """Test the deprecation warning mentions modeling_tools. @@ -40,6 +42,8 @@ def test_ccl_factory_deprecation_warning_content(): # pylint: disable=import-outside-toplevel,unused-import import firecrown.ccl_factory # noqa: F401 + assert firecrown.ccl_factory is not None # Verify import succeeded + # If we get here without error, the import worked @@ -73,6 +77,9 @@ def test_all_items_importable(): # pylint: disable=too-many-locals +@pytest.mark.filterwarnings( + r"ignore:firecrown\.ccl_factory is deprecated.*:DeprecationWarning" +) def test_items_identical_to_new_location(): """Test that imported items are the same objects as in modeling_tools.""" # pylint: disable=import-outside-toplevel diff --git a/tests/test_cmb_factories.py b/tests/test_cmb_factories.py index 0c7306d99..ec271662c 100644 --- a/tests/test_cmb_factories.py +++ b/tests/test_cmb_factories.py @@ -2,7 +2,6 @@ from unittest import mock import pytest -import numpy as np import sacc @@ -11,7 +10,7 @@ CMBConvergenceFactory, CMBConvergenceArgs, ) -from firecrown.metadata_types import InferredGalaxyZDist, CMB +from firecrown.metadata_types import CMB, CMBLensing from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ParamsMap @@ -136,11 +135,10 @@ def test_cmb_convergence_factory_create(): """Test CMBConvergenceFactory create method.""" factory = CMBConvergenceFactory(z_source=1090.0, scale=1.5) - # Create a mock InferredGalaxyZDist with CMB measurements - mock_zdist = InferredGalaxyZDist( + # Create a mock TomographicBin with CMB measurements + mock_zdist = CMBLensing( bin_name="cmb_bin", - z=np.linspace(0, 2, 100), - dndz=np.ones(100), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) @@ -155,10 +153,9 @@ def test_cmb_convergence_factory_create_caching(): """Test that CMBConvergenceFactory caches created objects.""" factory = CMBConvergenceFactory() - mock_zdist = InferredGalaxyZDist( + mock_zdist = CMBLensing( bin_name="cmb_bin", - z=np.linspace(0, 2, 100), - dndz=np.ones(100), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) @@ -223,10 +220,9 @@ def test_cmb_convergence_factory_different_params(): factory1 = CMBConvergenceFactory(z_source=1090.0, scale=1.0) factory2 = CMBConvergenceFactory(z_source=1100.0, scale=2.0) - mock_zdist = InferredGalaxyZDist( + mock_zdist = CMBLensing( bin_name="cmb_bin", - z=np.linspace(0, 2, 100), - dndz=np.ones(100), + z_lss=1100.0, measurements={CMB.CONVERGENCE}, ) diff --git a/tests/test_number_counts_source.py b/tests/test_number_counts_source.py index 0b51f9637..f85c2257c 100644 --- a/tests/test_number_counts_source.py +++ b/tests/test_number_counts_source.py @@ -3,15 +3,15 @@ import firecrown.likelihood.number_counts as nc import firecrown.metadata_types as mt import firecrown.modeling_tools as mtools -from firecrown import parameters +from firecrown import updatable def test_get_derived_parameters( - harmonic_bin_1: mt.InferredGalaxyZDist, + harmonic_bin_1: mt.TomographicBin, tools_with_vanilla_cosmology: mtools.ModelingTools, ): ncs = nc.NumberCounts.create_ready(harmonic_bin_1, derived_scale=True) - ncs.update(parameters.ParamsMap({"bin_1_bias": 1.0})) + ncs.update(updatable.ParamsMap({"bin_1_bias": 1.0})) ncs.create_tracers(tools_with_vanilla_cosmology) params = ncs.get_derived_parameters() assert params is not None diff --git a/tests/test_parameters_deprecated.py b/tests/test_parameters_deprecated.py index 7b1d1159c..54f18ab8e 100644 --- a/tests/test_parameters_deprecated.py +++ b/tests/test_parameters_deprecated.py @@ -7,6 +7,7 @@ import sys import warnings +import pytest def test_parameters_module_emits_deprecation_warning(): @@ -22,6 +23,7 @@ def test_parameters_module_emits_deprecation_warning(): # pylint: disable=unused-import,import-outside-toplevel import firecrown.parameters # noqa: F401 + assert firecrown.parameters is not None # Verify import succeeded # Should have at least one warning (there might be multiple due to # imports within the module) assert len(w) >= 1 @@ -89,6 +91,9 @@ def test_params_map_from_deprecated_module(): assert params.get("b") == 2.0 +@pytest.mark.filterwarnings( + r"ignore:.*firecrown\.parameters module is deprecated.*:DeprecationWarning" +) def test_required_parameters_from_deprecated_module(): """Ensure RequiredParameters imported from deprecated module works correctly.""" # pylint: disable=import-outside-toplevel diff --git a/tests/test_updatable.py b/tests/test_updatable.py index 5cf538756..fb71b4d28 100644 --- a/tests/test_updatable.py +++ b/tests/test_updatable.py @@ -18,7 +18,7 @@ DerivedParameterCollection, SamplerParameter, ) -from firecrown import parameters +from firecrown import updatable class MinimalUpdatable(Updatable): @@ -27,7 +27,7 @@ class MinimalUpdatable(Updatable): def __init__(self, prefix: str | None = None): """Initialize object with defaulted value.""" super().__init__(prefix) - self.a = parameters.register_new_updatable_parameter(default_value=1.0) + self.a = updatable.register_new_updatable_parameter(default_value=1.0) class SimpleUpdatable(Updatable): # pylint: disable=too-many-instance-attributes @@ -37,8 +37,8 @@ def __init__(self, prefix: str | None = None): """Initialize object with defaulted values.""" super().__init__(prefix) - self.x = parameters.register_new_updatable_parameter(default_value=2.0) - self.y = parameters.register_new_updatable_parameter(default_value=3.0) + self.x = updatable.register_new_updatable_parameter(default_value=2.0) + self.y = updatable.register_new_updatable_parameter(default_value=3.0) class UpdatableWithDerived(Updatable): @@ -48,8 +48,8 @@ def __init__(self): """Initialize object with defaulted values.""" super().__init__() - self.A = parameters.register_new_updatable_parameter(default_value=2.0) - self.B = parameters.register_new_updatable_parameter(default_value=1.0) + self.A = updatable.register_new_updatable_parameter(default_value=2.0) + self.B = updatable.register_new_updatable_parameter(default_value=1.0) def _get_derived_parameters(self) -> DerivedParameterCollection: derived_scale = DerivedParameter("Section", "Name", self.A + self.B) @@ -98,11 +98,11 @@ def test_updatable_record_with_internal_params(): obj = SimpleUpdatable("test") obj.set_internal_parameter( "internal1", - parameters.register_new_updatable_parameter(value=1.0, default_value=1.0), + updatable.register_new_updatable_parameter(value=1.0, default_value=1.0), ) obj.set_internal_parameter( "internal2", - parameters.register_new_updatable_parameter(value=2.0, default_value=2.0), + updatable.register_new_updatable_parameter(value=2.0, default_value=2.0), ) params = ParamsMap({"test_x": 1.0, "test_y": 2.0}) @@ -336,7 +336,7 @@ def test_updatable_collection_insertion(): def test_set_sampler_parameter(): my_updatable = MinimalUpdatable() - my_param = parameters.register_new_updatable_parameter(default_value=42.0) + my_param = updatable.register_new_updatable_parameter(default_value=42.0) my_param.set_fullname(prefix=None, name="the_meaning_of_life") my_updatable.set_sampler_parameter(my_param) @@ -346,7 +346,7 @@ def test_set_sampler_parameter(): def test_set_sampler_parameter_rejects_internal_parameter(): my_updatable = MinimalUpdatable() - my_param = parameters.register_new_updatable_parameter( + my_param = updatable.register_new_updatable_parameter( value=42.0, default_value=41.0 ) @@ -356,9 +356,9 @@ def test_set_sampler_parameter_rejects_internal_parameter(): def test_set_sampler_parameter_rejects_duplicates(): my_updatable = MinimalUpdatable() - my_param = parameters.register_new_updatable_parameter(default_value=42.0) + my_param = updatable.register_new_updatable_parameter(default_value=42.0) my_param.set_fullname(prefix=None, name="the_meaning_of_life") - my_param_same = parameters.register_new_updatable_parameter(default_value=42.0) + my_param_same = updatable.register_new_updatable_parameter(default_value=42.0) my_param_same.set_fullname(prefix=None, name="the_meaning_of_life") my_updatable.set_sampler_parameter(my_param) @@ -371,7 +371,7 @@ def test_set_internal_parameter(): my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", - parameters.register_new_updatable_parameter(value=1.0, default_value=42.0), + updatable.register_new_updatable_parameter(value=1.0, default_value=42.0), ) assert hasattr(my_updatable, "the_meaning_of_life") @@ -380,7 +380,7 @@ def test_set_internal_parameter(): def test_set_parameter_using_internal_parameter(): my_updatable = MinimalUpdatable() - ip = parameters.InternalParameter(2112) + ip = updatable.InternalParameter(2112) my_updatable.set_parameter("epic_Rush_album", ip) assert hasattr(my_updatable, "epic_Rush_album") @@ -392,7 +392,7 @@ def test_set_internal_parameter_rejects_sampler_parameter(): with pytest.raises(TypeError): my_updatable.set_internal_parameter( "sampler_param", - parameters.register_new_updatable_parameter(default_value=1.0), + updatable.register_new_updatable_parameter(default_value=1.0), ) @@ -400,13 +400,13 @@ def test_set_internal_parameter_rejects_duplicates(): my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", - parameters.register_new_updatable_parameter(value=1.0, default_value=42.0), + updatable.register_new_updatable_parameter(value=1.0, default_value=42.0), ) with pytest.raises(ValueError): my_updatable.set_internal_parameter( "the_meaning_of_life", - parameters.register_new_updatable_parameter(value=1.0, default_value=42.0), + updatable.register_new_updatable_parameter(value=1.0, default_value=42.0), ) @@ -414,11 +414,11 @@ def test_set_parameter(): my_updatable = MinimalUpdatable() my_updatable.set_parameter( "the_meaning_of_life", - parameters.register_new_updatable_parameter(value=1.0, default_value=42.0), + updatable.register_new_updatable_parameter(value=1.0, default_value=42.0), ) my_updatable.set_parameter( "no_meaning_of_life", - parameters.register_new_updatable_parameter(default_value=42.0), + updatable.register_new_updatable_parameter(default_value=42.0), ) assert hasattr(my_updatable, "the_meaning_of_life") @@ -432,7 +432,7 @@ def test_update_rejects_internal_parameters(): my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", - parameters.register_new_updatable_parameter(value=2.0, default_value=42.0), + updatable.register_new_updatable_parameter(value=2.0, default_value=42.0), ) assert hasattr(my_updatable, "the_meaning_of_life") @@ -528,8 +528,8 @@ def test_nesting_updatables_missing_parameters(nested_updatables): base.update(params) - for updatable in nested_updatables: - assert updatable.is_updated() + for my_updatable in nested_updatables: + assert my_updatable.is_updated() def test_nesting_updatables_required_parameters(nested_updatables): diff --git a/tutorial/_quarto.yml b/tutorial/_quarto.yml index fa3ceccc7..5d14cc387 100644 --- a/tutorial/_quarto.yml +++ b/tutorial/_quarto.yml @@ -7,7 +7,7 @@ project: - development_example.qmd - intro_article.qmd - two_point_framework.qmd - - inferred_zdist.qmd + - tomographic_bin.qmd - inferred_zdist_generators.qmd - inferred_zdist_serialization.qmd - two_point_generators.qmd @@ -40,12 +40,12 @@ website: contents: - section: "Tomographic Bins" contents: - - href: inferred_zdist.qmd - text: "Distributions" + - href: tomographic_bin.qmd + text: "Inferred Redshift Distributions" - href: inferred_zdist_generators.qmd - text: "Distribution Generators" + text: "Redshift Distribution Generators" - href: inferred_zdist_serialization.qmd - text: "Serialization" + text: "Redshift Distribution Serialization" - section: "Two-Point" contents: - href: two_point_framework.qmd diff --git a/tutorial/inferred_zdist_generators.qmd b/tutorial/inferred_zdist_generators.qmd index a487d3a25..c47b3e457 100644 --- a/tutorial/inferred_zdist_generators.qmd +++ b/tutorial/inferred_zdist_generators.qmd @@ -8,7 +8,7 @@ format: html ## Purpose of this document -In the previous [tutorial](inferred_zdist.qmd), we explored how to use `InferredGalaxyZDist` objects to represent redshift distributions for galaxies. +In the previous [tutorial](tomographic_bin.qmd), we explored how to use `TomographicBin` objects to represent redshift distributions for galaxies. These distributions are necessary for modeling galaxy redshift distributions and can be created using various methods. In this tutorial, we will focus on generating these distributions using the `ZDistLSSTSRD` class in Firecrown. This class generates redshift distributions in accordance with the LSST Science Requirements Document (SRD). @@ -19,7 +19,7 @@ P(z|B_i, \theta) \equiv \frac{\mathrm{d}n}{\mathrm{d}z}(z;B_i, \theta), $${#eq-Pi} where $B_i$ is the $i$-th bin of the photometric redshifts, and $\theta$ is a set of parameters that describe the model. -In Firecrown this distribution is represented as an object of type `InferredGalaxyZDist`[^1]. +In Firecrown this distribution is represented as an object of type `TomographicBin`[^1]. An object of this type contains the redshifts, the corresponding probabilities, and the data-type of the measurements. Firecrown also provides facilities to generate these distributions, given a chosen model. In `firecrown.generators`, we have the `ZDistLSSTSRD` class, which can be used to generate redshift distributions according to the LSST SRD. @@ -132,7 +132,7 @@ d_y_all = pd.concat( ) ``` -Next, using the same SRD prescriptions, we want to generate the `InferredGalaxyZDist` objects representing @eq-Pi for a specific binning, and using a specific resolution parameter $\sigma_z$. +Next, using the same SRD prescriptions, we want to generate the `TomographicBin` objects representing @eq-Pi for a specific binning, and using a specific resolution parameter $\sigma_z$. Here we show the first bin for the lens and source samples for both Year 1 and Year 10, the functions are evaluated at $100$ points equally spaced between $0$ and $0.6$.[^3] [^3]: Note that we are making use of several module-level constants in the `firecrown.generators` module, namely `Y1_LENS_BINS`, `Y10_LENS_BINS`, `Y1_SOURCE_BINS`, and `Y10_SOURCE_BINS`, which are dictionaries that contain the bin edges and the resolution parameter $\sigma_z$ for the bins. @@ -140,7 +140,7 @@ Here we show the first bin for the lens and source samples for both Year 1 and Y ```{python} import numpy as np import firecrown -from firecrown.metadata_types import InferredGalaxyZDist +from firecrown.metadata_types import TomographicBin from firecrown.generators import ( ZDistLSSTSRD, Y1_LENS_BINS, @@ -155,7 +155,7 @@ z = np.linspace(0.0, 0.6, 100) # We use the same zdist_y1_* and zdist_y10_* that were created above. -# We create two InferredGalaxyZDist objects, one for Y1 and one for Y10. +# We create two TomographicBin objects, one for Y1 and one for Y10. Pz_lens0_y1 = zdist_y1_lens.binned_distribution( zpl=Y1_LENS_BINS["edges"][0], zpu=Y1_LENS_BINS["edges"][1], @@ -190,13 +190,13 @@ Pz_source0_y10 = zdist_y10_source.binned_distribution( ) # Next we check that the objects we created are of the expected type. -assert isinstance(Pz_lens0_y1, InferredGalaxyZDist) -assert isinstance(Pz_lens0_y10, InferredGalaxyZDist) -assert isinstance(Pz_source0_y1, InferredGalaxyZDist) -assert isinstance(Pz_source0_y10, InferredGalaxyZDist) +assert isinstance(Pz_lens0_y1, TomographicBin) +assert isinstance(Pz_lens0_y10, TomographicBin) +assert isinstance(Pz_source0_y1, TomographicBin) +assert isinstance(Pz_source0_y10, TomographicBin) ``` -The plot of the $\textrm{d}N/\textrm{d}z$ distributions in these `InferredGalaxyZDist` objects is shown in @fig-inferred-dist. +The plot of the $\textrm{d}N/\textrm{d}z$ distributions in these `TomographicBin` objects is shown in @fig-inferred-dist. ```{python} # | label: fig-inferred-dist @@ -257,7 +257,7 @@ The following code block demonstrates how to do this. from itertools import pairwise # We use the same zdist_y1 and zdist_y10 that were created above. -# We create two InferredGalaxyZDist objects, one for Y1 and one for Y10. +# We create two TomographicBin objects, one for Y1 and one for Y10. z = np.linspace(0.0, 3.5, 1000) all_y1_bins = [ zdist_y1_lens.binned_distribution( @@ -282,7 +282,7 @@ all_y1_bins = [ ] ``` -The plot of the $\textrm{d}N/\textrm{d}z$ distributions in these `InferredGalaxyZDist` objects is shown in @fig-inferred-dist-all. +The plot of the $\textrm{d}N/\textrm{d}z$ distributions in these `TomographicBin` objects is shown in @fig-inferred-dist-all. ```{python} # | label: fig-inferred-dist-all @@ -433,5 +433,5 @@ percent_diff = 100.0 * (N_gal - N_gal_phot) / N_gal ## End note Note that these facilities for creating redshift distributions are not limitations on the use of the distributions. -Any facility that can create the $z$ and $\textrm{d}N/\textrm{d}z$ arrays can be used to create an `InferredGalaxyZDist` object. -Code that uses the `InferredGalaxyZDist` objects does not depend on how they were created. +Any facility that can create the $z$ and $\textrm{d}N/\textrm{d}z$ arrays can be used to create an `TomographicBin` object. +Code that uses the `TomographicBin` objects does not depend on how they were created. diff --git a/tutorial/inferred_zdist_serialization.qmd b/tutorial/inferred_zdist_serialization.qmd index bcf09fed1..ab9811f54 100644 --- a/tutorial/inferred_zdist_serialization.qmd +++ b/tutorial/inferred_zdist_serialization.qmd @@ -10,7 +10,7 @@ format: html In the previous [tutorial](inferred_zdist_generators.qmd), we discussed using the `ZDistLSSTSRD` class in Firecrown to generate redshift distributions for galaxies. In this tutorial, we will cover how to serialize these distributions to disk and read them back in. -While `InferredGalaxyZDist` objects can be serialized, the resulting files are not human-readable because they contain the final redshift distribution. +While `TomographicBin` objects can be serialized, the resulting files are not human-readable because they contain the final redshift distribution. To achieve a human-readable format, we need to serialize the parameters used to generate the distribution. Firecrown addresses this by introducing the `ZDistLSSTSRDBin` and `ZDistLSSTSRDBinCollection` dataclasses.[^1] @@ -116,7 +116,7 @@ assert bin_collection.bins[0].measurements == bin_collection_read.bins[0].measur assert bin_collection.bins[0].z == bin_collection_read.bins[0].z ``` -Calling `generate()` on the read object will generate the `InferredGalaxyZDist` redshift distribution dataclasses. +Calling `generate()` on the read object will generate the redshift distribution dataclasses `TomographicBin`. ```{python} from pprint import pprint diff --git a/tutorial/inferred_zdist.qmd b/tutorial/tomographic_bin.qmd similarity index 88% rename from tutorial/inferred_zdist.qmd rename to tutorial/tomographic_bin.qmd index 410ec29bd..27d4dd5b2 100644 --- a/tutorial/inferred_zdist.qmd +++ b/tutorial/tomographic_bin.qmd @@ -1,5 +1,5 @@ --- -title: "Using Firecrown InferredGalaxyZDist objects" +title: "Using Firecrown TomographicBin objects" subtitle: "Version {{< env FIRECROWN_VERSION >}}" format: html --- @@ -13,24 +13,24 @@ The galaxy redshift distributions play a crucial role in modeling the distributi ## Overview -Firecrown employs `InferredGalaxyZDist` objects to encapsulate these distributions. +Firecrown employs `TomographicBin` objects to encapsulate these distributions. These objects are essential for representing the redshift distribution of galaxies within predefined photometric redshift bins. Additionally, Firecrown utilizes metadata dataclasses to handle metadata and calibration data pertinent to cosmological analyses. These dataclasses can be constructed directly, extracted from SACC objects, or generated through Firecrown's provided functionalities. -This document outlines the process of creating `InferredGalaxyZDist` objects directly and demonstrates their utilization in cosmological analyses. +This document outlines the process of creating `TomographicBin` objects directly and demonstrates their utilization in cosmological analyses. -## Creating an `InferredGalaxyZDist` Object +## Creating an `TomographicBin` Object -The `InferredGalaxyZDist` object serves as the cornerstone for representing the redshift distribution of galaxies within a designated photometric redshift bin. Each bin is identified by a string identifier, `bin_name`, utilized by theoretical models to specify parameters specific to the corresponding redshift bin. +The `TomographicBin` object serves as the cornerstone for representing the redshift distribution of galaxies within a designated photometric redshift bin. Each bin is identified by a string identifier, `bin_name`, utilized by theoretical models to specify parameters specific to the corresponding redshift bin. -The `InferredGalaxyZDist` object can be created by providing the following parameters: +The `TomographicBin` object can be created by providing the following parameters: ```{python} -from firecrown.metadata_types import Galaxies, InferredGalaxyZDist +from firecrown.metadata_types import Galaxies, TomographicBin import numpy as np z = np.linspace(0.0, 1.0, 200) -lens0 = InferredGalaxyZDist( +lens0 = TomographicBin( bin_name="lens0", z=z, dndz=np.exp(-0.5 * ((z - 0.5) / 0.02) ** 2) / np.sqrt(2 * np.pi) / 0.02, @@ -183,7 +183,7 @@ data = pd.DataFrame( ## Conclusion This document has provided an overview of Firecrown's capabilities in representing galaxy redshift distributions and utilizing them in cosmological analyses. -The `InferredGalaxyZDist` object encapsulates the redshift distribution of galaxies within predefined photometric redshift bins. +The `TomographicBin` object encapsulates the redshift distribution of galaxies within predefined photometric redshift bins. The `TwoPointHarmonic` object is used to encapsulate the two-point correlation function in harmonic space, while the `TwoPoint` object encapsulates the theoretical model for the two-point correlation function. These objects are essential for modeling the distribution of galaxies within specific photometric redshift bins and are crucial for cosmological analyses. diff --git a/tutorial/two_point_framework.qmd b/tutorial/two_point_framework.qmd index 857e01adc..81ee01ce7 100644 --- a/tutorial/two_point_framework.qmd +++ b/tutorial/two_point_framework.qmd @@ -18,7 +18,7 @@ If you are looking for practical instructions on how to use the system, you may ## Introduction Firecrown’s two-point likelihood framework is built around a structured hierarchy of metadata types that describe the configuration and organization of cosmological measurements. -At the foundation are basic bin descriptors, such as [[metadata_types|InferredGalaxyZDist]], which define properties of individual observables or selections. +At the foundation are basic bin descriptors, such as [[metadata_types|TomographicBin]], which define properties of individual observables or selections. These descriptors are not directly tied to specific measurements but are shared across multiple components in a complex analysis. This allows Firecrown to ensure that components that should have identical descriptors have exactly identical descriptors, by construction. @@ -47,7 +47,7 @@ The complete hierarchy is visualized in @fig-framework-hierarchy. %%| fig-cap-location: margin %%| fig-cap: "Hierarchy of types in the Firecrown two-point statistic framework." graph TD - A["Basic Bin Description
e.g. InferredGalaxyZDist"] + A["Basic Bin Description
e.g. TomographicBin"] A --> B["TwoPointXY
(bin combination and
measured types)"] B --> C1["TwoPointHarmonic
(ell layout)"] B --> C2["TwoPointReal
(theta layout)"] @@ -65,8 +65,8 @@ graph TD The foundation of Firecrown’s two-point framework begins with a description of individual bins, represented as dataclasses. These classes encapsulate the metadata required to define a bin used in cosmological measurements. -A key example is the [[metadata_types|InferredGalaxyZDist]] class, which describes the inferred redshift distribution of a single bin. -Each bin is labeled using a [[metadata_types|InferredGalaxyZDist.bin_name]] and includes arrays [[metadata_types|InferredGalaxyZDist.z]] and [[metadata_types|InferredGalaxyZDist.dndz]] representing the redshift and corresponding distribution. +A key example is the [[metadata_types|TomographicBin]] class, which describes the inferred redshift distribution of a single bin. +Each bin is labeled using a [[metadata_types|TomographicBin.bin_name]] and includes arrays [[metadata_types|TomographicBin.z]] and [[metadata_types|TomographicBin.dndz]] representing the redshift and corresponding distribution. Additionally, it contains a set of [[metadata_types|Measurement]] types (e.g., [[metadata_types|Galaxies.COUNTS]], [[metadata_types|Galaxies.SHEAR_T]], [[metadata_types|CMB.CONVERGENCE]])[^enumerations] indicating which observables were measured in this bin. It is valid to include multiple measurement types for the same bin, such as galaxy number counts and shear measured on the same galaxy subsample. @@ -77,10 +77,10 @@ It is valid to include multiple measurement types for the same bin, such as gala Such additions are made in `firecrown/metadata_types.py`. Firecrown's use of type checking should ensure that any code that would be invalidated by adding a new type, or value, is identified. -To further differentiate among subpopulations, each bin may be tagged with a [[metadata_types|InferredGalaxyZDist.type_source]]. +To further differentiate among subpopulations, each bin may be tagged with a [[metadata_types|TomographicBin.type_source]]. This identifier, while typically a simple string[^type_source], distinguishes between subsets within the same measurement category. -For example, in galaxy surveys, [[metadata_types|InferredGalaxyZDist.type_source]] might refer to red vs. blue galaxies, or in CMB lensing, to Planck vs. SPT data. -This tag allows the modeling framework to apply different theoretical treatments or nuisance parametrization to distinct subcomponents while keeping the overall bin structure unified. +For example, in galaxy surveys, [[metadata_types|TomographicBin.type_source]] might refer to red vs. blue galaxies, or in CMB lensing, to Planck vs. SPT data. +This tag allows the modeling framework to apply different theoretical treatments or nuisance parameterizations to distinct subcomponents while keeping the overall bin structure unified. [^type_source]: More precisely, `type_source` is actually of type `TypeSource`, which is a subclass of `str`. This type has some additional functionality used by the metadata system for validation, and which is usually transparent to users of Firecrown. @@ -91,21 +91,21 @@ The implementation is as follows: #| echo: false #| output: asis from firecrown.fctools.print_code import display_class_attributes -from firecrown.metadata_types import InferredGalaxyZDist -display_class_attributes(InferredGalaxyZDist) +from firecrown.metadata_types import TomographicBin +display_class_attributes(TomographicBin) ``` Subclassing [[utils|YAMLSerializable]] enables these objects to be saved to and loaded from YAML files, facilitating configuration and reproducibility. -The dataclass is *frozen*, indicating that `InferredGalaxyZDist` objects cannot be modified after creation. -Modifying an `InferredGalaxyZDist` object that may be shared between multiple `TwoPoint` objects could lead to unexpected behavior. +The dataclass is *frozen*, indicating that `TomographicBin` objects cannot be modified after creation. +Modifying an `TomographicBin` object that may be shared between multiple `TwoPoint` objects could lead to unexpected behavior. It is also marked with *kw_only*, ensuring that only keyword arguments are accepted during construction, thus ensuring that use of the dataclass is mostly self-documenting. In other tutorials, we explore the following topics: -1. **[Inferred Redshift Distributions](inferred_zdist.qmd)**: A guide to utilizing Firecrown's capabilities to describe galaxy redshift distributions for cosmological analyses. +1. **[Inferred Redshift Distributions](tomographic_bin.qmd)**: A guide to utilizing Firecrown's capabilities to describe galaxy redshift distributions for cosmological analyses. -2. **[Redshift Distribution Generators](inferred_zdist_generators.qmd)**: Demonstrates how to generate [[metadata_types|InferredGalaxyZDist]] objects from LSST SRD redshift distributions. -3. **[Redshift Distribution Serialization](inferred_zdist_serialization.qmd)**: Explains how to serialize and deserialize [[metadata_types|InferredGalaxyZDist]] objects and use the [[generators|ZDistLSSTSRDBin]] and [[generators|ZDistLSSTSRDBinCollection]] generators dataclasses. +2. **[Redshift Distribution Generators](inferred_zdist_generators.qmd)**: Demonstrates how to generate [[metadata_types|TomographicBin]] objects from LSST SRD redshift distributions. +3. **[Redshift Distribution Serialization](inferred_zdist_serialization.qmd)**: Explains how to serialize and deserialize [[metadata_types|TomographicBin]] objects and use the [[generators|ZDistLSSTSRDBin]] and [[generators|ZDistLSSTSRDBinCollection]] generators dataclasses. # Bin Combinations: [[metadata_types|TwoPointXY]] @@ -120,11 +120,11 @@ In practice, the same pair of bins may appear in multiple combinations, each cor ```{python} #| eval: false -bin1 = InferredGalaxyZDist( +bin1 = TomographicBin( z=..., dndz=..., measurements={Galaxies.COUNTS, Galaxies.SHEAR_E} ) -bin2 = InferredGalaxyZDist( +bin2 = TomographicBin( z=..., dndz=..., measurements={Galaxies.COUNTS, Galaxies.SHEAR_E} ) @@ -191,7 +191,7 @@ It also enables use cases such as theoretical predictions or forecasts where dat # Two-Point Data Container: [[data_types|TwoPointMeasurement]] -The previous types ([[metadata_types|InferredGalaxyZDist]], [[metadata_types|TwoPointXY]], [[metadata_types|TwoPointHarmonic]], [[metadata_types|TwoPointReal]]) define the structure and layout of a two-point measurement, what is being measured and how, but do not include actual data. +The previous types ([[metadata_types|TomographicBin]], [[metadata_types|TwoPointXY]], [[metadata_types|TwoPointHarmonic]], [[metadata_types|TwoPointReal]]) define the structure and layout of a two-point measurement, what is being measured and how, but do not include actual data. These layout descriptions can be used independently of any measurement data, for example, in computing theoretical predictions, plotting expectations, or running forecasts. To represent an actual measurement, either from observational data or a simulated dataset, the framework introduces the [[data_types|TwoPointMeasurement]] class. @@ -221,11 +221,11 @@ This framework provides a structured approach for managing two-point correlation It separates concerns into different components, each with a specific role, and supports both theoretical predictions and actual observational data. All dataclasses below are frozen for immutability and support YAML serialization. -1. **[[metadata_types|InferredGalaxyZDist]]**: +1. **[[metadata_types|TomographicBin]]**: - Represents the metadata for a particular bin, which includes redshift distributions, measurement types (e.g., galaxy counts, shear), and a [[metadata_types|TypeSource]] to distinguish subpopulations (e.g., red or blue galaxies). 2. **[[metadata_types|TwoPointXY]]**: - - Combines two [[metadata_types|InferredGalaxyZDist]] bins and specifies which measurements (e.g., galaxy counts, shear) are being cross-correlated. + - Combines two [[metadata_types|TomographicBin]] bins and specifies which measurements (e.g., galaxy counts, shear) are being cross-correlated. - This object encapsulates the logic for validating that the selected measurements match the bins involved, avoiding duplication of bin metadata. 3. **[[metadata_types|TwoPointHarmonic]] and [[metadata_types|TwoPointReal]]**: diff --git a/tutorial/two_point_generators.qmd b/tutorial/two_point_generators.qmd index 21d359099..96194b059 100644 --- a/tutorial/two_point_generators.qmd +++ b/tutorial/two_point_generators.qmd @@ -10,7 +10,7 @@ format: html In the tutorial [redshift distributions](inferred_zdist_generators.qmd) we illustrate the process of using Firecrown to create redshift distributions based on specified parameters describing the distributions. Additionally, in [serializable redshift distributions](inferred_zdist_serialization.qmd) we demonstrate how to generate redshift distributions using serializable objects. -This document outlines how to use `InferredGalaxyZDist` objects to derive the two-point statistics pertinent to the Large Synoptic Survey Telescope (LSST), employing the redshift distribution outlined in the LSST Science Requirements Document (SRD). +This document outlines how to use `TomographicBin` objects to derive the two-point statistics pertinent to the Large Synoptic Survey Telescope (LSST), employing the redshift distribution outlined in the LSST Science Requirements Document (SRD). This tutorial focuses on generating two-point statistics from scratch using metadata. For loading existing data from SACC files and applying scale cuts, see: - [Two-Point Factory Basics](two_point_factory_basics.qmd)