Skip to content

gwmock.population.adapter

gwmock.population.adapter

Population adapter bridging gwmock-pop batches to per-event dictionaries.

gwmock.population.adapter.PopulationAdapter

Adapt batched population outputs to deterministic per-event dictionaries.

Source code in src/gwmock/population/adapter.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
class PopulationAdapter:
    """Adapt batched population outputs to deterministic per-event dictionaries."""

    def __init__(
        self,
        population_mapping: Mapping[str, Sequence[Any]],
        *,
        source_type: str,
        parameter_names: Sequence[str] | None = None,
        metadata: Mapping[str, Any] | None = None,
    ) -> None:
        """Initialize the population adapter.

        Args:
            population_mapping: Batched parameter mapping keyed by canonical parameter names.
            source_type: Non-empty population routing key supplied by ``gwmock-pop``.
            parameter_names: Optional ordered parameter names. If omitted, the mapping order is used.
        """
        self._population_mapping = {name: tuple(values) for name, values in population_mapping.items()}
        self._source_type = self._validate_source_type(source_type)
        self._parameter_names = tuple(parameter_names or self._population_mapping.keys())
        self._metadata = dict(metadata or {})
        self._sample_count = self._validate_population_mapping(
            population_mapping=self._population_mapping,
            parameter_names=self._parameter_names,
        )

    @classmethod
    def from_backend(
        cls,
        backend: GWPopSimulator,
        *,
        n_samples: int | None,
        **kwargs: Any,
    ) -> PopulationAdapter:
        """Build an adapter from a ``gwmock-pop`` protocol backend.

        Args:
            backend: The backend to use.
            n_samples: The number of samples to generate, or ``None`` to load the full catalogue.
            **kwargs: Additional arguments to pass to the backend.

        Returns:
            A PopulationAdapter instance.

        Raises:
            TypeError: If the backend does not satisfy the GWPopSimulator protocol.
            ValueError: If n_samples is not a positive integer.
            ValueError: If backend.parameter_names is empty.
        """
        if not isinstance(backend, GWPopSimulator):
            raise TypeError("backend must satisfy the GWPopSimulator protocol.")
        if n_samples is not None and n_samples <= 0:
            raise ValueError("n_samples must be a positive integer.")

        parameter_names = tuple(backend.parameter_names)
        if not parameter_names:
            raise ValueError("backend.parameter_names must not be empty.")
        backend_metadata = getattr(backend, "metadata", None)

        return cls(
            backend.simulate(n_samples=n_samples, **kwargs),
            source_type=backend.source_type,
            parameter_names=parameter_names,
            metadata=backend_metadata if isinstance(backend_metadata, Mapping) else None,
        )

    @classmethod
    def from_mapping(
        cls,
        population_mapping: Mapping[str, Sequence[Any]],
        *,
        source_type: str,
        parameter_names: Sequence[str] | None = None,
    ) -> PopulationAdapter:
        """Build an adapter from an already-materialized population mapping.

        Args:
            population_mapping: The population mapping to use.
            source_type: The source type to use for the backend.
            parameter_names: The parameter names to use.

        Returns:
            A PopulationAdapter instance.
        """
        return cls(
            population_mapping,
            source_type=source_type,
            parameter_names=parameter_names,
        )

    @property
    def parameter_names(self) -> tuple[str, ...]:
        """Return the deterministic parameter ordering.

        Returns:
            The deterministic parameter ordering.
        """
        return self._parameter_names

    @property
    def source_type(self) -> str:
        """Return the backend routing key.

        Returns:
            The backend routing key.
        """
        return self._source_type

    @property
    def population_mapping(self) -> Mapping[str, Sequence[Any]]:
        """Return the validated population mapping.

        Returns:
            The validated population mapping.
        """
        return MappingProxyType(self._population_mapping)

    @property
    def metadata(self) -> dict[str, Any]:
        """Return backend metadata preserved across the gwmock-pop boundary.

        Returns:
            The backend metadata.
        """
        return dict(self._metadata)

    def __len__(self) -> int:
        """Return the number of events available in the adapter.

        Returns:
            The number of events available in the adapter.
        """
        return self._sample_count

    def __iter__(self) -> Iterator[dict[str, Any]]:
        """Iterate over per-event parameter dictionaries.

        Returns:
            An iterator over the per-event parameter dictionaries.
        """
        return self.iter_event_parameters()

    def iter_event_parameters(self) -> Iterator[dict[str, Any]]:
        """Yield deterministic per-event parameter dictionaries.

        Returns:
            An iterator over the per-event parameter dictionaries.
        """
        for index in range(len(self)):
            yield self.get_event_parameters(index)

    def get_event_parameters(self, index: int) -> dict[str, Any]:
        """Return one event dictionary from the batched population mapping.

        Args:
            index: The index of the event to get.

        Returns:
            The event dictionary.
        """
        if index < 0 or index >= len(self):
            raise IndexError("Population event index out of range.")

        return {
            parameter_name: self._coerce_event_value(self._population_mapping[parameter_name][index])
            for parameter_name in self.parameter_names
        }

    @staticmethod
    def _validate_source_type(source_type: str) -> str:
        """Validate the source type.

        Args:
            source_type: The source type to validate.

        Returns:
            The validated source type.
        """
        if not isinstance(source_type, str) or not source_type.strip():
            raise ValueError("source_type must be a non-empty string.")
        return source_type

    @classmethod
    def _validate_population_mapping(
        cls,
        *,
        population_mapping: Mapping[str, Sequence[Any]],
        parameter_names: Sequence[str],
    ) -> int:
        """Validate the population mapping.

        Args:
            population_mapping: The population mapping to validate.
            parameter_names: The parameter names to validate.

        Returns:
            The number of samples in the population mapping.
        """
        mapping_keys = tuple(population_mapping.keys())
        expected_keys = tuple(parameter_names)
        if mapping_keys != expected_keys:
            raise ValueError("Population mapping keys must match parameter_names in the same order.")

        sample_count: int | None = None
        for parameter_name in parameter_names:
            values = population_mapping[parameter_name]
            sample_count = cls._validate_parameter_values(
                parameter_name=parameter_name,
                values=values,
                expected_length=sample_count,
            )
        return sample_count or 0

    @staticmethod
    def _validate_parameter_values(
        *,
        parameter_name: str,
        values: Sequence[Any],
        expected_length: int | None,
    ) -> int:
        """Validate the parameter values.

        Args:
            parameter_name: The parameter name to validate.
            values: The values to validate.
            expected_length: The expected length of the values.

        Returns:
            The length of the values.
        """
        shape = getattr(values, "shape", None)
        if shape is not None and len(shape) != 1:
            raise ValueError(f"Population values for {parameter_name} must be one-dimensional.")

        try:
            length = len(values)
        except TypeError as exc:
            raise TypeError(f"Population values for {parameter_name} must be indexable sequences.") from exc

        if expected_length is not None and length != expected_length:
            raise ValueError("All population parameter arrays must have the same number of samples.")
        return length

    @staticmethod
    def _coerce_event_value(value: Any) -> Any:
        """Coerce the event value.

        Args:
            value: The value to coerce.

        Returns:
            The coerced value.
        """
        return value.item() if hasattr(value, "item") else value

