Source code for piblin_jax.data.collections.consistent_measurement_set

"""
ConsistentMeasurementSet class for piblin-jax.

MeasurementSet variant where all measurements have the same dataset structure.
"""

from typing import Any

from .measurement import Measurement
from .measurement_set import MeasurementSet


[docs] class ConsistentMeasurementSet(MeasurementSet): """ MeasurementSet where all measurements have the same dataset structure. This specialized variant enforces that all measurements contain datasets of the same types in the same order. This is useful for: - Replicate measurements (same protocol, multiple runs) - Time series measurements (same observables at different times) - Consistent multi-channel measurements The structural consistency enables array-based operations and easier data aggregation. Parameters ---------- measurements : list[Measurement] List of Measurement objects. All must have the same structure. conditions : dict[str, Any] | None, optional Experimental conditions for the measurement series. details : dict[str, Any] | None, optional Additional context for the measurement series. Raises ------ ValueError If measurements do not all have the same dataset structure. Notes ----- Structure is defined as the sequence of dataset types. For example: - [OneDimensionalDataset, OneDimensionalDataset] is consistent with itself - [OneDimensionalDataset] is NOT consistent with [ZeroDimensionalDataset] - [OneDimensionalDataset, ZeroDimensionalDataset] is NOT consistent with [ZeroDimensionalDataset, OneDimensionalDataset] (order matters) Examples -------- >>> import numpy as np >>> from piblin_jax.data.datasets import OneDimensionalDataset >>> from piblin_jax.data.collections import Measurement, ConsistentMeasurementSet >>> >>> # Create replicate measurements with consistent structure >>> x = np.linspace(0, 10, 100) >>> measurements = [] >>> for i in range(5): ... y = np.sin(x) + np.random.normal(0, 0.1, len(x)) ... ds = OneDimensionalDataset(x, y) ... m = Measurement([ds], conditions={"replicate": i+1}) ... measurements.append(m) >>> >>> # Create consistent measurement set >>> cms = ConsistentMeasurementSet( ... measurements=measurements, ... conditions={"sample": "S1", "experiment": "replicates"} ... ) >>> >>> len(cms) 5 >>> >>> # All measurements have the same structure >>> for m in cms: ... print(len(m.datasets), type(m.datasets[0]).__name__) 1 OneDimensionalDataset 1 OneDimensionalDataset 1 OneDimensionalDataset 1 OneDimensionalDataset 1 OneDimensionalDataset >>> >>> # This will raise ValueError - inconsistent structures >>> from piblin_jax.data.datasets import ZeroDimensionalDataset >>> m1 = Measurement([OneDimensionalDataset(np.array([1, 2]), np.array([3, 4]))]) >>> m2 = Measurement([ZeroDimensionalDataset(5.0)]) >>> ConsistentMeasurementSet([m1, m2]) # doctest: +SKIP ValueError: All measurements must have same structure """
[docs] def __init__( self, measurements: list[Measurement], conditions: dict[str, Any] | None = None, details: dict[str, Any] | None = None, ): """ Initialize ConsistentMeasurementSet with structure validation. Parameters ---------- measurements : list[Measurement] List of Measurement objects with consistent structure. conditions : dict[str, Any] | None, optional Experimental conditions for this measurement series. details : dict[str, Any] | None, optional Additional context for this measurement series. Raises ------ ValueError If measurements do not all have the same structure. """ # Validate consistency before calling parent __init__ if measurements: first_structure = self._get_structure(measurements[0]) for i, measurement in enumerate(measurements[1:], start=1): current_structure = self._get_structure(measurement) if current_structure != first_structure: raise ValueError( f"All measurements must have same structure. " f"Measurement 0 has structure {first_structure}, " f"but measurement {i} has structure {current_structure}." ) # Call parent constructor super().__init__(measurements, conditions, details)
@staticmethod def _get_structure(measurement: Measurement) -> tuple[str, ...]: """ Extract structure signature from a measurement. The structure is defined as the sequence of dataset type names. Parameters ---------- measurement : Measurement Measurement to analyze. Returns ------- tuple[str, ...] Tuple of dataset type names in order. Examples -------- >>> from piblin_jax.data.datasets import OneDimensionalDataset >>> ds1 = OneDimensionalDataset(np.array([1, 2]), np.array([3, 4])) >>> ds2 = OneDimensionalDataset(np.array([5, 6]), np.array([7, 8])) >>> m = Measurement([ds1, ds2]) >>> ConsistentMeasurementSet._get_structure(m) ('OneDimensionalDataset', 'OneDimensionalDataset') """ return tuple(type(dataset).__name__ for dataset in measurement.datasets)