mergeron 2024.738972.0__py3-none-any.whl → 2024.739079.9__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 +28 -3
- mergeron/core/__init__.py +2 -67
- mergeron/core/damodaran_margin_data.py +66 -52
- mergeron/core/excel_helper.py +32 -37
- mergeron/core/ftc_merger_investigations_data.py +66 -35
- mergeron/core/guidelines_boundaries.py +256 -1042
- mergeron/core/guidelines_boundary_functions.py +981 -0
- mergeron/core/{guidelines_boundaries_specialized_functions.py → guidelines_boundary_functions_extra.py} +53 -16
- mergeron/core/proportions_tests.py +2 -4
- mergeron/core/pseudorandom_numbers.py +6 -11
- mergeron/data/__init__.py +3 -0
- mergeron/data/damodaran_margin_data.xls +0 -0
- mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
- mergeron/{jinja_LaTex_templates/setup_tikz_tables.tex.jinja2 → data/jinja2_LaTeX_templates/setup_tikz_tables.tex} +45 -50
- mergeron/demo/__init__.py +3 -0
- mergeron/demo/visualize_empirical_margin_distribution.py +88 -0
- mergeron/ext/__init__.py +2 -4
- mergeron/ext/tol_colors.py +3 -3
- mergeron/gen/__init__.py +53 -55
- mergeron/gen/_data_generation_functions.py +28 -93
- mergeron/gen/data_generation.py +20 -24
- mergeron/gen/{investigations_stats.py → enforcement_stats.py} +59 -57
- mergeron/gen/market_sample.py +6 -10
- mergeron/gen/upp_tests.py +29 -26
- mergeron-2024.739079.9.dist-info/METADATA +109 -0
- mergeron-2024.739079.9.dist-info/RECORD +36 -0
- mergeron/core/InCommon RSA Server CA cert chain.pem +0 -68
- mergeron-2024.738972.0.dist-info/METADATA +0 -108
- mergeron-2024.738972.0.dist-info/RECORD +0 -31
- /mergeron/{core → data}/ftc_invdata.msgpack +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/mergeron.cls +0 -0
- /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/mergeron_table_collection_template.tex.jinja2 +0 -0
- {mergeron-2024.738972.0.dist-info → mergeron-2024.739079.9.dist-info}/WHEEL +0 -0
mergeron/gen/__init__.py
CHANGED
|
@@ -7,28 +7,26 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
import enum
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from
|
|
11
|
-
from typing import ClassVar, Protocol, TypeVar
|
|
10
|
+
from typing import ClassVar, Protocol
|
|
12
11
|
|
|
13
12
|
import numpy as np
|
|
14
|
-
from attrs import Attribute, define, field, frozen, validators
|
|
15
|
-
from numpy.typing import
|
|
13
|
+
from attrs import Attribute, cmp_using, define, field, frozen, validators
|
|
14
|
+
from numpy.typing import NDArray
|
|
16
15
|
|
|
17
|
-
from .. import
|
|
16
|
+
from .. import VERSION, RECConstants, UPPAggrSelector # noqa: TID252
|
|
18
17
|
from ..core.pseudorandom_numbers import DIST_PARMS_DEFAULT # noqa: TID252
|
|
19
18
|
|
|
20
|
-
__version__ =
|
|
19
|
+
__version__ = VERSION
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
EMPTY_ARRAY_DEFAULT = np.zeros(2)
|
|
24
|
-
FCOUNT_WTS_DEFAULT =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
TI = TypeVar("TI", bound=NBitBase)
|
|
23
|
+
FCOUNT_WTS_DEFAULT = np.divide(
|
|
24
|
+
(_nr := np.arange(1, 6)[::-1]), _nr.sum(), dtype=np.float64
|
|
25
|
+
)
|
|
28
26
|
|
|
29
27
|
|
|
30
28
|
@enum.unique
|
|
31
|
-
class
|
|
29
|
+
class PriceConstants(tuple[bool, str | None], enum.ReprEnum):
|
|
32
30
|
"""Price specification.
|
|
33
31
|
|
|
34
32
|
Whether prices are symmetric and, if not, the direction of correlation, if any.
|
|
@@ -38,7 +36,7 @@ class PRIConstants(tuple[bool, str | None], enum.ReprEnum):
|
|
|
38
36
|
ZERO = (False, None)
|
|
39
37
|
NEG = (False, "negative share-correlation")
|
|
40
38
|
POS = (False, "positive share-correlation")
|
|
41
|
-
CSY = (False, "market-wide cost-symmetry")
|
|
39
|
+
# TODO: CSY = (False, "market-wide cost-symmetry")
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
@enum.unique
|
|
@@ -46,7 +44,7 @@ class SHRConstants(enum.StrEnum):
|
|
|
46
44
|
"""Market share distributions."""
|
|
47
45
|
|
|
48
46
|
UNI = "Uniform"
|
|
49
|
-
"""Uniform distribution over
|
|
47
|
+
R"""Uniform distribution over :math:`s_1 + s_2 \leqslant 1`"""
|
|
50
48
|
|
|
51
49
|
DIR_FLAT = "Flat Dirichlet"
|
|
52
50
|
"""Shape parameter for all merging-firm-shares is unity (1)"""
|
|
@@ -87,48 +85,56 @@ class ShareSpec:
|
|
|
87
85
|
"""
|
|
88
86
|
|
|
89
87
|
recapture_form: RECConstants
|
|
90
|
-
"""
|
|
88
|
+
"""See :class:`mergeron.RECConstants`"""
|
|
91
89
|
|
|
92
90
|
recapture_rate: float | None
|
|
93
|
-
"""A value between 0 and 1.
|
|
91
|
+
"""A value between 0 and 1, typically 0.8.
|
|
94
92
|
|
|
95
|
-
None if market share specification requires direct generation of
|
|
96
|
-
outside good choice probabilities (RECConstants.OUTIN).
|
|
93
|
+
:code:`None` if market share specification requires direct generation of
|
|
94
|
+
outside good choice probabilities (:attr:`mergeron.RECConstants.OUTIN`).
|
|
97
95
|
|
|
98
96
|
The recapture rate is usually calibrated to the numbers-equivalent of the
|
|
99
|
-
HHI threshold for the presumtion of harm from unilateral
|
|
100
|
-
in published merger guidelines. Accordingly,
|
|
97
|
+
HHI threshold for the presumtion of harm from unilateral competitive effects
|
|
98
|
+
in published merger guidelines. Accordingly, the recapture rate rounded to
|
|
99
|
+
the nearest 5% is:
|
|
100
|
+
|
|
101
|
+
* 0.85, **7-to-6 merger from symmetry**; US Guidelines, 1992, 2023
|
|
102
|
+
* 0.80, **6-to-5 merger to symmetry**; EU Guidelines for horizontal mergers, 2004
|
|
103
|
+
* 0.80, 5-to-4 merger from symmetry
|
|
104
|
+
* 0.80, **5-to-4 merger to symmetry**; US Guidelines, 2010
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* 0.78, **5-to-4 merger to symmetry**; US Guidelines, 2010
|
|
106
|
+
Highlighting indicates hypothetical mergers in the neighborhood of (the boundary of)
|
|
107
|
+
the Guidelines presumption of harm. (In the EU Guidelines, concentration measures serve as
|
|
108
|
+
screens for further investigation, rather than as the basis for presumptions of harm or
|
|
109
|
+
presumptions no harm.)
|
|
107
110
|
|
|
108
|
-
Highlighting indicates hypothetical mergers close to the boundary of the presumption.
|
|
109
111
|
"""
|
|
110
112
|
|
|
111
113
|
dist_type: SHRConstants
|
|
112
|
-
"""
|
|
114
|
+
"""See :class:`mergeron.gen.SHRConstants`"""
|
|
113
115
|
|
|
114
|
-
dist_parms: NDArray[np.float64] | None
|
|
116
|
+
dist_parms: NDArray[np.float64] | None = field(
|
|
117
|
+
default=None, eq=cmp_using(eq=np.array_equal)
|
|
118
|
+
)
|
|
115
119
|
"""Parameters for tailoring market-share distribution
|
|
116
120
|
|
|
117
121
|
For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
|
|
118
|
-
for Beta distribution, shape parameters, defaults to `(1, 1)`;
|
|
119
|
-
for Bounded-Beta distribution, vector of (min, max, mean, std. deviation), non-optional;
|
|
120
122
|
for Dirichlet-type distributions, a vector of shape parameters of length
|
|
121
123
|
no less than the length of firm-count weights below; defaults depend on
|
|
122
124
|
type of Dirichlet-distribution specified.
|
|
123
125
|
|
|
124
126
|
"""
|
|
125
|
-
firm_counts_weights: NDArray[np.float64 | np.int64] | None
|
|
126
|
-
|
|
127
|
+
firm_counts_weights: NDArray[np.float64 | np.int64] | None = field(
|
|
128
|
+
default=None, eq=cmp_using(eq=np.array_equal)
|
|
129
|
+
)
|
|
130
|
+
"""Relative or absolute frequencies of firm counts
|
|
127
131
|
|
|
128
132
|
|
|
129
133
|
Given frequencies are exogenous to generated market data sample;
|
|
130
|
-
defaults to FCOUNT_WTS_DEFAULT, which specifies
|
|
131
|
-
with weights in descending order from 5 to 1.
|
|
134
|
+
for Dirichlet-type distributions, defaults to FCOUNT_WTS_DEFAULT, which specifies
|
|
135
|
+
firm-counts of 2 to 6 with weights in descending order from 5 to 1.
|
|
136
|
+
|
|
137
|
+
"""
|
|
132
138
|
|
|
133
139
|
|
|
134
140
|
@enum.unique
|
|
@@ -167,10 +173,10 @@ class PCMSpec:
|
|
|
167
173
|
"""
|
|
168
174
|
|
|
169
175
|
firm2_pcm_constraint: FM2Constants
|
|
170
|
-
"""See FM2Constants"""
|
|
176
|
+
"""See :class:`mergeron.gen.FM2Constants`"""
|
|
171
177
|
|
|
172
178
|
dist_type: PCMConstants
|
|
173
|
-
"""See PCMConstants"""
|
|
179
|
+
"""See :class:`mergeron.gen.PCMConstants`"""
|
|
174
180
|
|
|
175
181
|
dist_parms: NDArray[np.float64] | None
|
|
176
182
|
"""Parameter specification for tailoring PCM distribution
|
|
@@ -179,6 +185,7 @@ class PCMSpec:
|
|
|
179
185
|
for Beta distribution, shape parameters, defaults to `(1, 1)`;
|
|
180
186
|
for Bounded-Beta distribution, vector of (min, max, mean, std. deviation), non-optional;
|
|
181
187
|
for empirical distribution based on Damodaran margin data, optional, ignored
|
|
188
|
+
|
|
182
189
|
"""
|
|
183
190
|
|
|
184
191
|
|
|
@@ -223,15 +230,6 @@ class SSZConstants(float, enum.ReprEnum):
|
|
|
223
230
|
|
|
224
231
|
|
|
225
232
|
# Validators for selected attributes of MarketSpec
|
|
226
|
-
def _sample_size_validator(
|
|
227
|
-
_object: MarketSpec, _attribute: Attribute[int], _value: int, /
|
|
228
|
-
) -> None:
|
|
229
|
-
if _value < 10**6:
|
|
230
|
-
raise ValueError(
|
|
231
|
-
f"Sample size must be no less than {10**6:,d}; got, {_value:,d}."
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
|
|
235
233
|
def _share_spec_validator(
|
|
236
234
|
_instance: MarketSpec, _attribute: Attribute[ShareSpec], _value: ShareSpec, /
|
|
237
235
|
) -> None:
|
|
@@ -312,31 +310,31 @@ class MarketSpec:
|
|
|
312
310
|
|
|
313
311
|
share_spec: ShareSpec = field(
|
|
314
312
|
kw_only=True,
|
|
315
|
-
default=ShareSpec(RECConstants.INOUT, 0.
|
|
313
|
+
default=ShareSpec(RECConstants.INOUT, 0.85, SHRConstants.UNI, None, None),
|
|
316
314
|
validator=[validators.instance_of(ShareSpec), _share_spec_validator],
|
|
317
315
|
)
|
|
318
|
-
"""Market-share specification, see
|
|
316
|
+
"""Market-share specification, see :class:`mergeron.gen.ShareSpec`"""
|
|
319
317
|
|
|
320
318
|
pcm_spec: PCMSpec = field(
|
|
321
319
|
kw_only=True,
|
|
322
320
|
default=PCMSpec(FM2Constants.IID, PCMConstants.UNI, None),
|
|
323
321
|
validator=[validators.instance_of(PCMSpec), _pcm_spec_validator],
|
|
324
322
|
)
|
|
325
|
-
"""Margin specification, see
|
|
323
|
+
"""Margin specification, see :class:`mergeron.gen.PCMSpec`"""
|
|
326
324
|
|
|
327
|
-
price_spec:
|
|
325
|
+
price_spec: PriceConstants = field(
|
|
328
326
|
kw_only=True,
|
|
329
|
-
default=
|
|
330
|
-
validator=validators.instance_of(
|
|
327
|
+
default=PriceConstants.SYM,
|
|
328
|
+
validator=validators.instance_of(PriceConstants),
|
|
331
329
|
)
|
|
332
|
-
"""Price specification, see
|
|
330
|
+
"""Price specification, see :class:`mergeron.gen.PriceConstants`"""
|
|
333
331
|
|
|
334
332
|
hsr_filing_test_type: SSZConstants = field(
|
|
335
333
|
kw_only=True,
|
|
336
334
|
default=SSZConstants.ONE,
|
|
337
335
|
validator=validators.instance_of(SSZConstants),
|
|
338
336
|
)
|
|
339
|
-
"""Method for modeling HSR filing threholds, see SSZConstants"""
|
|
337
|
+
"""Method for modeling HSR filing threholds, see :class:`mergeron.gen.SSZConstants`"""
|
|
340
338
|
|
|
341
339
|
|
|
342
340
|
@enum.unique
|
|
@@ -465,8 +463,8 @@ class UPPTestsRaw:
|
|
|
465
463
|
|
|
466
464
|
A test success is a draw ("market") that meeets the
|
|
467
465
|
specified test criterion, and a test failure is
|
|
468
|
-
one that does not; test criteria are
|
|
469
|
-
|
|
466
|
+
one that does not; test criteria are evaluated in
|
|
467
|
+
:func:`enforcement_stats.gen_upp_arrays`.
|
|
470
468
|
"""
|
|
471
469
|
|
|
472
470
|
guppi_test_simple: NDArray[np.bool_]
|
|
@@ -488,7 +486,7 @@ class UPPTestsRaw:
|
|
|
488
486
|
class UPPTestsCounts:
|
|
489
487
|
"""Counts of markets resolved as specified
|
|
490
488
|
|
|
491
|
-
Resolution may be either
|
|
489
|
+
Resolution may be either :attr:`INVResolution.ENFT` or :attr:`INVResolution.CLRN`.
|
|
492
490
|
"""
|
|
493
491
|
|
|
494
492
|
by_firm_count: NDArray[np.int64]
|
|
@@ -4,36 +4,33 @@ Non-public functions called in data_generation.py
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from importlib.metadata import version
|
|
8
7
|
from typing import Literal
|
|
9
8
|
|
|
10
9
|
import numpy as np
|
|
11
10
|
from numpy.random import SeedSequence
|
|
12
11
|
from numpy.typing import NDArray
|
|
13
12
|
|
|
14
|
-
from .. import
|
|
15
|
-
from ..core.damodaran_margin_data import
|
|
13
|
+
from .. import VERSION, RECConstants # noqa: TID252
|
|
14
|
+
from ..core.damodaran_margin_data import mgn_data_resampler # noqa: TID252
|
|
16
15
|
from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
17
16
|
DIST_PARMS_DEFAULT,
|
|
18
17
|
MultithreadedRNG,
|
|
19
18
|
prng,
|
|
20
19
|
)
|
|
21
20
|
from . import (
|
|
22
|
-
EMPTY_ARRAY_DEFAULT,
|
|
23
21
|
FCOUNT_WTS_DEFAULT,
|
|
24
|
-
TF,
|
|
25
22
|
FM2Constants,
|
|
26
23
|
MarginDataSample,
|
|
27
24
|
MarketSpec,
|
|
28
25
|
PCMConstants,
|
|
26
|
+
PriceConstants,
|
|
29
27
|
PriceDataSample,
|
|
30
|
-
PRIConstants,
|
|
31
28
|
ShareDataSample,
|
|
32
29
|
SHRConstants,
|
|
33
30
|
SSZConstants,
|
|
34
31
|
)
|
|
35
32
|
|
|
36
|
-
__version__ =
|
|
33
|
+
__version__ = VERSION
|
|
37
34
|
|
|
38
35
|
|
|
39
36
|
def _gen_share_data(
|
|
@@ -100,7 +97,7 @@ def _gen_share_data(
|
|
|
100
97
|
|
|
101
98
|
# If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
|
|
102
99
|
_frmshr_array = _mkt_share_sample.mktshr_array[:, :2]
|
|
103
|
-
_r_bar = _mkt_sample_spec.share_spec.recapture_rate or 0.
|
|
100
|
+
_r_bar = _mkt_sample_spec.share_spec.recapture_rate or 0.85
|
|
104
101
|
if _recapture_form == RECConstants.INOUT:
|
|
105
102
|
_mkt_share_sample = ShareDataSample(
|
|
106
103
|
_mkt_share_sample.mktshr_array,
|
|
@@ -114,7 +111,7 @@ def _gen_share_data(
|
|
|
114
111
|
|
|
115
112
|
def _gen_market_shares_uniform(
|
|
116
113
|
_s_size: int = 10**6,
|
|
117
|
-
_dist_parms_mktshr: NDArray[np.
|
|
114
|
+
_dist_parms_mktshr: NDArray[np.float64] | None = DIST_PARMS_DEFAULT,
|
|
118
115
|
_mktshr_rng_seed_seq: SeedSequence | None = None,
|
|
119
116
|
_nthreads: int = 16,
|
|
120
117
|
/,
|
|
@@ -139,8 +136,8 @@ def _gen_market_shares_uniform(
|
|
|
139
136
|
"""
|
|
140
137
|
|
|
141
138
|
_frmshr_array = np.empty((_s_size, 2), dtype=np.float64)
|
|
142
|
-
_dist_parms_mktshr: NDArray[np.
|
|
143
|
-
DIST_PARMS_DEFAULT if _dist_parms_mktshr is None else _dist_parms_mktshr
|
|
139
|
+
_dist_parms_mktshr: NDArray[np.float64] = (
|
|
140
|
+
DIST_PARMS_DEFAULT if _dist_parms_mktshr is None else _dist_parms_mktshr
|
|
144
141
|
)
|
|
145
142
|
_mrng = MultithreadedRNG(
|
|
146
143
|
_frmshr_array,
|
|
@@ -150,7 +147,7 @@ def _gen_market_shares_uniform(
|
|
|
150
147
|
nthreads=_nthreads,
|
|
151
148
|
)
|
|
152
149
|
_mrng.fill()
|
|
153
|
-
# Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2
|
|
150
|
+
# Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2 <= 1
|
|
154
151
|
_frmshr_array = np.sort(_frmshr_array, axis=1)
|
|
155
152
|
_frmshr_array = np.column_stack((
|
|
156
153
|
_frmshr_array[:, 0],
|
|
@@ -179,8 +176,8 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
179
176
|
_s_size: int = 10**6,
|
|
180
177
|
_recapture_form: RECConstants = RECConstants.INOUT,
|
|
181
178
|
_dist_type_dir: SHRConstants = SHRConstants.DIR_FLAT,
|
|
182
|
-
_dist_parms_dir: NDArray[np.
|
|
183
|
-
_firm_count_wts: NDArray[np.
|
|
179
|
+
_dist_parms_dir: NDArray[np.float64] | None = None,
|
|
180
|
+
_firm_count_wts: NDArray[np.float64] | None = None,
|
|
184
181
|
_fcount_rng_seed_seq: SeedSequence | None = None,
|
|
185
182
|
_mktshr_rng_seed_seq: SeedSequence | None = None,
|
|
186
183
|
_nthreads: int = 16,
|
|
@@ -218,7 +215,7 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
218
215
|
|
|
219
216
|
"""
|
|
220
217
|
|
|
221
|
-
_firm_count_wts: NDArray[np.
|
|
218
|
+
_firm_count_wts: NDArray[np.float64] = (
|
|
222
219
|
FCOUNT_WTS_DEFAULT if _firm_count_wts is None else _firm_count_wts
|
|
223
220
|
)
|
|
224
221
|
|
|
@@ -314,7 +311,7 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
314
311
|
|
|
315
312
|
|
|
316
313
|
def _gen_market_shares_dirichlet(
|
|
317
|
-
_dir_alphas: NDArray[np.
|
|
314
|
+
_dir_alphas: NDArray[np.float64],
|
|
318
315
|
_s_size: int = 10**6,
|
|
319
316
|
_recapture_form: RECConstants = RECConstants.INOUT,
|
|
320
317
|
_mktshr_rng_seed_seq: SeedSequence | None = None,
|
|
@@ -412,18 +409,18 @@ def _gen_price_data(
|
|
|
412
409
|
|
|
413
410
|
_pr_max_ratio = 5.0
|
|
414
411
|
match _mkt_sample_spec.price_spec:
|
|
415
|
-
case
|
|
412
|
+
case PriceConstants.SYM:
|
|
416
413
|
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
417
|
-
case
|
|
414
|
+
case PriceConstants.POS:
|
|
418
415
|
_price_array, _nth_firm_price = (
|
|
419
416
|
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
420
417
|
)
|
|
421
|
-
case
|
|
418
|
+
case PriceConstants.NEG:
|
|
422
419
|
_price_array, _nth_firm_price = (
|
|
423
420
|
np.ceil((1 - _p) * _pr_max_ratio)
|
|
424
421
|
for _p in (_frmshr_array, _nth_firm_share)
|
|
425
422
|
)
|
|
426
|
-
case
|
|
423
|
+
case PriceConstants.ZERO:
|
|
427
424
|
_price_array_gen = prng(_seed_seq).choice(
|
|
428
425
|
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
429
426
|
)
|
|
@@ -458,10 +455,10 @@ def _gen_price_data(
|
|
|
458
455
|
_hsr_filing_test = _rev_ratio >= _test_rev_ratio_inv
|
|
459
456
|
# del _rev_array, _rev_ratio
|
|
460
457
|
case SSZConstants.HSR_NTH:
|
|
461
|
-
# To get around the 10-to-1 ratio restriction, specify that the nth firm
|
|
462
|
-
#
|
|
463
|
-
#
|
|
464
|
-
#
|
|
458
|
+
# To get around the 10-to-1 ratio restriction, specify that the nth firm test:
|
|
459
|
+
# if the smaller merging firm matches or exceeds the n-th firm in size, and
|
|
460
|
+
# the larger merging firm has at least 10 times the size of the nth firm,
|
|
461
|
+
# the size test is considered met.
|
|
465
462
|
# Alternatively, if the smaller merging firm has 10% or greater share,
|
|
466
463
|
# the value of transaction test is considered met.
|
|
467
464
|
_rev_ratio_to_nth = np.round(np.sort(_rev_array, axis=1) / _nth_firm_rev, 4)
|
|
@@ -483,33 +480,31 @@ def _gen_price_data(
|
|
|
483
480
|
|
|
484
481
|
|
|
485
482
|
def _gen_pcm_data(
|
|
486
|
-
_frmshr_array: NDArray[np.
|
|
483
|
+
_frmshr_array: NDArray[np.float64],
|
|
484
|
+
_price_array: NDArray[np.float64],
|
|
485
|
+
_aggregate_purchase_prob: NDArray[np.float64],
|
|
487
486
|
_mkt_sample_spec: MarketSpec,
|
|
488
|
-
_price_array: NDArray[np.floating[TF]],
|
|
489
|
-
_aggregate_purchase_prob: NDArray[np.floating[TF]],
|
|
490
487
|
_pcm_rng_seed_seq: SeedSequence,
|
|
491
488
|
_nthreads: int = 16,
|
|
492
489
|
/,
|
|
493
490
|
) -> MarginDataSample:
|
|
494
|
-
_recapture_form = _mkt_sample_spec.share_spec.recapture_form
|
|
495
491
|
_dist_type_pcm, _dist_firm2_pcm, _dist_parms_pcm = (
|
|
496
492
|
getattr(_mkt_sample_spec.pcm_spec, _f)
|
|
497
493
|
for _f in ("dist_type", "firm2_pcm_constraint", "dist_parms")
|
|
498
494
|
)
|
|
499
|
-
_dist_type: Literal["Beta", "Uniform"] = (
|
|
500
|
-
"Uniform" if _dist_type_pcm == PCMConstants.UNI else "Beta"
|
|
501
|
-
)
|
|
502
495
|
|
|
496
|
+
_dist_type: Literal["Beta", "Uniform"]
|
|
503
497
|
_pcm_array = np.empty((len(_frmshr_array), 2), dtype=np.float64)
|
|
504
498
|
_mnl_test_array = np.empty((len(_frmshr_array), 2), dtype=int)
|
|
505
499
|
|
|
506
500
|
_beta_min, _beta_max = [None] * 2 # placeholder
|
|
507
501
|
if _dist_type_pcm == PCMConstants.EMPR:
|
|
508
|
-
_pcm_array =
|
|
502
|
+
_pcm_array = mgn_data_resampler(
|
|
509
503
|
_pcm_array.shape, # type: ignore
|
|
510
504
|
seed_sequence=_pcm_rng_seed_seq,
|
|
511
505
|
)
|
|
512
506
|
else:
|
|
507
|
+
_dist_type = "Uniform" if _dist_type_pcm == PCMConstants.UNI else "Beta"
|
|
513
508
|
if _dist_type_pcm == PCMConstants.BETA:
|
|
514
509
|
if _dist_parms_pcm is None:
|
|
515
510
|
_dist_parms_pcm = np.ones(2, np.float64)
|
|
@@ -562,66 +557,6 @@ def _gen_pcm_data(
|
|
|
562
557
|
return MarginDataSample(_pcm_array, _mnl_test_array)
|
|
563
558
|
|
|
564
559
|
|
|
565
|
-
def _gen_divr_array(
|
|
566
|
-
_recapture_form: RECConstants,
|
|
567
|
-
_recapture_rate: float | None,
|
|
568
|
-
_frmshr_array: NDArray[np.float64],
|
|
569
|
-
_aggregate_purchase_prob: NDArray[np.float64] = EMPTY_ARRAY_DEFAULT,
|
|
570
|
-
/,
|
|
571
|
-
) -> NDArray[np.float64]:
|
|
572
|
-
"""
|
|
573
|
-
Given merging-firm shares and related parameters, return diverion ratios.
|
|
574
|
-
|
|
575
|
-
If recapture is specified as "Outside-in" (RECConstants.OUTIN), then the
|
|
576
|
-
choice-probability for the outside good must be supplied.
|
|
577
|
-
|
|
578
|
-
Parameters
|
|
579
|
-
----------
|
|
580
|
-
_recapture_form
|
|
581
|
-
Enum specifying Fixed (proportional), Inside-out, or Outside-in
|
|
582
|
-
|
|
583
|
-
_recapture_rate
|
|
584
|
-
If recapture is proportional or inside-out, the recapture rate
|
|
585
|
-
for the firm with the smaller share.
|
|
586
|
-
|
|
587
|
-
_frmshr_array
|
|
588
|
-
Merging-firm shares.
|
|
589
|
-
|
|
590
|
-
_aggregate_purchase_prob
|
|
591
|
-
1 minus probability that the outside good is chosen; converts
|
|
592
|
-
market shares to choice probabilities by multiplication.
|
|
593
|
-
|
|
594
|
-
Returns
|
|
595
|
-
-------
|
|
596
|
-
Merging-firm diversion ratios for mergers in the sample.
|
|
597
|
-
|
|
598
|
-
"""
|
|
599
|
-
|
|
600
|
-
_divr_array: NDArray[np.float64]
|
|
601
|
-
if _recapture_form == RECConstants.FIXED:
|
|
602
|
-
_divr_array = _recapture_rate * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
|
|
603
|
-
|
|
604
|
-
else:
|
|
605
|
-
_purchprob_array = _aggregate_purchase_prob * _frmshr_array
|
|
606
|
-
_divr_array = _purchprob_array[:, ::-1] / (1 - _purchprob_array)
|
|
607
|
-
|
|
608
|
-
_divr_assert_test = (
|
|
609
|
-
(np.round(np.einsum("ij->i", _frmshr_array), 15) == 1)
|
|
610
|
-
| (np.argmin(_frmshr_array, axis=1) == np.argmax(_divr_array, axis=1))
|
|
611
|
-
)[:, None]
|
|
612
|
-
if not all(_divr_assert_test):
|
|
613
|
-
raise ValueError(
|
|
614
|
-
"{} {} {} {}".format(
|
|
615
|
-
"Data construction fails tests:",
|
|
616
|
-
"the index of min(s_1, s_2) must equal",
|
|
617
|
-
"the index of max(d_12, d_21), for all draws.",
|
|
618
|
-
"unless frmshr_array sums to 1.00.",
|
|
619
|
-
)
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
return _divr_array
|
|
623
|
-
|
|
624
|
-
|
|
625
560
|
def _beta_located(
|
|
626
561
|
_mu: float | NDArray[np.float64], _sigma: float | NDArray[np.float64], /
|
|
627
562
|
) -> NDArray[np.float64]:
|
|
@@ -647,7 +582,7 @@ def _beta_located(
|
|
|
647
582
|
return np.array([_mu * _mul, (1 - _mu) * _mul], dtype=np.float64)
|
|
648
583
|
|
|
649
584
|
|
|
650
|
-
def beta_located_bound(_dist_parms: NDArray[np.
|
|
585
|
+
def beta_located_bound(_dist_parms: NDArray[np.float64], /) -> NDArray[np.float64]:
|
|
651
586
|
R"""
|
|
652
587
|
Return shape parameters for a non-standard beta, given the mean, stddev, range
|
|
653
588
|
|
mergeron/gen/data_generation.py
CHANGED
|
@@ -5,31 +5,32 @@ Methods to generate data for analyzing merger enforcement policy.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from typing import NamedTuple
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
from numpy.random import SeedSequence
|
|
12
12
|
from numpy.typing import NDArray
|
|
13
13
|
|
|
14
|
-
from .. import
|
|
14
|
+
from .. import VERSION, RECConstants # noqa: TID252
|
|
15
15
|
from . import (
|
|
16
16
|
EMPTY_ARRAY_DEFAULT,
|
|
17
17
|
FM2Constants,
|
|
18
18
|
MarketDataSample,
|
|
19
19
|
MarketSpec,
|
|
20
|
-
|
|
20
|
+
PriceConstants,
|
|
21
21
|
SHRConstants,
|
|
22
22
|
SSZConstants,
|
|
23
23
|
)
|
|
24
|
-
from ._data_generation_functions import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
_gen_price_data,
|
|
29
|
-
_gen_share_data,
|
|
30
|
-
)
|
|
24
|
+
from ._data_generation_functions import _gen_pcm_data, _gen_price_data, _gen_share_data
|
|
25
|
+
|
|
26
|
+
__version__ = VERSION
|
|
27
|
+
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
class SeedSequenceData(NamedTuple):
|
|
30
|
+
mktshr_rng_seed_seq: SeedSequence
|
|
31
|
+
pcm_rng_seed_seq: SeedSequence
|
|
32
|
+
fcount_rng_seed_seq: SeedSequence | None
|
|
33
|
+
pr_rng_seed_seq: SeedSequence | None
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def gen_market_sample(
|
|
@@ -53,7 +54,7 @@ def gen_market_sample(
|
|
|
53
54
|
2.) price-cost margins
|
|
54
55
|
3.) firm-counts, from :code:`[2, 2 + len(firm_counts_weights)]`,
|
|
55
56
|
weighted by :code:`firm_counts_weights`, where relevant
|
|
56
|
-
4.) prices, if :code:`price_spec ==
|
|
57
|
+
4.) prices, if :code:`price_spec == PriceConstants.ZERO`.
|
|
57
58
|
|
|
58
59
|
Parameters
|
|
59
60
|
----------
|
|
@@ -74,8 +75,6 @@ def gen_market_sample(
|
|
|
74
75
|
|
|
75
76
|
"""
|
|
76
77
|
|
|
77
|
-
_mkt_sample_spec = _mkt_sample_spec or MarketSpec()
|
|
78
|
-
|
|
79
78
|
_recapture_form = _mkt_sample_spec.share_spec.recapture_form
|
|
80
79
|
_recapture_rate = _mkt_sample_spec.share_spec.recapture_rate
|
|
81
80
|
_dist_type_mktshr = _mkt_sample_spec.share_spec.dist_type
|
|
@@ -141,9 +140,9 @@ def gen_market_sample(
|
|
|
141
140
|
# Generate margin data
|
|
142
141
|
_pcm_data = _gen_pcm_data(
|
|
143
142
|
_mktshr_array[:, :2],
|
|
144
|
-
_mkt_sample_spec,
|
|
145
143
|
_price_array,
|
|
146
144
|
_aggregate_purchase_prob,
|
|
145
|
+
_mkt_sample_spec,
|
|
147
146
|
_pcm_rng_seed_seq,
|
|
148
147
|
nthreads,
|
|
149
148
|
)
|
|
@@ -186,14 +185,14 @@ def gen_market_sample(
|
|
|
186
185
|
def parse_seed_seq_list(
|
|
187
186
|
_sseq_list: list[SeedSequence] | None,
|
|
188
187
|
_mktshr_dist_type: SHRConstants,
|
|
189
|
-
_price_spec:
|
|
188
|
+
_price_spec: PriceConstants,
|
|
190
189
|
/,
|
|
191
|
-
) ->
|
|
190
|
+
) -> SeedSequenceData:
|
|
192
191
|
"""Initialize RNG seed sequences to ensure independence of distinct random streams."""
|
|
193
192
|
_fcount_rng_seed_seq: SeedSequence | None = None
|
|
194
193
|
_pr_rng_seed_seq: SeedSequence | None = None
|
|
195
194
|
|
|
196
|
-
if _price_spec ==
|
|
195
|
+
if _price_spec == PriceConstants.ZERO:
|
|
197
196
|
_pr_rng_seed_seq = _sseq_list.pop() if _sseq_list else SeedSequence(pool_size=8)
|
|
198
197
|
|
|
199
198
|
if _mktshr_dist_type == SHRConstants.UNI:
|
|
@@ -212,11 +211,8 @@ def parse_seed_seq_list(
|
|
|
212
211
|
else (SeedSequence(pool_size=8) for _ in range(_seed_count))
|
|
213
212
|
)
|
|
214
213
|
|
|
215
|
-
return (
|
|
216
|
-
_mktshr_rng_seed_seq,
|
|
217
|
-
_pcm_rng_seed_seq,
|
|
218
|
-
_fcount_rng_seed_seq,
|
|
219
|
-
_pr_rng_seed_seq,
|
|
214
|
+
return SeedSequenceData(
|
|
215
|
+
_mktshr_rng_seed_seq, _pcm_rng_seed_seq, _fcount_rng_seed_seq, _pr_rng_seed_seq
|
|
220
216
|
)
|
|
221
217
|
|
|
222
218
|
|
|
@@ -230,7 +226,7 @@ def gen_divr_array(
|
|
|
230
226
|
"""
|
|
231
227
|
Given merging-firm shares and related parameters, return diverion ratios.
|
|
232
228
|
|
|
233
|
-
If recapture is specified as
|
|
229
|
+
If recapture is specified as :attr:`mergeron.RECConstants.OUTIN`, then the
|
|
234
230
|
choice-probability for the outside good must be supplied.
|
|
235
231
|
|
|
236
232
|
Parameters
|