gwmock.population.adapter.PopulationAdapter.parameter_names property

Return the deterministic parameter ordering.

Returns:

Type Description
tuple[str, ...]

The deterministic parameter ordering.

gwmock.population.adapter.PopulationAdapter.source_type property

Return the backend routing key.

Returns:

Type Description
str

The backend routing key.

gwmock.population.adapter.PopulationAdapter.population_mapping property

Return the validated population mapping.

Returns:

Type Description
Mapping[str, Sequence[Any]]

The validated population mapping.

gwmock.population.adapter.PopulationAdapter.metadata property

Return backend metadata preserved across the gwmock-pop boundary.

Returns:

Type Description
dict[str, Any]

The backend metadata.

gwmock.population.adapter.PopulationAdapter.from_backend(backend, *, n_samples, **kwargs) classmethod

Build an adapter from a gwmock-pop protocol backend.

Parameters:

Name Type Description Default
backend GWPopSimulator

The backend to use.

required
n_samples int | None

The number of samples to generate, or None to load the full catalogue.

required
**kwargs Any

Additional arguments to pass to the backend.

{}

Returns:

Type Description
PopulationAdapter

A PopulationAdapter instance.

Raises:

Type Description
TypeError

If the backend does not satisfy the GWPopSimulator protocol.

ValueError

