mergeron 2024.738963.0__py3-none-any.whl → 2024.738973.0__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/core/__init__.py +18 -8
- mergeron/core/guidelines_boundaries.py +87 -897
- mergeron/core/guidelines_boundary_functions.py +826 -0
- mergeron/core/{guidelines_boundaries_specialized_functions.py → guidelines_boundary_functions_extra.py} +48 -9
- mergeron/gen/__init__.py +20 -37
- mergeron/gen/{_data_generation_functions_nonpublic.py → _data_generation_functions.py} +77 -19
- mergeron/gen/data_generation.py +17 -14
- mergeron/gen/market_sample.py +79 -0
- mergeron/gen/upp_tests.py +99 -66
- {mergeron-2024.738963.0.dist-info → mergeron-2024.738973.0.dist-info}/METADATA +1 -1
- {mergeron-2024.738963.0.dist-info → mergeron-2024.738973.0.dist-info}/RECORD +12 -10
- {mergeron-2024.738963.0.dist-info → mergeron-2024.738973.0.dist-info}/WHEEL +0 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Specialized methods for defining and analyzing boundaries for Guidelines standards.
|
|
3
3
|
|
|
4
|
-
These methods provide
|
|
5
|
-
to have
|
|
4
|
+
These methods (functions) provide rely on scipy of sympy for core computations,
|
|
5
|
+
and may provide improved precision than core functions, but tend to have
|
|
6
|
+
poor performance
|
|
6
7
|
|
|
7
8
|
"""
|
|
8
9
|
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from dataclasses import dataclass
|
|
9
12
|
from importlib.metadata import version
|
|
10
13
|
from typing import Literal
|
|
11
14
|
|
|
12
15
|
import numpy as np
|
|
13
16
|
from mpmath import mp, mpf # type: ignore
|
|
17
|
+
from numpy.typing import NDArray
|
|
14
18
|
from scipy.spatial.distance import minkowski as distance_function # type: ignore
|
|
15
19
|
from sympy import lambdify, simplify, solve, symbols
|
|
16
20
|
|
|
17
21
|
from .. import _PKG_NAME # noqa: TID252
|
|
18
|
-
from .
|
|
19
|
-
|
|
20
|
-
GuidelinesBoundaryCallable,
|
|
21
|
-
_shrratio_boundary_intcpt,
|
|
22
|
-
lerp,
|
|
23
|
-
)
|
|
22
|
+
from . import GuidelinesBoundary
|
|
23
|
+
from .guidelines_boundary_functions import _shrratio_boundary_intcpt, lerp
|
|
24
24
|
|
|
25
25
|
__version__ = version(_PKG_NAME)
|
|
26
26
|
|
|
@@ -29,7 +29,46 @@ mp.prec = 80
|
|
|
29
29
|
mp.trap_complex = True
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
@dataclass(slots=True, frozen=True)
|
|
33
|
+
class GuidelinesBoundaryCallable:
|
|
34
|
+
boundary_function: Callable[[NDArray[np.float64]], NDArray[np.float64]]
|
|
35
|
+
area: float
|
|
36
|
+
s_naught: float = 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def dh_area_quad(_dh_val: float = 0.01, /, *, prec: int = 9) -> float:
|
|
40
|
+
"""
|
|
41
|
+
Area under the ΔHHI boundary.
|
|
42
|
+
|
|
43
|
+
When the given ΔHHI bound matches a Guidelines safeharbor,
|
|
44
|
+
the area under the boundary is half the intrinsic clearance rate
|
|
45
|
+
for the ΔHHI safeharbor.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
_dh_val
|
|
50
|
+
Merging-firms' ΔHHI bound.
|
|
51
|
+
prec
|
|
52
|
+
Specified precision in decimal places.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
Area under ΔHHI boundary.
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
_dh_val = mpf(f"{_dh_val}")
|
|
61
|
+
_s_naught = (1 - mp.sqrt(1 - 2 * _dh_val)) / 2
|
|
62
|
+
|
|
63
|
+
return round(
|
|
64
|
+
float(
|
|
65
|
+
_s_naught + mp.quad(lambda x: _dh_val / (2 * x), [_s_naught, 1 - _s_naught])
|
|
66
|
+
),
|
|
67
|
+
prec,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def hhi_delta_boundary_qdtr(_dh_val: float = 0.01, /) -> GuidelinesBoundaryCallable:
|
|
33
72
|
"""
|
|
34
73
|
Generate the list of share combination on the ΔHHI boundary.
|
|
35
74
|
|
mergeron/gen/__init__.py
CHANGED
|
@@ -5,23 +5,21 @@ Defines constants and containers for industry data generation and testing
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from importlib.metadata import version
|
|
9
|
-
|
|
10
|
-
from .. import _PKG_NAME, RECConstants, UPPAggrSelector # noqa: TID252
|
|
11
|
-
|
|
12
|
-
__version__ = version(_PKG_NAME)
|
|
13
|
-
|
|
14
|
-
|
|
15
8
|
import enum
|
|
16
9
|
from dataclasses import dataclass
|
|
17
|
-
from
|
|
10
|
+
from importlib.metadata import version
|
|
11
|
+
from typing import ClassVar, Protocol, TypeVar
|
|
18
12
|
|
|
19
13
|
import numpy as np
|
|
20
|
-
from attrs import Attribute, define, field, validators
|
|
14
|
+
from attrs import Attribute, define, field, frozen, validators
|
|
21
15
|
from numpy.typing import NBitBase, NDArray
|
|
22
16
|
|
|
17
|
+
from .. import _PKG_NAME, RECConstants, UPPAggrSelector # noqa: TID252
|
|
23
18
|
from ..core.pseudorandom_numbers import DIST_PARMS_DEFAULT # noqa: TID252
|
|
24
19
|
|
|
20
|
+
__version__ = version(_PKG_NAME)
|
|
21
|
+
|
|
22
|
+
|
|
25
23
|
EMPTY_ARRAY_DEFAULT = np.zeros(2)
|
|
26
24
|
FCOUNT_WTS_DEFAULT = ((_nr := np.arange(1, 6)[::-1]) / _nr.sum()).astype(np.float64)
|
|
27
25
|
|
|
@@ -74,7 +72,7 @@ class SHRConstants(enum.StrEnum):
|
|
|
74
72
|
"""
|
|
75
73
|
|
|
76
74
|
|
|
77
|
-
@
|
|
75
|
+
@frozen
|
|
78
76
|
class ShareSpec:
|
|
79
77
|
"""Market share specification
|
|
80
78
|
|
|
@@ -152,7 +150,7 @@ class FM2Constants(enum.StrEnum):
|
|
|
152
150
|
SYM = "symmetric"
|
|
153
151
|
|
|
154
152
|
|
|
155
|
-
@
|
|
153
|
+
@frozen
|
|
156
154
|
class PCMSpec:
|
|
157
155
|
"""Price-cost margin (PCM) specification
|
|
158
156
|
|
|
@@ -224,18 +222,9 @@ class SSZConstants(float, enum.ReprEnum):
|
|
|
224
222
|
"""When initial set of draws is not restricted in any way."""
|
|
225
223
|
|
|
226
224
|
|
|
227
|
-
# Validators for selected attributes of
|
|
228
|
-
def _sample_size_validator(
|
|
229
|
-
_object: MarketSampleSpec, _attribute: Attribute[int], _value: int, /
|
|
230
|
-
) -> None:
|
|
231
|
-
if _value < 10**6:
|
|
232
|
-
raise ValueError(
|
|
233
|
-
f"Sample size must be no less than {10**6:,d}; got, {_value:,d}."
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
|
|
225
|
+
# Validators for selected attributes of MarketSpec
|
|
237
226
|
def _share_spec_validator(
|
|
238
|
-
_instance:
|
|
227
|
+
_instance: MarketSpec, _attribute: Attribute[ShareSpec], _value: ShareSpec, /
|
|
239
228
|
) -> None:
|
|
240
229
|
_r_bar = _value.recapture_rate
|
|
241
230
|
if _r_bar and not (0 < _r_bar <= 1):
|
|
@@ -274,7 +263,7 @@ def _share_spec_validator(
|
|
|
274
263
|
|
|
275
264
|
|
|
276
265
|
def _pcm_spec_validator(
|
|
277
|
-
_instance:
|
|
266
|
+
_instance: MarketSpec, _attribute: Attribute[PCMSpec], _value: PCMSpec, /
|
|
278
267
|
) -> None:
|
|
279
268
|
if (
|
|
280
269
|
_instance.share_spec.recapture_form == RECConstants.FIXED
|
|
@@ -308,15 +297,10 @@ def _pcm_spec_validator(
|
|
|
308
297
|
)
|
|
309
298
|
|
|
310
299
|
|
|
311
|
-
@define(slots=
|
|
312
|
-
class
|
|
300
|
+
@define(slots=False)
|
|
301
|
+
class MarketSpec:
|
|
313
302
|
"""Parameter specification for market data generation."""
|
|
314
303
|
|
|
315
|
-
sample_size: int = field(
|
|
316
|
-
default=10**6, validator=(validators.instance_of(int), _sample_size_validator)
|
|
317
|
-
)
|
|
318
|
-
"""sample size generated"""
|
|
319
|
-
|
|
320
304
|
share_spec: ShareSpec = field(
|
|
321
305
|
kw_only=True,
|
|
322
306
|
default=ShareSpec(RECConstants.INOUT, 0.855, SHRConstants.UNI, None, None),
|
|
@@ -353,17 +337,16 @@ class INVResolution(enum.StrEnum):
|
|
|
353
337
|
BOTH = "both"
|
|
354
338
|
|
|
355
339
|
|
|
356
|
-
@
|
|
340
|
+
@frozen
|
|
357
341
|
class UPPTestRegime:
|
|
358
342
|
resolution: INVResolution = field(
|
|
359
343
|
default=INVResolution.ENFT, validator=validators.instance_of(INVResolution)
|
|
360
344
|
)
|
|
361
345
|
guppi_aggregator: UPPAggrSelector = field(
|
|
362
|
-
default=UPPAggrSelector.
|
|
346
|
+
default=UPPAggrSelector.MIN, validator=validators.instance_of(UPPAggrSelector)
|
|
363
347
|
)
|
|
364
348
|
divr_aggregator: UPPAggrSelector | None = field(
|
|
365
|
-
default=
|
|
366
|
-
validator=validators.instance_of((UPPAggrSelector, type(None))),
|
|
349
|
+
default=None, validator=validators.instance_of((UPPAggrSelector, type(None)))
|
|
367
350
|
)
|
|
368
351
|
|
|
369
352
|
|
|
@@ -469,7 +452,7 @@ class MarginDataSample:
|
|
|
469
452
|
|
|
470
453
|
@dataclass(slots=True, frozen=True)
|
|
471
454
|
class UPPTestsRaw:
|
|
472
|
-
"""arrays marking test failures and successes
|
|
455
|
+
"""Container for arrays marking test failures and successes
|
|
473
456
|
|
|
474
457
|
A test success is a draw ("market") that meeets the
|
|
475
458
|
specified test criterion, and a test failure is
|
|
@@ -494,9 +477,9 @@ class UPPTestsRaw:
|
|
|
494
477
|
|
|
495
478
|
@dataclass(slots=True, frozen=True)
|
|
496
479
|
class UPPTestsCounts:
|
|
497
|
-
"""
|
|
480
|
+
"""Counts of markets resolved as specified
|
|
498
481
|
|
|
499
|
-
Resolution
|
|
482
|
+
Resolution may be either "enforcement" or "clearance".
|
|
500
483
|
"""
|
|
501
484
|
|
|
502
485
|
by_firm_count: NDArray[np.int64]
|
|
@@ -19,11 +19,12 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
19
19
|
prng,
|
|
20
20
|
)
|
|
21
21
|
from . import (
|
|
22
|
+
EMPTY_ARRAY_DEFAULT,
|
|
22
23
|
FCOUNT_WTS_DEFAULT,
|
|
23
24
|
TF,
|
|
24
25
|
FM2Constants,
|
|
25
26
|
MarginDataSample,
|
|
26
|
-
|
|
27
|
+
MarketSpec,
|
|
27
28
|
PCMConstants,
|
|
28
29
|
PriceDataSample,
|
|
29
30
|
PRIConstants,
|
|
@@ -36,7 +37,8 @@ __version__ = version(_PKG_NAME)
|
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
def _gen_share_data(
|
|
39
|
-
|
|
40
|
+
_sample_size: int,
|
|
41
|
+
_mkt_sample_spec: MarketSpec,
|
|
40
42
|
_fcount_rng_seed_seq: SeedSequence | None,
|
|
41
43
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
42
44
|
_nthreads: int = 16,
|
|
@@ -66,7 +68,7 @@ def _gen_share_data(
|
|
|
66
68
|
for _f in ("recapture_form", "dist_type", "dist_parms", "firm_counts_weights")
|
|
67
69
|
)
|
|
68
70
|
|
|
69
|
-
_ssz =
|
|
71
|
+
_ssz = _sample_size
|
|
70
72
|
|
|
71
73
|
match _dist_type_mktshr:
|
|
72
74
|
case SHRConstants.UNI:
|
|
@@ -396,24 +398,22 @@ def _gen_market_shares_dirichlet(
|
|
|
396
398
|
def _gen_price_data(
|
|
397
399
|
_frmshr_array: NDArray[np.float64],
|
|
398
400
|
_nth_firm_share: NDArray[np.float64],
|
|
399
|
-
_mkt_sample_spec:
|
|
401
|
+
_mkt_sample_spec: MarketSpec,
|
|
400
402
|
_seed_seq: SeedSequence | None = None,
|
|
401
403
|
/,
|
|
402
404
|
) -> PriceDataSample:
|
|
403
|
-
_ssz = len(_frmshr_array)
|
|
404
|
-
|
|
405
405
|
_hsr_filing_test_type = _mkt_sample_spec.hsr_filing_test_type
|
|
406
406
|
|
|
407
407
|
_price_array, _price_ratio_array, _hsr_filing_test = (
|
|
408
408
|
np.ones_like(_frmshr_array, np.float64),
|
|
409
409
|
np.empty_like(_frmshr_array, np.float64),
|
|
410
|
-
np.empty(
|
|
410
|
+
np.empty(len(_frmshr_array), bool),
|
|
411
411
|
)
|
|
412
412
|
|
|
413
413
|
_pr_max_ratio = 5.0
|
|
414
414
|
match _mkt_sample_spec.price_spec:
|
|
415
415
|
case PRIConstants.SYM:
|
|
416
|
-
_nth_firm_price = np.ones((
|
|
416
|
+
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
417
417
|
case PRIConstants.POS:
|
|
418
418
|
_price_array, _nth_firm_price = (
|
|
419
419
|
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
@@ -477,14 +477,14 @@ def _gen_price_data(
|
|
|
477
477
|
# del _nth_firm_rev, _rev_ratio_to_nth
|
|
478
478
|
case _:
|
|
479
479
|
# Otherwise, all draws meet the filing test
|
|
480
|
-
_hsr_filing_test = np.ones(
|
|
480
|
+
_hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
481
481
|
|
|
482
482
|
return PriceDataSample(_price_array, _hsr_filing_test)
|
|
483
483
|
|
|
484
484
|
|
|
485
485
|
def _gen_pcm_data(
|
|
486
486
|
_frmshr_array: NDArray[np.floating[TF]],
|
|
487
|
-
_mkt_sample_spec:
|
|
487
|
+
_mkt_sample_spec: MarketSpec,
|
|
488
488
|
_price_array: NDArray[np.floating[TF]],
|
|
489
489
|
_aggregate_purchase_prob: NDArray[np.floating[TF]],
|
|
490
490
|
_pcm_rng_seed_seq: SeedSequence,
|
|
@@ -504,27 +504,25 @@ def _gen_pcm_data(
|
|
|
504
504
|
_mnl_test_array = np.empty((len(_frmshr_array), 2), dtype=int)
|
|
505
505
|
|
|
506
506
|
_beta_min, _beta_max = [None] * 2 # placeholder
|
|
507
|
-
_dist_parms = np.ones(2, np.float64)
|
|
508
507
|
if _dist_type_pcm == PCMConstants.EMPR:
|
|
509
508
|
_pcm_array = resample_mgn_data(
|
|
510
509
|
_pcm_array.shape, # type: ignore
|
|
511
510
|
seed_sequence=_pcm_rng_seed_seq,
|
|
512
511
|
)
|
|
513
512
|
else:
|
|
514
|
-
if _dist_type_pcm == PCMConstants.
|
|
515
|
-
_dist_parms = (
|
|
516
|
-
DIST_PARMS_DEFAULT if _dist_parms_pcm is None else _dist_parms_pcm
|
|
517
|
-
)
|
|
518
|
-
elif _dist_type_pcm == PCMConstants.BETA:
|
|
519
|
-
# Error-checking (could move to validators in definition of MarketSampleSpec)
|
|
520
|
-
|
|
513
|
+
if _dist_type_pcm == PCMConstants.BETA:
|
|
521
514
|
if _dist_parms_pcm is None:
|
|
522
|
-
_dist_parms_pcm =
|
|
515
|
+
_dist_parms_pcm = np.ones(2, np.float64)
|
|
523
516
|
|
|
524
517
|
elif _dist_type_pcm == PCMConstants.BETA_BND: # Bounded beta
|
|
525
518
|
if _dist_parms_pcm is None:
|
|
526
519
|
_dist_parms_pcm = np.array([0, 1, 0, 1], np.float64)
|
|
527
520
|
_dist_parms = beta_located_bound(_dist_parms_pcm)
|
|
521
|
+
else:
|
|
522
|
+
# _dist_type_pcm == PCMConstants.UNI
|
|
523
|
+
_dist_parms = (
|
|
524
|
+
DIST_PARMS_DEFAULT if _dist_parms_pcm is None else _dist_parms_pcm
|
|
525
|
+
)
|
|
528
526
|
|
|
529
527
|
_pcm_rng = MultithreadedRNG(
|
|
530
528
|
_pcm_array,
|
|
@@ -564,6 +562,66 @@ def _gen_pcm_data(
|
|
|
564
562
|
return MarginDataSample(_pcm_array, _mnl_test_array)
|
|
565
563
|
|
|
566
564
|
|
|
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
|
+
|
|
567
625
|
def _beta_located(
|
|
568
626
|
_mu: float | NDArray[np.float64], _sigma: float | NDArray[np.float64], /
|
|
569
627
|
) -> NDArray[np.float64]:
|
mergeron/gen/data_generation.py
CHANGED
|
@@ -7,7 +7,6 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
from importlib.metadata import version
|
|
9
9
|
|
|
10
|
-
import attrs
|
|
11
10
|
import numpy as np
|
|
12
11
|
from numpy.random import SeedSequence
|
|
13
12
|
from numpy.typing import NDArray
|
|
@@ -17,12 +16,12 @@ from . import (
|
|
|
17
16
|
EMPTY_ARRAY_DEFAULT,
|
|
18
17
|
FM2Constants,
|
|
19
18
|
MarketDataSample,
|
|
20
|
-
|
|
19
|
+
MarketSpec,
|
|
21
20
|
PRIConstants,
|
|
22
21
|
SHRConstants,
|
|
23
22
|
SSZConstants,
|
|
24
23
|
)
|
|
25
|
-
from .
|
|
24
|
+
from ._data_generation_functions import (
|
|
26
25
|
_gen_market_shares_dirichlet, # noqa: F401 easter-egg for external modules
|
|
27
26
|
_gen_market_shares_uniform, # noqa: F401 easter-egg for external modules
|
|
28
27
|
_gen_pcm_data,
|
|
@@ -34,9 +33,10 @@ __version__ = version(_PKG_NAME)
|
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def gen_market_sample(
|
|
37
|
-
_mkt_sample_spec:
|
|
36
|
+
_mkt_sample_spec: MarketSpec,
|
|
38
37
|
/,
|
|
39
38
|
*,
|
|
39
|
+
sample_size: int = 10**6,
|
|
40
40
|
seed_seq_list: list[SeedSequence] | None = None,
|
|
41
41
|
nthreads: int = 16,
|
|
42
42
|
) -> MarketDataSample:
|
|
@@ -59,6 +59,8 @@ def gen_market_sample(
|
|
|
59
59
|
----------
|
|
60
60
|
_mkt_sample_spec
|
|
61
61
|
class specifying parameters for data generation
|
|
62
|
+
sample_size
|
|
63
|
+
number of draws to generate
|
|
62
64
|
seed_seq_list
|
|
63
65
|
tuple of SeedSequences to ensure replicable data generation with
|
|
64
66
|
appropriately independent random streams
|
|
@@ -72,7 +74,7 @@ def gen_market_sample(
|
|
|
72
74
|
|
|
73
75
|
"""
|
|
74
76
|
|
|
75
|
-
_mkt_sample_spec = _mkt_sample_spec or
|
|
77
|
+
_mkt_sample_spec = _mkt_sample_spec or MarketSpec()
|
|
76
78
|
|
|
77
79
|
_recapture_form = _mkt_sample_spec.share_spec.recapture_form
|
|
78
80
|
_recapture_rate = _mkt_sample_spec.share_spec.recapture_rate
|
|
@@ -89,19 +91,20 @@ def gen_market_sample(
|
|
|
89
91
|
seed_seq_list, _dist_type_mktshr, _mkt_sample_spec.price_spec
|
|
90
92
|
)
|
|
91
93
|
|
|
92
|
-
_shr_sample_size = 1.0 *
|
|
94
|
+
_shr_sample_size = 1.0 * sample_size
|
|
93
95
|
# Scale up sample size to offset discards based on specified criteria
|
|
94
96
|
_shr_sample_size *= _hsr_filing_test_type
|
|
95
97
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
96
98
|
_shr_sample_size *= SSZConstants.MNL_DEP
|
|
97
|
-
|
|
98
|
-
_mkt_sample_spec, sample_size=int(_shr_sample_size)
|
|
99
|
-
)
|
|
100
|
-
del _shr_sample_size
|
|
99
|
+
_shr_sample_size = int(_shr_sample_size)
|
|
101
100
|
|
|
102
101
|
# Generate share data
|
|
103
102
|
_mktshr_data = _gen_share_data(
|
|
104
|
-
|
|
103
|
+
_shr_sample_size,
|
|
104
|
+
_mkt_sample_spec,
|
|
105
|
+
_fcount_rng_seed_seq,
|
|
106
|
+
_mktshr_rng_seed_seq,
|
|
107
|
+
nthreads,
|
|
105
108
|
)
|
|
106
109
|
|
|
107
110
|
_mktshr_array, _fcounts, _aggregate_purchase_prob, _nth_firm_share = (
|
|
@@ -116,7 +119,7 @@ def gen_market_sample(
|
|
|
116
119
|
|
|
117
120
|
# Generate merging-firm price data
|
|
118
121
|
_price_data = _gen_price_data(
|
|
119
|
-
_mktshr_array[:, :2], _nth_firm_share,
|
|
122
|
+
_mktshr_array[:, :2], _nth_firm_share, _mkt_sample_spec, _pr_rng_seed_seq
|
|
120
123
|
)
|
|
121
124
|
|
|
122
125
|
_price_array, _hsr_filing_test = (
|
|
@@ -138,7 +141,7 @@ def gen_market_sample(
|
|
|
138
141
|
# Generate margin data
|
|
139
142
|
_pcm_data = _gen_pcm_data(
|
|
140
143
|
_mktshr_array[:, :2],
|
|
141
|
-
|
|
144
|
+
_mkt_sample_spec,
|
|
142
145
|
_price_array,
|
|
143
146
|
_aggregate_purchase_prob,
|
|
144
147
|
_pcm_rng_seed_seq,
|
|
@@ -148,7 +151,7 @@ def gen_market_sample(
|
|
|
148
151
|
getattr(_pcm_data, _f) for _f in ("pcm_array", "mnl_test_array")
|
|
149
152
|
)
|
|
150
153
|
|
|
151
|
-
_s_size =
|
|
154
|
+
_s_size = sample_size # originally-specified sample size
|
|
152
155
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
153
156
|
_mktshr_array = _mktshr_array[_mnl_test_rows][:_s_size]
|
|
154
157
|
_pcm_array = _pcm_array[_mnl_test_rows][:_s_size]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Methods to generate data for analyzing merger enforcement policy.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from importlib.metadata import version
|
|
9
|
+
|
|
10
|
+
from attrs import define
|
|
11
|
+
from numpy.random import SeedSequence
|
|
12
|
+
|
|
13
|
+
from .. import _PKG_NAME # noqa: TID252
|
|
14
|
+
from ..core import guidelines_boundaries as gbl # noqa: TID252
|
|
15
|
+
from . import MarketSpec, UPPTestRegime
|
|
16
|
+
from .data_generation import gen_market_sample
|
|
17
|
+
from .upp_tests import SaveData, invres_cnts, save_data_to_hdf5, sim_invres_cnts_ll
|
|
18
|
+
|
|
19
|
+
__version__ = version(_PKG_NAME)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@define(slots=False)
|
|
23
|
+
class MarketSample(MarketSpec):
|
|
24
|
+
def generate_sample(
|
|
25
|
+
self,
|
|
26
|
+
/,
|
|
27
|
+
*,
|
|
28
|
+
sample_size: int = 10**6,
|
|
29
|
+
seed_seq_list: list[SeedSequence] | None,
|
|
30
|
+
nthreads: int,
|
|
31
|
+
save_data_to_file: SaveData = False,
|
|
32
|
+
) -> None:
|
|
33
|
+
self.data = gen_market_sample(
|
|
34
|
+
self,
|
|
35
|
+
sample_size=sample_size,
|
|
36
|
+
seed_seq_list=seed_seq_list,
|
|
37
|
+
nthreads=nthreads,
|
|
38
|
+
)
|
|
39
|
+
_invalid_array_names = (
|
|
40
|
+
("fcounts", "choice_prob_outgd", "nth_firm_share", "hhi_post")
|
|
41
|
+
if self.share_spec.dist_type == "Uniform"
|
|
42
|
+
else ()
|
|
43
|
+
)
|
|
44
|
+
if save_data_to_file:
|
|
45
|
+
save_data_to_hdf5(
|
|
46
|
+
self.data,
|
|
47
|
+
excluded_attrs=_invalid_array_names,
|
|
48
|
+
save_data_to_file=save_data_to_file,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def estimate_invres_counts(
|
|
52
|
+
self,
|
|
53
|
+
_invres_parm_vec: gbl.HMGThresholds,
|
|
54
|
+
_upp_test_regime: UPPTestRegime,
|
|
55
|
+
/,
|
|
56
|
+
*,
|
|
57
|
+
sample_size: int = 10**6,
|
|
58
|
+
seed_seq_list: list[SeedSequence] | None,
|
|
59
|
+
nthreads: int,
|
|
60
|
+
save_data_to_file: SaveData = False,
|
|
61
|
+
) -> None:
|
|
62
|
+
if getattr(self, "market_data_sample", None) is None:
|
|
63
|
+
self.invres_counts = sim_invres_cnts_ll(
|
|
64
|
+
self,
|
|
65
|
+
_invres_parm_vec,
|
|
66
|
+
_upp_test_regime,
|
|
67
|
+
save_data_to_file=save_data_to_file,
|
|
68
|
+
sample_size=sample_size,
|
|
69
|
+
seed_seq_list=seed_seq_list,
|
|
70
|
+
nthreads=nthreads,
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
self.invres_counts = invres_cnts(
|
|
74
|
+
self.data, _invres_parm_vec, _upp_test_regime
|
|
75
|
+
)
|
|
76
|
+
if save_data_to_file:
|
|
77
|
+
save_data_to_hdf5(
|
|
78
|
+
self.invres_counts, save_data_to_file=save_data_to_file
|
|
79
|
+
)
|