mergeron 2024.739099.2__tar.gz → 2024.739104.1__tar.gz
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-2024.739099.2 → mergeron-2024.739104.1}/PKG-INFO +1 -1
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/pyproject.toml +1 -1
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/__init__.py +7 -10
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/ftc_merger_investigations_data.py +4 -2
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/guidelines_boundaries.py +11 -11
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/gen/__init__.py +140 -160
- mergeron-2024.739104.1/src/mergeron/gen/data_generation.py +518 -0
- mergeron-2024.739099.2/src/mergeron/gen/_data_generation_functions.py → mergeron-2024.739104.1/src/mergeron/gen/data_generation_functions.py +224 -133
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/gen/enforcement_stats.py +28 -30
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/gen/upp_tests.py +72 -202
- mergeron-2024.739099.2/src/mergeron/gen/data_generation.py +0 -283
- mergeron-2024.739099.2/src/mergeron/gen/market_sample.py +0 -143
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/README.rst +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/License.txt +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/__init__.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/damodaran_margin_data.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/guidelines_boundary_functions.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/guidelines_boundary_functions_extra.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/pseudorandom_numbers.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/__init__.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/damodaran_margin_data.xls +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/ftc_invdata.msgpack +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/mergeron.cls +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/data/jinja2_LaTeX_templates/setup_tikz_tables.tex +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/demo/__init__.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/demo/visualize_empirical_margin_distribution.py +0 -0
- {mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.739104.1
|
|
4
4
|
Summary: Merger Policy Analysis using Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
@@ -2,14 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import TypeAlias
|
|
5
|
+
from typing import TypeAlias
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
-
from numpy.typing import
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
9
|
|
|
10
10
|
_PKG_NAME: str = Path(__file__).parent.stem
|
|
11
11
|
|
|
12
|
-
VERSION = "2024.
|
|
12
|
+
VERSION = "2024.739104.1"
|
|
13
13
|
|
|
14
14
|
__version__ = VERSION
|
|
15
15
|
|
|
@@ -22,24 +22,21 @@ If the subdirectory doesn't exist, it is created on package invocation.
|
|
|
22
22
|
if not DATA_DIR.is_dir():
|
|
23
23
|
DATA_DIR.mkdir(parents=False)
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
np.set_printoptions(precision=18)
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
TF = TypeVar("TF", bound=NBitBase)
|
|
32
|
-
ArrayFloat = NDArray[np.floating[TF]]
|
|
28
|
+
ArrayINT = NDArray[np.intp]
|
|
29
|
+
ArrayFloat = NDArray[np.half | np.single | np.double]
|
|
33
30
|
|
|
34
31
|
|
|
35
32
|
ArrayBoolean: TypeAlias = NDArray[np.bool_]
|
|
36
33
|
|
|
37
|
-
ArrayDouble: TypeAlias = NDArray[np.
|
|
34
|
+
ArrayDouble: TypeAlias = NDArray[np.double]
|
|
38
35
|
ArrayBIGINT: TypeAlias = NDArray[np.int64]
|
|
39
36
|
|
|
40
37
|
|
|
41
38
|
@enum.unique
|
|
42
|
-
class
|
|
39
|
+
class RECTypes(enum.StrEnum):
|
|
43
40
|
"""Recapture rate - derivation methods."""
|
|
44
41
|
|
|
45
42
|
INOUT = "inside-out"
|
|
@@ -285,7 +285,7 @@ def _construct_new_period_data(
|
|
|
285
285
|
# Consistency here means that the number of investigations reported
|
|
286
286
|
# in each period is no less than the number reported in
|
|
287
287
|
# any prior period.Although the time periods for table 3.2 through 3.5
|
|
288
|
-
# are not the
|
|
288
|
+
# are not the same in the data for 1996-2005 and 1996-2007 as in
|
|
289
289
|
# the data for the other periods, they are nonetheless shorter than
|
|
290
290
|
# the period 1996-2011, and hence the counts reported for 1996-2011
|
|
291
291
|
# cannot be less than those reported in these prior periods. Note that
|
|
@@ -337,7 +337,8 @@ def _construct_new_period_data(
|
|
|
337
337
|
_invdata_cuml_array[:, -3:-1] - _invdata_base_array[:, -3:-1] # type: ignore
|
|
338
338
|
)
|
|
339
339
|
|
|
340
|
-
#
|
|
340
|
+
# # // spellchecker: disable
|
|
341
|
+
# To examine the number of corrected values per table, // spellchecker: disable
|
|
341
342
|
# uncomment the statements below
|
|
342
343
|
# _invdata_array_bld_tbc = where(
|
|
343
344
|
# _invdata_array_bld_enfcls < 0, _invdata_array_bld_enfcls, 0
|
|
@@ -347,6 +348,7 @@ def _construct_new_period_data(
|
|
|
347
348
|
# f"{_data_period}, {_table_no}, {_invdata_ind_group}:",
|
|
348
349
|
# abs(np.einsum('ij->', invdata_array_bld_tbc))
|
|
349
350
|
# )
|
|
351
|
+
# # // spellchecker: disable
|
|
350
352
|
|
|
351
353
|
# Enforce non-negativity
|
|
352
354
|
_invdata_array_bld_enfcls = np.stack((
|
{mergeron-2024.739099.2 → mergeron-2024.739104.1}/src/mergeron/core/guidelines_boundaries.py
RENAMED
|
@@ -13,7 +13,7 @@ import numpy as np
|
|
|
13
13
|
from attrs import Attribute, field, frozen, validators
|
|
14
14
|
from mpmath import mp, mpf # type: ignore
|
|
15
15
|
|
|
16
|
-
from .. import VERSION, ArrayDouble,
|
|
16
|
+
from .. import VERSION, ArrayDouble, RECTypes, UPPAggrSelector # noqa: TID252
|
|
17
17
|
from . import guidelines_boundary_functions as gbfn
|
|
18
18
|
|
|
19
19
|
__version__ = VERSION
|
|
@@ -223,14 +223,14 @@ def _divr_value_validator(
|
|
|
223
223
|
|
|
224
224
|
def _rec_spec_validator(
|
|
225
225
|
_instance: DiversionRatioBoundary,
|
|
226
|
-
_attribute: Attribute[
|
|
227
|
-
_value:
|
|
226
|
+
_attribute: Attribute[RECTypes],
|
|
227
|
+
_value: RECTypes,
|
|
228
228
|
/,
|
|
229
229
|
) -> None:
|
|
230
|
-
if _value ==
|
|
230
|
+
if _value == RECTypes.OUTIN and _instance.recapture_rate:
|
|
231
231
|
raise ValueError(
|
|
232
232
|
f"Invalid recapture specification, {_value!r}. "
|
|
233
|
-
"You may consider specifying `mergeron.
|
|
233
|
+
"You may consider specifying `mergeron.RECTypes.INOUT` here, and "
|
|
234
234
|
'assigning the default recapture rate as attribute, "recapture_rate" of '
|
|
235
235
|
"this `DiversionRatioBoundarySpec` object."
|
|
236
236
|
)
|
|
@@ -265,24 +265,24 @@ class DiversionRatioBoundary:
|
|
|
265
265
|
kw_only=False, default=0.85, validator=validators.instance_of(float)
|
|
266
266
|
)
|
|
267
267
|
|
|
268
|
-
recapture_form:
|
|
268
|
+
recapture_form: RECTypes | None = field(
|
|
269
269
|
kw_only=True,
|
|
270
|
-
default=
|
|
270
|
+
default=RECTypes.INOUT,
|
|
271
271
|
validator=(
|
|
272
|
-
validators.instance_of((type(None),
|
|
272
|
+
validators.instance_of((type(None), RECTypes)),
|
|
273
273
|
_rec_spec_validator,
|
|
274
274
|
),
|
|
275
275
|
)
|
|
276
276
|
"""
|
|
277
277
|
The form of the recapture rate.
|
|
278
278
|
|
|
279
|
-
When :attr:`mergeron.
|
|
279
|
+
When :attr:`mergeron.RECTypes.INOUT`, the recapture rate for
|
|
280
280
|
he product having the smaller market-share is assumed to equal the default,
|
|
281
281
|
and the recapture rate for the product with the larger market-share is
|
|
282
282
|
computed assuming MNL demand. Fixed recapture rates are specified as
|
|
283
|
-
:attr:`mergeron.
|
|
283
|
+
:attr:`mergeron.RECTypes.FIXED`. (To specify that recapture rates be
|
|
284
284
|
constructed from the generated purchase-probabilities for products in
|
|
285
|
-
the market and for the outside good, specify :attr:`mergeron.
|
|
285
|
+
the market and for the outside good, specify :attr:`mergeron.RECTypes.OUTIN`.)
|
|
286
286
|
|
|
287
287
|
The GUPPI boundary is a continuum of diversion ratio boundaries conditional on
|
|
288
288
|
price-cost margins, :math:`d_{ij} = g_i * p_i / (m_j * p_j)`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Defines constants
|
|
2
|
+
Defines constants, specifications (classes with attributes defining varous parameters) and
|
|
3
|
+
containers for industry data generation and testing.
|
|
3
4
|
|
|
4
5
|
"""
|
|
5
6
|
|
|
@@ -7,17 +8,20 @@ from __future__ import annotations
|
|
|
7
8
|
|
|
8
9
|
import enum
|
|
9
10
|
from dataclasses import dataclass
|
|
10
|
-
from typing import ClassVar, Protocol
|
|
11
|
+
from typing import ClassVar, NamedTuple, Protocol
|
|
11
12
|
|
|
12
13
|
import numpy as np
|
|
13
|
-
from attrs import Attribute, cmp_using,
|
|
14
|
+
from attrs import Attribute, cmp_using, field, frozen, validators
|
|
15
|
+
from numpy.random import SeedSequence
|
|
14
16
|
|
|
15
17
|
from .. import ( # noqa: TID252
|
|
16
18
|
VERSION,
|
|
17
19
|
ArrayBIGINT,
|
|
18
20
|
ArrayBoolean,
|
|
19
21
|
ArrayDouble,
|
|
20
|
-
|
|
22
|
+
ArrayFloat,
|
|
23
|
+
ArrayINT,
|
|
24
|
+
RECTypes,
|
|
21
25
|
UPPAggrSelector,
|
|
22
26
|
)
|
|
23
27
|
from ..core.pseudorandom_numbers import DIST_PARMS_DEFAULT # noqa: TID252
|
|
@@ -31,8 +35,15 @@ FCOUNT_WTS_DEFAULT = np.divide(
|
|
|
31
35
|
)
|
|
32
36
|
|
|
33
37
|
|
|
38
|
+
class SeedSequenceData(NamedTuple):
|
|
39
|
+
mktshr_rng_seed_seq: SeedSequence
|
|
40
|
+
pcm_rng_seed_seq: SeedSequence
|
|
41
|
+
fcount_rng_seed_seq: SeedSequence | None
|
|
42
|
+
pr_rng_seed_seq: SeedSequence | None
|
|
43
|
+
|
|
44
|
+
|
|
34
45
|
@enum.unique
|
|
35
|
-
class
|
|
46
|
+
class PriceSpec(tuple[bool, str | None], enum.ReprEnum):
|
|
36
47
|
"""Price specification.
|
|
37
48
|
|
|
38
49
|
Whether prices are symmetric and, if not, the direction of correlation, if any.
|
|
@@ -46,7 +57,7 @@ class PriceConstants(tuple[bool, str | None], enum.ReprEnum):
|
|
|
46
57
|
|
|
47
58
|
|
|
48
59
|
@enum.unique
|
|
49
|
-
class
|
|
60
|
+
class SHRDistributions(enum.StrEnum):
|
|
50
61
|
"""Market share distributions."""
|
|
51
62
|
|
|
52
63
|
UNI = "Uniform"
|
|
@@ -80,39 +91,84 @@ class SHRConstants(enum.StrEnum):
|
|
|
80
91
|
"""
|
|
81
92
|
|
|
82
93
|
|
|
83
|
-
@frozen
|
|
94
|
+
@frozen(kw_only=False)
|
|
84
95
|
class ShareSpec:
|
|
85
96
|
"""Market share specification
|
|
86
97
|
|
|
87
98
|
A key feature of market-share specification in this package is that
|
|
88
99
|
the draws represent markets with multiple different firm-counts.
|
|
89
100
|
Firm-counts are unspecified if the share distribution is
|
|
90
|
-
:attr:`mergeron.
|
|
101
|
+
:attr:`mergeron.SHRDistributions.UNI`, for Dirichlet-distributed market-shares,
|
|
91
102
|
the default specification is that firm-counts vary between
|
|
92
103
|
2 and 7 firms with each value equally likely.
|
|
93
104
|
|
|
94
105
|
Notes
|
|
95
106
|
-----
|
|
96
|
-
If :attr:`mergeron.gen.ShareSpec.dist_type`:code:` == `:attr:`mergeron.gen.
|
|
107
|
+
If :attr:`mergeron.gen.ShareSpec.dist_type`:code:` == `:attr:`mergeron.gen.SHRDistributions.UNI`,
|
|
97
108
|
then it is infeasible that
|
|
98
|
-
:attr:`mergeron.gen.ShareSpec.recapture_form`:code:` == `:attr:`mergeron.
|
|
109
|
+
:attr:`mergeron.gen.ShareSpec.recapture_form`:code:` == `:attr:`mergeron.RECTypes.OUTIN`.
|
|
99
110
|
In other words, if firm-counts are unspecified, the recapture rate cannot be
|
|
100
111
|
estimated using outside good choice probabilities.
|
|
101
112
|
|
|
102
113
|
For a sample with explicit firm counts, market shares must
|
|
103
114
|
be specified as having a supported Dirichlet distribution
|
|
104
|
-
(see :class:`mergeron.gen.
|
|
115
|
+
(see :class:`mergeron.gen.SHRDistributions`).
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
dist_type: SHRDistributions
|
|
120
|
+
"""See :class:`SHRDistributions`"""
|
|
121
|
+
|
|
122
|
+
dist_parms: ArrayDouble | None = field(
|
|
123
|
+
default=None, eq=cmp_using(eq=np.array_equal)
|
|
124
|
+
)
|
|
125
|
+
"""Parameters for tailoring market-share distribution
|
|
126
|
+
|
|
127
|
+
For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
|
|
128
|
+
for Dirichlet-type distributions, a vector of shape parameters of length
|
|
129
|
+
no less than the length of firm-count weights below; defaults depend on
|
|
130
|
+
type of Dirichlet-distribution specified.
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
firm_counts_weights: ArrayFloat | ArrayINT | None = field(
|
|
134
|
+
default=None, eq=cmp_using(eq=np.array_equal)
|
|
135
|
+
)
|
|
136
|
+
"""Relative or absolute frequencies of firm counts
|
|
137
|
+
|
|
138
|
+
Given frequencies are exogenous to generated market data sample;
|
|
139
|
+
for Dirichlet-type distributions, defaults to FCOUNT_WTS_DEFAULT, which specifies
|
|
140
|
+
firm-counts of 2 to 6 with weights in descending order from 5 to 1.
|
|
105
141
|
|
|
106
142
|
"""
|
|
107
143
|
|
|
108
|
-
|
|
109
|
-
|
|
144
|
+
@firm_counts_weights.validator
|
|
145
|
+
def _check_fcw(_i: ShareSpec, _a: Attribute[ArrayDouble], _v: ArrayDouble) -> None:
|
|
146
|
+
if _v is not None and _i.dist_type == SHRDistributions.UNI:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
"Generated data for markets with specified firm-counts or "
|
|
149
|
+
"varying firm counts are not feasible for market shares "
|
|
150
|
+
"with Uniform distribution. Consider revising the "
|
|
151
|
+
r"distribution type to {SHRDistributions.DIR_FLAT}, which gives "
|
|
152
|
+
"uniformly distributed draws on the :math:`n+1` simplex "
|
|
153
|
+
"for firm-count, :math:`n`."
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
recapture_form: RECTypes = field(default=RECTypes.INOUT)
|
|
157
|
+
"""See :class:`mergeron.RECTypes`"""
|
|
158
|
+
|
|
159
|
+
@recapture_form.validator
|
|
160
|
+
def _check_rf(_i: ShareSpec, _a: Attribute[RECTypes], _v: RECTypes) -> None:
|
|
161
|
+
if _v == RECTypes.OUTIN and _i.dist_type == SHRDistributions.UNI:
|
|
162
|
+
raise ValueError(
|
|
163
|
+
"Market share specification requires estimation of recapture rate from "
|
|
164
|
+
"generated data. Either delete recapture rate specification or set it to None."
|
|
165
|
+
)
|
|
110
166
|
|
|
111
|
-
recapture_rate: float | None
|
|
167
|
+
recapture_rate: float | None = field(default=0.8)
|
|
112
168
|
"""A value between 0 and 1, typically 0.8.
|
|
113
169
|
|
|
114
170
|
:code:`None` if market share specification requires direct generation of
|
|
115
|
-
outside good choice probabilities (:attr:`mergeron.
|
|
171
|
+
outside good choice probabilities (:attr:`mergeron.RECTypes.OUTIN`).
|
|
116
172
|
|
|
117
173
|
The recapture rate is usually calibrated to the numbers-equivalent of the
|
|
118
174
|
HHI threshold for the presumtion of harm from unilateral competitive effects
|
|
@@ -131,35 +187,20 @@ class ShareSpec:
|
|
|
131
187
|
|
|
132
188
|
"""
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
no less than the length of firm-count weights below; defaults depend on
|
|
145
|
-
type of Dirichlet-distribution specified.
|
|
146
|
-
|
|
147
|
-
"""
|
|
148
|
-
firm_counts_weights: (
|
|
149
|
-
ArrayDouble | ArrayBIGINT | ArrayDouble | ArrayBIGINT | None
|
|
150
|
-
) = field(default=None, eq=cmp_using(eq=np.array_equal))
|
|
151
|
-
"""Relative or absolute frequencies of firm counts
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Given frequencies are exogenous to generated market data sample;
|
|
155
|
-
for Dirichlet-type distributions, defaults to FCOUNT_WTS_DEFAULT, which specifies
|
|
156
|
-
firm-counts of 2 to 6 with weights in descending order from 5 to 1.
|
|
157
|
-
|
|
158
|
-
"""
|
|
190
|
+
@recapture_rate.validator
|
|
191
|
+
def _check_rr(_i: ShareSpec, _a: Attribute[float], _v: float) -> None:
|
|
192
|
+
if _v and not (0 < _v <= 1):
|
|
193
|
+
raise ValueError("Recapture rate must lie in the interval, [0, 1).")
|
|
194
|
+
elif _v is None and _i.recapture_form != RECTypes.OUTIN:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"Recapture specification, {_i.recapture_form!r} requires that "
|
|
197
|
+
"the market sample specification inclues a recapture rate in the "
|
|
198
|
+
"interval [0, 1)."
|
|
199
|
+
)
|
|
159
200
|
|
|
160
201
|
|
|
161
202
|
@enum.unique
|
|
162
|
-
class
|
|
203
|
+
class PCMDistributions(enum.StrEnum):
|
|
163
204
|
"""Margin distributions."""
|
|
164
205
|
|
|
165
206
|
UNI = "Uniform"
|
|
@@ -193,13 +234,10 @@ class PCMSpec:
|
|
|
193
234
|
|
|
194
235
|
"""
|
|
195
236
|
|
|
196
|
-
|
|
197
|
-
"""See :class:`
|
|
198
|
-
|
|
199
|
-
dist_type: PCMConstants
|
|
200
|
-
"""See :class:`PCMConstants`"""
|
|
237
|
+
dist_type: PCMDistributions = field(kw_only=False, default=PCMDistributions.UNI)
|
|
238
|
+
"""See :class:`PCMDistributions`"""
|
|
201
239
|
|
|
202
|
-
dist_parms: ArrayDouble | None
|
|
240
|
+
dist_parms: ArrayDouble | None = field(kw_only=False, default=None)
|
|
203
241
|
"""Parameter specification for tailoring PCM distribution
|
|
204
242
|
|
|
205
243
|
For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
|
|
@@ -209,6 +247,38 @@ class PCMSpec:
|
|
|
209
247
|
|
|
210
248
|
"""
|
|
211
249
|
|
|
250
|
+
@dist_parms.validator
|
|
251
|
+
def _check_dp(
|
|
252
|
+
_i: PCMSpec, _a: Attribute[ArrayDouble | None], _v: ArrayDouble | None
|
|
253
|
+
) -> None:
|
|
254
|
+
if _i.dist_type.name.startswith("BETA"):
|
|
255
|
+
if _v is None:
|
|
256
|
+
pass
|
|
257
|
+
elif np.array_equal(_v, DIST_PARMS_DEFAULT):
|
|
258
|
+
raise ValueError(
|
|
259
|
+
f"The distribution parameters, {DIST_PARMS_DEFAULT!r} "
|
|
260
|
+
"are not valid with margin distribution, {_dist_type_pcm!r}"
|
|
261
|
+
)
|
|
262
|
+
elif (
|
|
263
|
+
_i.dist_type == PCMDistributions.BETA and len(_v) != len(("a", "b"))
|
|
264
|
+
) or (
|
|
265
|
+
_i.dist_type == PCMDistributions.BETA_BND
|
|
266
|
+
and len(_v) != len(("mu", "sigma", "max", "min"))
|
|
267
|
+
):
|
|
268
|
+
raise ValueError(
|
|
269
|
+
f"Given number, {len(_v)} of parameters "
|
|
270
|
+
f'for PCM with distribution, "{_i.dist_type}" is incorrect.'
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
elif _i.dist_type == PCMDistributions.EMPR and _v is not None:
|
|
274
|
+
raise ValueError(
|
|
275
|
+
f"Empirical distribution does not require additional parameters; "
|
|
276
|
+
f'"given value, {_v!r} is ignored."'
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
firm2_pcm_constraint: FM2Constants = field(kw_only=False, default=FM2Constants.IID)
|
|
280
|
+
"""See :class:`FM2Constants`"""
|
|
281
|
+
|
|
212
282
|
|
|
213
283
|
@enum.unique
|
|
214
284
|
class SSZConstants(float, enum.ReprEnum):
|
|
@@ -251,111 +321,6 @@ class SSZConstants(float, enum.ReprEnum):
|
|
|
251
321
|
|
|
252
322
|
|
|
253
323
|
# Validators for selected attributes of MarketSpec
|
|
254
|
-
def _share_spec_validator(
|
|
255
|
-
_instance: MarketSpec, _attribute: Attribute[ShareSpec], _value: ShareSpec, /
|
|
256
|
-
) -> None:
|
|
257
|
-
_r_bar = _value.recapture_rate
|
|
258
|
-
if _r_bar and not (0 < _r_bar <= 1):
|
|
259
|
-
raise ValueError("Recapture rate must lie in the interval, [0, 1).")
|
|
260
|
-
|
|
261
|
-
elif _r_bar and _value.recapture_form == RECConstants.OUTIN:
|
|
262
|
-
raise ValueError(
|
|
263
|
-
"Market share specification requires estimation of recapture rate from "
|
|
264
|
-
"generated data. Either delete recapture rate specification or set it to None."
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
if _value.dist_type == SHRConstants.UNI:
|
|
268
|
-
if _value.recapture_form == RECConstants.OUTIN:
|
|
269
|
-
raise ValueError(
|
|
270
|
-
f"Invalid recapture specification, {_value.recapture_form!r} "
|
|
271
|
-
"for market share specification with Uniform distribution. "
|
|
272
|
-
"Redefine the market-sample specification, modifying the ."
|
|
273
|
-
"market-share specification or the recapture specification."
|
|
274
|
-
)
|
|
275
|
-
elif _value.firm_counts_weights is not None:
|
|
276
|
-
raise ValueError(
|
|
277
|
-
"Generated data for markets with specified firm-counts or "
|
|
278
|
-
"varying firm counts are not feasible for market shares "
|
|
279
|
-
"with Uniform distribution. Consider revising the "
|
|
280
|
-
r"distribution type to {SHRConstants.DIR_FLAT}, which gives "
|
|
281
|
-
"uniformly distributed draws on the :math:`n+1` simplex "
|
|
282
|
-
"for firm-count, :math:`n`."
|
|
283
|
-
)
|
|
284
|
-
elif _value.recapture_form != RECConstants.OUTIN and (
|
|
285
|
-
_r_bar is None or not isinstance(_r_bar, float)
|
|
286
|
-
):
|
|
287
|
-
raise ValueError(
|
|
288
|
-
f"Recapture specification, {_value.recapture_form!r} requires that "
|
|
289
|
-
"the market sample specification inclues a recapture rate."
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
def _pcm_spec_validator(
|
|
294
|
-
_instance: MarketSpec, _attribute: Attribute[PCMSpec], _value: PCMSpec, /
|
|
295
|
-
) -> None:
|
|
296
|
-
if (
|
|
297
|
-
_instance.share_spec.recapture_form == RECConstants.FIXED
|
|
298
|
-
and _value.firm2_pcm_constraint == FM2Constants.MNL
|
|
299
|
-
):
|
|
300
|
-
raise ValueError(
|
|
301
|
-
"{} {} {}".format(
|
|
302
|
-
f'Specification of "recapture_form", "{_instance.share_spec.recapture_form}"',
|
|
303
|
-
"requires Firm 2 margin must have property, ",
|
|
304
|
-
f'"{FM2Constants.IID}" or "{FM2Constants.SYM}".',
|
|
305
|
-
)
|
|
306
|
-
)
|
|
307
|
-
elif _value.dist_type.name.startswith("BETA"):
|
|
308
|
-
if _value.dist_parms is None:
|
|
309
|
-
pass
|
|
310
|
-
elif np.array_equal(_value.dist_parms, DIST_PARMS_DEFAULT):
|
|
311
|
-
raise ValueError(
|
|
312
|
-
f"The distribution parameters, {DIST_PARMS_DEFAULT!r} "
|
|
313
|
-
"are not valid with margin distribution, {_dist_type_pcm!r}"
|
|
314
|
-
)
|
|
315
|
-
elif (
|
|
316
|
-
_value.dist_type == PCMConstants.BETA
|
|
317
|
-
and len(_value.dist_parms) != len(("max", "min"))
|
|
318
|
-
) or (
|
|
319
|
-
_value.dist_type == PCMConstants.BETA_BND
|
|
320
|
-
and len(_value.dist_parms) != len(("mu", "sigma", "max", "min"))
|
|
321
|
-
):
|
|
322
|
-
raise ValueError(
|
|
323
|
-
f"Given number, {len(_value.dist_parms)} of parameters "
|
|
324
|
-
f'for PCM with distribution, "{_value.dist_type}" is incorrect.'
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
@define(slots=False)
|
|
329
|
-
class MarketSpec:
|
|
330
|
-
"""Parameter specification for market data generation."""
|
|
331
|
-
|
|
332
|
-
share_spec: ShareSpec = field(
|
|
333
|
-
kw_only=True,
|
|
334
|
-
default=ShareSpec(RECConstants.INOUT, 0.85, SHRConstants.UNI, None, None),
|
|
335
|
-
validator=[validators.instance_of(ShareSpec), _share_spec_validator],
|
|
336
|
-
)
|
|
337
|
-
"""Market-share specification, see :class:`ShareSpec`"""
|
|
338
|
-
|
|
339
|
-
pcm_spec: PCMSpec = field(
|
|
340
|
-
kw_only=True,
|
|
341
|
-
default=PCMSpec(FM2Constants.IID, PCMConstants.UNI, None),
|
|
342
|
-
validator=[validators.instance_of(PCMSpec), _pcm_spec_validator],
|
|
343
|
-
)
|
|
344
|
-
"""Margin specification, see :class:`PCMSpec`"""
|
|
345
|
-
|
|
346
|
-
price_spec: PriceConstants = field(
|
|
347
|
-
kw_only=True,
|
|
348
|
-
default=PriceConstants.SYM,
|
|
349
|
-
validator=validators.instance_of(PriceConstants),
|
|
350
|
-
)
|
|
351
|
-
"""Price specification, see :class:`PriceConstants`"""
|
|
352
|
-
|
|
353
|
-
hsr_filing_test_type: SSZConstants = field(
|
|
354
|
-
kw_only=True,
|
|
355
|
-
default=SSZConstants.ONE,
|
|
356
|
-
validator=validators.instance_of(SSZConstants),
|
|
357
|
-
)
|
|
358
|
-
"""Method for modeling HSR filing threholds, see :class:`SSZConstants`"""
|
|
359
324
|
|
|
360
325
|
|
|
361
326
|
@dataclass(slots=True, frozen=True)
|
|
@@ -460,16 +425,22 @@ class INVResolution(enum.StrEnum):
|
|
|
460
425
|
|
|
461
426
|
@frozen
|
|
462
427
|
class UPPTestRegime:
|
|
428
|
+
"""Configuration for UPP tests."""
|
|
429
|
+
|
|
463
430
|
resolution: INVResolution = field(
|
|
464
|
-
|
|
431
|
+
kw_only=False,
|
|
432
|
+
default=INVResolution.ENFT,
|
|
433
|
+
validator=validators.in_([INVResolution.CLRN, INVResolution.ENFT]),
|
|
465
434
|
)
|
|
435
|
+
"""Whether to test clearance, enforcement, or both."""
|
|
436
|
+
|
|
466
437
|
guppi_aggregator: UPPAggrSelector = field(
|
|
467
|
-
default=UPPAggrSelector.MIN
|
|
468
|
-
)
|
|
469
|
-
divr_aggregator: UPPAggrSelector | None = field(
|
|
470
|
-
default=UPPAggrSelector.MIN,
|
|
471
|
-
validator=validators.instance_of((UPPAggrSelector, type(None))),
|
|
438
|
+
kw_only=False, default=UPPAggrSelector.MIN
|
|
472
439
|
)
|
|
440
|
+
"""Aggregator for GUPPI test."""
|
|
441
|
+
|
|
442
|
+
divr_aggregator: UPPAggrSelector = field(kw_only=False, default=UPPAggrSelector.MIN)
|
|
443
|
+
"""Aggregator for diversion ratio test."""
|
|
473
444
|
|
|
474
445
|
|
|
475
446
|
@dataclass(slots=True, frozen=True)
|
|
@@ -501,13 +472,22 @@ class UPPTestsRaw:
|
|
|
501
472
|
class UPPTestsCounts:
|
|
502
473
|
"""Counts of markets resolved as specified
|
|
503
474
|
|
|
504
|
-
Resolution may be either :attr:`INVResolution.ENFT
|
|
475
|
+
Resolution may be either :attr:`INVResolution.ENFT`,
|
|
476
|
+
:attr:`INVResolution.CLRN`, or :attr:`INVResolution.BOTH`.
|
|
477
|
+
In the case of :attr:`INVResolution.BOTH`, two colums of counts
|
|
478
|
+
are returned: one for each resolution.
|
|
479
|
+
|
|
505
480
|
"""
|
|
506
481
|
|
|
507
482
|
by_firm_count: ArrayBIGINT
|
|
508
483
|
by_delta: ArrayBIGINT
|
|
509
484
|
by_conczone: ArrayBIGINT
|
|
510
|
-
"""Zones are "unoncentrated", "moderately concentrated", and "highly concentrated"
|
|
485
|
+
"""Zones are "unoncentrated", "moderately concentrated", and "highly concentrated",
|
|
486
|
+
with futher detail by HHI and ΔHHI for mergers in the "unconcentrated" and
|
|
487
|
+
"moderately concentrated" zones. See
|
|
488
|
+
:attr:`mergeron.gen.enforcement_stats.HMG_PRESUMPTION_ZONE_MAP` and
|
|
489
|
+
:attr:`mergeron.gen.enforcement_stats.ZONE_VALS` for more detail.
|
|
490
|
+
|
|
511
491
|
"""
|
|
512
492
|
|
|
513
493
|
|