If n_samples is not a positive integer.

ValueError

If backend.parameter_names is empty.

Source code in src/gwmock/population/adapter.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@classmethod
def from_backend(
    cls,
    backend: GWPopSimulator,
    *,
    n_samples: int | None,
    **kwargs: Any,
) -> PopulationAdapter:
    """Build an adapter from a ``gwmock-pop`` protocol backend.

    Args:
        backend: The backend to use.
        n_samples: The number of samples to generate, or ``None`` to load the full catalogue.
        **kwargs: Additional arguments to pass to the backend.

    Returns:
        A PopulationAdapter instance.

    Raises:
        TypeError: If the backend does not satisfy the GWPopSimulator protocol.
        ValueError: If n_samples is not a positive integer.
        ValueError: If backend.parameter_names is empty.
    """
    if not isinstance(backend, GWPopSimulator):
        raise TypeError("backend must satisfy the GWPopSimulator protocol.")
    if n_samples is not None and n_samples <= 0:
        raise ValueError("n_samples must be a positive integer.")

    parameter_names = tuple(backend.parameter_names)
    if not parameter_names:
        raise ValueError("backend.parameter_names must not be empty.")
    backend_metadata = getattr(backend, "metadata", None)

    return cls(
        backend.simulate(n_samples=n_samples, **kwargs),
        source_type=backend.source_type,
        parameter_names=parameter_names,
        metadata=backend_metadata if isinstance(backend_metadata, Mapping) else None,
    )

gwmock.population.adapter.PopulationAdapter.from_mapping(population_mapping, *, source_type, parameter_names=None) classmethod

Build an adapter from an already-materialized population mapping.

Parameters:

Name Type Description Default
population_mapping Mapping[str, Sequence[Any]]

The population mapping to use.

required
source_type str

The source type to use for the backend.

required
parameter_names Sequence[str] | None

The parameter names to use.

None

Returns:

Type Description
PopulationAdapter

A PopulationAdapter instance.

Source code in src/gwmock/population/adapter.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@classmethod
def from_mapping(
    cls,
    population_mapping: Mapping[str, Sequence[Any]],
    *,
    source_type: str,
    parameter_names: Sequence[str] | None = None,
) -> PopulationAdapter:
    """Build an adapter from an already-materialized population mapping.

    Args:
        population_mapping: The population mapping to use.
        source_type: The source type to use for the backend.
        parameter_names: The parameter names to use.

    Returns:
        A PopulationAdapter instance.
    """
    return cls(
        population_mapping,
        source_type=source_type,
        parameter_names=parameter_names,
    )

gwmock.population.adapter.PopulationAdapter.iter_event_parameters()

Yield deterministic per-event parameter dictionaries.

Returns:

Type Description
Iterator[dict[str, Any]]

An iterator over the per-event parameter dictionaries.

Source code in src/gwmock/population/adapter.py
155
156
157
158
159
160
161
162
def iter_event_parameters(self) -> Iterator[dict[str, Any]]:
    """Yield deterministic per-event parameter dictionaries.

    Returns:
        An iterator over the per-event parameter dictionaries.
    """
    for index in range(len(self)):
        yield self.get_event_parameters(index)

gwmock.population.adapter.PopulationAdapter.get_event_parameters(index)

Return one event dictionary from the batched population mapping.

Parameters:

Name Type Description Default
index int

The index of the event to get.

required

Returns:

Type Description
dict[str, Any]

The event dictionary.

Source code in src/gwmock/population/adapter.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def get_event_parameters(self, index: int) -> dict[str, Any]:
    """Return one event dictionary from the batched population mapping.

    Args:
        index: The index of the event to get.

    Returns:
        The event dictionary.
    """
    if index < 0 or index >= len(self):
        raise IndexError("Population event index out of range.")

    return {
        parameter_name: self._coerce_event_value(self._population_mapping[parameter_name][index])
        for parameter_name in self.parameter_names
    }