mergeron 2025.739265.2__py3-none-any.whl → 2025.739290.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mergeron might be problematic. Click here for more details.
- mergeron/__init__.py +67 -2
- mergeron/core/guidelines_boundaries.py +85 -21
- mergeron/core/pseudorandom_numbers.py +61 -51
- mergeron/gen/__init__.py +222 -84
- mergeron/gen/data_generation.py +143 -182
- mergeron/gen/data_generation_functions.py +68 -118
- mergeron/gen/enforcement_stats.py +30 -6
- mergeron/gen/upp_tests.py +6 -7
- {mergeron-2025.739265.2.dist-info → mergeron-2025.739290.1.dist-info}/METADATA +2 -1
- {mergeron-2025.739265.2.dist-info → mergeron-2025.739290.1.dist-info}/RECORD +11 -11
- {mergeron-2025.739265.2.dist-info → mergeron-2025.739290.1.dist-info}/WHEEL +0 -0
mergeron/gen/__init__.py
CHANGED
|
@@ -7,12 +7,15 @@ containers for industry data generation and testing.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import enum
|
|
10
|
+
from collections.abc import Sequence
|
|
10
11
|
from dataclasses import dataclass
|
|
11
|
-
from typing import ClassVar,
|
|
12
|
+
from typing import ClassVar, Protocol
|
|
12
13
|
|
|
13
14
|
import numpy as np
|
|
14
|
-
from attrs import Attribute, cmp_using, field,
|
|
15
|
+
from attrs import Attribute, Converter, cmp_using, field, validators
|
|
16
|
+
from attrs import frozen as frozen_attrs
|
|
15
17
|
from numpy.random import SeedSequence
|
|
18
|
+
from ruamel import yaml
|
|
16
19
|
|
|
17
20
|
from .. import ( # noqa: TID252
|
|
18
21
|
DEFAULT_REC_RATIO,
|
|
@@ -24,25 +27,28 @@ from .. import ( # noqa: TID252
|
|
|
24
27
|
ArrayINT,
|
|
25
28
|
RECForm,
|
|
26
29
|
UPPAggrSelector,
|
|
30
|
+
this_yaml,
|
|
31
|
+
)
|
|
32
|
+
from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
33
|
+
DEFAULT_BETA_DIST_PARMS,
|
|
34
|
+
DEFAULT_DIST_PARMS,
|
|
27
35
|
)
|
|
28
|
-
from ..core.pseudorandom_numbers import DEFAULT_DIST_PARMS # noqa: TID252
|
|
29
36
|
|
|
30
37
|
__version__ = VERSION
|
|
31
38
|
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
DEFAULT_FCOUNT_WTS = np.divide(
|
|
35
|
-
(_nr := np.arange(1, 7)[::-1]), _nr.sum(), dtype=np.float64
|
|
36
|
-
)
|
|
40
|
+
DEFAULT_FCOUNT_WTS = np.asarray((_nr := np.arange(6, 0, -1)) / _nr.sum(), float)
|
|
37
41
|
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
@dataclass
|
|
44
|
+
class SeedSequenceData:
|
|
45
|
+
share: SeedSequence
|
|
46
|
+
pcm: SeedSequence
|
|
47
|
+
fcounts: SeedSequence | None
|
|
48
|
+
price: SeedSequence | None
|
|
44
49
|
|
|
45
50
|
|
|
51
|
+
@this_yaml.register_class
|
|
46
52
|
@enum.unique
|
|
47
53
|
class PriceSpec(tuple[bool, str | None], enum.ReprEnum):
|
|
48
54
|
"""Price specification.
|
|
@@ -57,6 +63,7 @@ class PriceSpec(tuple[bool, str | None], enum.ReprEnum):
|
|
|
57
63
|
CSY = (False, "market-wide cost-symmetry")
|
|
58
64
|
|
|
59
65
|
|
|
66
|
+
@this_yaml.register_class
|
|
60
67
|
@enum.unique
|
|
61
68
|
class SHRDistribution(enum.StrEnum):
|
|
62
69
|
"""Market share distributions."""
|
|
@@ -92,11 +99,59 @@ class SHRDistribution(enum.StrEnum):
|
|
|
92
99
|
"""
|
|
93
100
|
|
|
94
101
|
|
|
95
|
-
|
|
102
|
+
def _fc_wts_conv(
|
|
103
|
+
_v: Sequence[float | int] | ArrayDouble | ArrayINT | None, _i: ShareSpec
|
|
104
|
+
) -> ArrayFloat | None:
|
|
105
|
+
if _i.dist_type == SHRDistribution.UNI:
|
|
106
|
+
return None
|
|
107
|
+
elif _v is None or np.array_equal(_v, DEFAULT_FCOUNT_WTS):
|
|
108
|
+
return DEFAULT_FCOUNT_WTS
|
|
109
|
+
else:
|
|
110
|
+
return _tv if (_tv := np.asarray(_v, float)).sum() == 1 else _tv / _tv.sum()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _shr_dp_conv(_v: Sequence[float] | ArrayFloat | None, _i: ShareSpec) -> ArrayFloat:
|
|
114
|
+
match _v:
|
|
115
|
+
case None if _i.dist_type == SHRDistribution.UNI:
|
|
116
|
+
return DEFAULT_DIST_PARMS
|
|
117
|
+
case None:
|
|
118
|
+
_fc_max = 1 + (
|
|
119
|
+
len(DEFAULT_FCOUNT_WTS)
|
|
120
|
+
if not hasattr(_i, "firm_counts_weights")
|
|
121
|
+
or _i.firm_counts_weights is None
|
|
122
|
+
or len(_i.firm_counts_weights) == 0
|
|
123
|
+
else len(_i.firm_counts_weights)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
match _i.dist_type:
|
|
127
|
+
case SHRDistribution.DIR_FLAT | SHRDistribution.DIR_FLAT_CONSTR:
|
|
128
|
+
return np.ones(_fc_max, float)
|
|
129
|
+
case SHRDistribution.DIR_ASYM:
|
|
130
|
+
return np.array([2.0] * 6 + [1.5] * 5 + [1.25] * _fc_max, float)
|
|
131
|
+
case SHRDistribution.DIR_COND:
|
|
132
|
+
return np.array([], float)
|
|
133
|
+
case _ if isinstance(_i.dist_type, SHRDistribution):
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"No default defined for market share distribution, {_i.dist_type!r}"
|
|
136
|
+
)
|
|
137
|
+
case _:
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Unsupported distribution for market share generation, {_i.dist_type!r}"
|
|
140
|
+
)
|
|
141
|
+
case _ if isinstance(_v, Sequence | np.ndarray):
|
|
142
|
+
return np.asarray(_v, float)
|
|
143
|
+
case _:
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"Input, {_v!r} has invalid type. Must be None, Sequence of floats, or Numpy ndarray."
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@this_yaml.register_class
|
|
150
|
+
@frozen_attrs
|
|
96
151
|
class ShareSpec:
|
|
97
152
|
"""Market share specification
|
|
98
153
|
|
|
99
|
-
A
|
|
154
|
+
A salientfeature of market-share specification in this package is that
|
|
100
155
|
the draws represent markets with multiple different firm-counts.
|
|
101
156
|
Firm-counts are unspecified if the share distribution is
|
|
102
157
|
:attr:`mergeron.SHRDistribution.UNI`, for Dirichlet-distributed market-shares,
|
|
@@ -105,85 +160,92 @@ class ShareSpec:
|
|
|
105
160
|
|
|
106
161
|
Notes
|
|
107
162
|
-----
|
|
108
|
-
If :attr
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
For a sample with explicit firm counts, market shares must be specified as
|
|
115
|
-
having a supported Dirichlet distribution (see :class:`mergeron.gen.SHRDistribution`).
|
|
163
|
+
If :attr:`.dist_type` == :attr:`.SHRDistribution.UNI`, it is then infeasible that
|
|
164
|
+
:attr:`.recapture_form` == :attr:`mergeron.RECForm.OUTIN`.
|
|
165
|
+
In other words, recapture ratios cannot be estimated using
|
|
166
|
+
outside-good choice probabilities if the distribution of markets over firm-counts
|
|
167
|
+
is unspecified.
|
|
116
168
|
|
|
117
169
|
"""
|
|
118
170
|
|
|
119
|
-
dist_type: SHRDistribution = field(
|
|
171
|
+
dist_type: SHRDistribution = field(kw_only=False)
|
|
120
172
|
"""See :class:`SHRDistribution`"""
|
|
121
173
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
174
|
+
firm_counts_weights: ArrayFloat | None = field(
|
|
175
|
+
kw_only=True,
|
|
176
|
+
eq=cmp_using(eq=np.array_equal),
|
|
177
|
+
converter=Converter(_fc_wts_conv, takes_self=True), # type: ignore
|
|
178
|
+
)
|
|
179
|
+
"""Relative or absolute frequencies of pre-merger firm counts
|
|
180
|
+
|
|
181
|
+
Defaults to :attr:`DEFAULT_FCOUNT_WTS`, which specifies pre-merger
|
|
182
|
+
firm-counts of 2 to 7 with weights in descending order from 6 to 1.
|
|
183
|
+
|
|
184
|
+
ALERT: Firm-count weights are irrelevant when the mergering firmss shares are specified
|
|
185
|
+
to have uniform distribution; therefore this attribute is forced to None if
|
|
186
|
+
:attr:`.dist_type` == :attr:`.SHRDistribution.UNI`.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
@firm_counts_weights.default
|
|
191
|
+
def _fcwd(_i: ShareSpec) -> ArrayFloat | None:
|
|
192
|
+
return _fc_wts_conv(None, _i)
|
|
193
|
+
|
|
194
|
+
@firm_counts_weights.validator
|
|
195
|
+
def _fcv(_i: ShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
|
|
196
|
+
if _i.dist_type != SHRDistribution.UNI and not len(_v):
|
|
197
|
+
raise ValueError(
|
|
198
|
+
f"Attribute, {'"firm_counts_weights"'} must be populated if the share distribution is "
|
|
199
|
+
"other than uniform distribution."
|
|
200
|
+
)
|
|
142
201
|
|
|
143
|
-
dist_parms: ArrayFloat
|
|
144
|
-
|
|
202
|
+
dist_parms: ArrayFloat = field(
|
|
203
|
+
kw_only=True,
|
|
204
|
+
converter=Converter(_shr_dp_conv, takes_self=True), # type: ignore
|
|
205
|
+
eq=cmp_using(eq=np.array_equal),
|
|
145
206
|
)
|
|
146
207
|
"""Parameters for tailoring market-share distribution
|
|
147
208
|
|
|
148
209
|
For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
|
|
149
210
|
for Dirichlet-type distributions, a vector of shape parameters of length
|
|
150
|
-
|
|
211
|
+
equal to 1 plus the length of firm-count weights below; defaults depend on
|
|
151
212
|
type of Dirichlet-distribution specified.
|
|
152
213
|
|
|
153
214
|
"""
|
|
154
215
|
|
|
216
|
+
@dist_parms.default
|
|
217
|
+
def _dpd(_i: ShareSpec) -> ArrayFloat:
|
|
218
|
+
# converters run after defaults, and we
|
|
219
|
+
# avoid redundancy and confusion here
|
|
220
|
+
return _shr_dp_conv(None, _i)
|
|
221
|
+
|
|
155
222
|
@dist_parms.validator
|
|
156
|
-
def
|
|
157
|
-
_i: ShareSpec,
|
|
158
|
-
_a: Attribute[ArrayFloat | ArrayINT | None],
|
|
159
|
-
_v: ArrayFloat | ArrayINT | None,
|
|
160
|
-
) -> None:
|
|
223
|
+
def _dpv(_i: ShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
|
|
161
224
|
if (
|
|
162
225
|
_i.firm_counts_weights is not None
|
|
163
226
|
and _v is not None
|
|
164
|
-
and len(_v) < 1 + len(_i.firm_counts_weights)
|
|
227
|
+
and 0 < len(_v) < (1 + len(_i.firm_counts_weights))
|
|
165
228
|
):
|
|
166
229
|
raise ValueError(
|
|
167
|
-
"If specified, the number of distribution parameters must
|
|
168
|
-
"the maximum firm-count premerger, which is
|
|
169
|
-
"vector specifying firm-count weights."
|
|
230
|
+
"If specified, the number of distribution parameters must euqal or "
|
|
231
|
+
"exceed the maximum firm-count premerger, which is "
|
|
232
|
+
"1 plus the length of the vector specifying firm-count weights."
|
|
170
233
|
)
|
|
171
234
|
|
|
172
|
-
firm_counts_weights: ArrayFloat | ArrayINT | None = field(
|
|
173
|
-
default=DEFAULT_FCOUNT_WTS, eq=cmp_using(eq=np.array_equal)
|
|
174
|
-
)
|
|
175
|
-
"""Relative or absolute frequencies of firm counts
|
|
176
|
-
|
|
177
|
-
Given frequencies are exogenous to generated market data sample;
|
|
178
|
-
for Dirichlet-type distributions, defaults to DEFAULT_FCOUNT_WTS, which specifies
|
|
179
|
-
firm-counts of 2 to 6 with weights in descending order from 5 to 1.
|
|
180
|
-
|
|
181
|
-
"""
|
|
182
|
-
|
|
183
235
|
recapture_form: RECForm = field(default=RECForm.INOUT)
|
|
184
236
|
"""See :class:`mergeron.RECForm`"""
|
|
185
237
|
|
|
186
|
-
|
|
238
|
+
@recapture_form.validator
|
|
239
|
+
def _rfv(_i: ShareSpec, _a: Attribute[RECForm], _v: RECForm) -> None:
|
|
240
|
+
if _i.dist_type == SHRDistribution.UNI and _v == RECForm.OUTIN:
|
|
241
|
+
raise ValueError(
|
|
242
|
+
"Outside-good choice probabilities cannot be generated if the "
|
|
243
|
+
"merging firms' market shares have uniform distribution over the "
|
|
244
|
+
"3-dimensional simplex with the distribution of markets over "
|
|
245
|
+
"firm-counts unspecified."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
recapture_ratio: float | None = field(kw_only=True)
|
|
187
249
|
"""A value between 0 and 1.
|
|
188
250
|
|
|
189
251
|
:code:`None` if market share specification requires direct generation of
|
|
@@ -203,10 +265,17 @@ class ShareSpec:
|
|
|
203
265
|
screens for further investigation, rather than as the basis for presumptions of harm or
|
|
204
266
|
presumptions no harm.)
|
|
205
267
|
|
|
268
|
+
ALERT: If diversion ratios are estimated by specifying a choice probability for the
|
|
269
|
+
outside good, the recapture ratio is set to None, overriding any user-specified value.
|
|
270
|
+
|
|
206
271
|
"""
|
|
207
272
|
|
|
273
|
+
@recapture_ratio.default
|
|
274
|
+
def _rrd(_i: ShareSpec) -> float | None:
|
|
275
|
+
return None if _i.recapture_form == RECForm.OUTIN else DEFAULT_REC_RATIO
|
|
276
|
+
|
|
208
277
|
@recapture_ratio.validator
|
|
209
|
-
def
|
|
278
|
+
def _rrv(_i: ShareSpec, _a: Attribute[float], _v: float) -> None:
|
|
210
279
|
if _v and not (0 < _v <= 1):
|
|
211
280
|
raise ValueError("Recapture ratio must lie in the interval, [0, 1).")
|
|
212
281
|
elif _v is None and _i.recapture_form != RECForm.OUTIN:
|
|
@@ -216,6 +285,22 @@ class ShareSpec:
|
|
|
216
285
|
"interval [0, 1)."
|
|
217
286
|
)
|
|
218
287
|
|
|
288
|
+
@classmethod
|
|
289
|
+
def to_yaml(
|
|
290
|
+
cls, _r: yaml.representer.SafeRepresenter, _d: ShareSpec
|
|
291
|
+
) -> yaml.MappingNode:
|
|
292
|
+
_ret: yaml.MappingNode = _r.represent_mapping(
|
|
293
|
+
f"!{cls.__name__}",
|
|
294
|
+
{_a.name: getattr(_d, _a.name) for _a in _d.__attrs_attrs__},
|
|
295
|
+
)
|
|
296
|
+
return _ret
|
|
297
|
+
|
|
298
|
+
@classmethod
|
|
299
|
+
def from_yaml(
|
|
300
|
+
cls, _c: yaml.constructor.SafeConstructor, _n: yaml.MappingNode
|
|
301
|
+
) -> ShareSpec:
|
|
302
|
+
return cls(**_c.construct_mapping(_n))
|
|
303
|
+
|
|
219
304
|
|
|
220
305
|
@enum.unique
|
|
221
306
|
class PCMDistribution(enum.StrEnum):
|
|
@@ -236,7 +321,8 @@ class FM2Constraint(enum.StrEnum):
|
|
|
236
321
|
SYM = "symmetric"
|
|
237
322
|
|
|
238
323
|
|
|
239
|
-
@
|
|
324
|
+
@this_yaml.register_class
|
|
325
|
+
@frozen_attrs
|
|
240
326
|
class PCMSpec:
|
|
241
327
|
"""Price-cost margin (PCM) specification
|
|
242
328
|
|
|
@@ -252,10 +338,14 @@ class PCMSpec:
|
|
|
252
338
|
|
|
253
339
|
"""
|
|
254
340
|
|
|
255
|
-
dist_type: PCMDistribution = field(
|
|
341
|
+
dist_type: PCMDistribution = field()
|
|
256
342
|
"""See :class:`PCMDistribution`"""
|
|
257
343
|
|
|
258
|
-
|
|
344
|
+
@dist_type.default
|
|
345
|
+
def _dtd(_i: PCMSpec) -> PCMDistribution:
|
|
346
|
+
return PCMDistribution.UNI
|
|
347
|
+
|
|
348
|
+
dist_parms: ArrayFloat | None = field(kw_only=True, eq=np.array_repr)
|
|
259
349
|
"""Parameter specification for tailoring PCM distribution
|
|
260
350
|
|
|
261
351
|
For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
|
|
@@ -265,12 +355,24 @@ class PCMSpec:
|
|
|
265
355
|
|
|
266
356
|
"""
|
|
267
357
|
|
|
358
|
+
@dist_parms.default
|
|
359
|
+
def _dpwd(_i: PCMSpec) -> ArrayFloat | None:
|
|
360
|
+
match _i.dist_type:
|
|
361
|
+
case PCMDistribution.EMPR:
|
|
362
|
+
return None
|
|
363
|
+
case PCMDistribution.BETA:
|
|
364
|
+
return DEFAULT_BETA_DIST_PARMS
|
|
365
|
+
case PCMDistribution.BETA_BND:
|
|
366
|
+
return np.array([0.5, 1.0, 0.0, 0.1], float)
|
|
367
|
+
case _:
|
|
368
|
+
return DEFAULT_DIST_PARMS
|
|
369
|
+
|
|
268
370
|
@dist_parms.validator
|
|
269
|
-
def
|
|
270
|
-
_i: PCMSpec, _a: Attribute[
|
|
371
|
+
def _dpv(
|
|
372
|
+
_i: PCMSpec, _a: Attribute[ArrayFloat | None], _v: ArrayFloat | None
|
|
271
373
|
) -> None:
|
|
272
374
|
if _i.dist_type.name.startswith("BETA"):
|
|
273
|
-
if _v is None:
|
|
375
|
+
if _v is None or not any(_v.shape):
|
|
274
376
|
pass
|
|
275
377
|
elif np.array_equal(_v, DEFAULT_DIST_PARMS):
|
|
276
378
|
raise ValueError(
|
|
@@ -288,17 +390,31 @@ class PCMSpec:
|
|
|
288
390
|
f'for PCM with distribution, "{_i.dist_type}" is incorrect.'
|
|
289
391
|
)
|
|
290
392
|
|
|
291
|
-
elif _i.dist_type == PCMDistribution.EMPR
|
|
393
|
+
elif _v is not None and _i.dist_type == PCMDistribution.EMPR:
|
|
292
394
|
raise ValueError(
|
|
293
395
|
f"Empirical distribution does not require additional parameters; "
|
|
294
396
|
f'"given value, {_v!r} is ignored."'
|
|
295
397
|
)
|
|
296
398
|
|
|
297
|
-
firm2_pcm_constraint: FM2Constraint = field(
|
|
298
|
-
kw_only=False, default=FM2Constraint.IID
|
|
299
|
-
)
|
|
399
|
+
firm2_pcm_constraint: FM2Constraint = field(kw_only=True, default=FM2Constraint.IID)
|
|
300
400
|
"""See :class:`FM2Constraint`"""
|
|
301
401
|
|
|
402
|
+
@classmethod
|
|
403
|
+
def to_yaml(
|
|
404
|
+
cls, _r: yaml.representer.SafeRepresenter, _d: PCMSpec
|
|
405
|
+
) -> yaml.MappingNode:
|
|
406
|
+
_ret: yaml.MappingNode = _r.represent_mapping(
|
|
407
|
+
f"!{cls.__name__}",
|
|
408
|
+
{_a.name: getattr(_d, _a.name) for _a in _d.__attrs_attrs__},
|
|
409
|
+
)
|
|
410
|
+
return _ret
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
413
|
+
def from_yaml(
|
|
414
|
+
cls, _c: yaml.constructor.SafeConstructor, _n: yaml.MappingNode
|
|
415
|
+
) -> PCMSpec:
|
|
416
|
+
return cls(**_c.construct_mapping(_n))
|
|
417
|
+
|
|
302
418
|
|
|
303
419
|
@enum.unique
|
|
304
420
|
class SSZConstant(float, enum.ReprEnum):
|
|
@@ -340,11 +456,8 @@ class SSZConstant(float, enum.ReprEnum):
|
|
|
340
456
|
"""When initial set of draws is not restricted in any way."""
|
|
341
457
|
|
|
342
458
|
|
|
343
|
-
# Validators for selected attributes of MarketSpec
|
|
344
|
-
|
|
345
|
-
|
|
346
459
|
@dataclass(slots=True, frozen=True)
|
|
347
|
-
class
|
|
460
|
+
class MarketSampleData:
|
|
348
461
|
"""Container for generated markets data sample."""
|
|
349
462
|
|
|
350
463
|
frmshr_array: ArrayDouble
|
|
@@ -440,10 +553,10 @@ class MarginDataSample:
|
|
|
440
553
|
class INVResolution(enum.StrEnum):
|
|
441
554
|
CLRN = "clearance"
|
|
442
555
|
ENFT = "enforcement"
|
|
443
|
-
BOTH = "
|
|
556
|
+
BOTH = "investigation"
|
|
444
557
|
|
|
445
558
|
|
|
446
|
-
@
|
|
559
|
+
@frozen_attrs
|
|
447
560
|
class UPPTestRegime:
|
|
448
561
|
"""Configuration for UPP tests."""
|
|
449
562
|
|
|
@@ -516,3 +629,28 @@ class DataclassInstance(Protocol):
|
|
|
516
629
|
"""Generic dataclass-instance"""
|
|
517
630
|
|
|
518
631
|
__dataclass_fields__: ClassVar
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
for _typ in (
|
|
635
|
+
PriceSpec,
|
|
636
|
+
SHRDistribution,
|
|
637
|
+
PCMDistribution,
|
|
638
|
+
FM2Constraint,
|
|
639
|
+
SSZConstant,
|
|
640
|
+
INVResolution,
|
|
641
|
+
):
|
|
642
|
+
# NOTE: If additional enums are defined in this module,
|
|
643
|
+
# add themn to the list above
|
|
644
|
+
|
|
645
|
+
_, _ = (
|
|
646
|
+
this_yaml.representer.add_representer(
|
|
647
|
+
_typ,
|
|
648
|
+
lambda _r, _d: _r.represent_scalar(f"!{_d.__class__.__name__}", _d.name),
|
|
649
|
+
),
|
|
650
|
+
this_yaml.constructor.add_constructor(
|
|
651
|
+
f"!{_typ.__name__}",
|
|
652
|
+
lambda _c, _n, /: getattr(
|
|
653
|
+
globals().get(_n.tag.lstrip("!")), _c.construct_scalar(_n)
|
|
654
|
+
),
|
|
655
|
+
),
|
|
656
|
+
)
|