mergeron 2024.739099.2__py3-none-any.whl → 2024.739104.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 +7 -10
- mergeron/core/ftc_merger_investigations_data.py +4 -2
- mergeron/core/guidelines_boundaries.py +11 -11
- mergeron/gen/__init__.py +140 -160
- mergeron/gen/data_generation.py +456 -221
- mergeron/gen/{_data_generation_functions.py → data_generation_functions.py} +224 -133
- mergeron/gen/enforcement_stats.py +28 -30
- mergeron/gen/upp_tests.py +72 -202
- {mergeron-2024.739099.2.dist-info → mergeron-2024.739104.1.dist-info}/METADATA +1 -1
- {mergeron-2024.739099.2.dist-info → mergeron-2024.739104.1.dist-info}/RECORD +11 -12
- mergeron/gen/market_sample.py +0 -143
- {mergeron-2024.739099.2.dist-info → mergeron-2024.739104.1.dist-info}/WHEEL +0 -0
|
@@ -4,13 +4,14 @@ Non-public functions called in data_generation.py
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
from collections.abc import Sequence
|
|
7
8
|
from typing import Literal
|
|
8
9
|
|
|
9
10
|
import numpy as np
|
|
10
11
|
from attrs import evolve
|
|
11
12
|
from numpy.random import SeedSequence
|
|
12
13
|
|
|
13
|
-
from .. import VERSION, ArrayBIGINT, ArrayDouble,
|
|
14
|
+
from .. import VERSION, ArrayBIGINT, ArrayDouble, RECTypes # noqa: TID252
|
|
14
15
|
from ..core.damodaran_margin_data import mgn_data_resampler # noqa: TID252
|
|
15
16
|
from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
16
17
|
DIST_PARMS_DEFAULT,
|
|
@@ -18,23 +19,25 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
18
19
|
prng,
|
|
19
20
|
)
|
|
20
21
|
from . import (
|
|
22
|
+
EMPTY_ARRAY_DEFAULT,
|
|
21
23
|
FCOUNT_WTS_DEFAULT,
|
|
22
24
|
FM2Constants,
|
|
23
25
|
MarginDataSample,
|
|
24
|
-
|
|
26
|
+
PCMDistributions,
|
|
25
27
|
PCMSpec,
|
|
26
|
-
PriceConstants,
|
|
27
28
|
PriceDataSample,
|
|
29
|
+
PriceSpec,
|
|
30
|
+
SeedSequenceData,
|
|
28
31
|
ShareDataSample,
|
|
29
32
|
ShareSpec,
|
|
30
|
-
|
|
33
|
+
SHRDistributions,
|
|
31
34
|
SSZConstants,
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
__version__ = VERSION
|
|
35
38
|
|
|
36
39
|
|
|
37
|
-
def
|
|
40
|
+
def gen_share_data(
|
|
38
41
|
_sample_size: int,
|
|
39
42
|
_share_spec: ShareSpec,
|
|
40
43
|
_fcount_rng_seed_seq: SeedSequence | None,
|
|
@@ -68,8 +71,8 @@ def _gen_share_data(
|
|
|
68
71
|
|
|
69
72
|
_ssz = _sample_size
|
|
70
73
|
|
|
71
|
-
if _dist_type_mktshr ==
|
|
72
|
-
_mkt_share_sample =
|
|
74
|
+
if _dist_type_mktshr == SHRDistributions.UNI:
|
|
75
|
+
_mkt_share_sample = gen_market_shares_uniform(
|
|
73
76
|
_ssz, _dist_parms_mktshr, _mktshr_rng_seed_seq, _nthreads
|
|
74
77
|
)
|
|
75
78
|
|
|
@@ -79,7 +82,7 @@ def _gen_share_data(
|
|
|
79
82
|
if _firm_count_prob_wts is None
|
|
80
83
|
else np.array(_firm_count_prob_wts, dtype=np.float64)
|
|
81
84
|
)
|
|
82
|
-
_mkt_share_sample =
|
|
85
|
+
_mkt_share_sample = gen_market_shares_dirichlet_multimarket(
|
|
83
86
|
_ssz,
|
|
84
87
|
_recapture_form,
|
|
85
88
|
_dist_type_mktshr,
|
|
@@ -97,8 +100,8 @@ def _gen_share_data(
|
|
|
97
100
|
|
|
98
101
|
# If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
|
|
99
102
|
_frmshr_array = _mkt_share_sample.mktshr_array[:, :2]
|
|
100
|
-
_r_bar = _share_spec.recapture_rate or 0.
|
|
101
|
-
if _recapture_form ==
|
|
103
|
+
_r_bar = _share_spec.recapture_rate or 0.8
|
|
104
|
+
if _recapture_form == RECTypes.INOUT:
|
|
102
105
|
_mkt_share_sample = ShareDataSample(
|
|
103
106
|
_mkt_share_sample.mktshr_array,
|
|
104
107
|
_mkt_share_sample.fcounts,
|
|
@@ -109,7 +112,7 @@ def _gen_share_data(
|
|
|
109
112
|
return _mkt_share_sample
|
|
110
113
|
|
|
111
114
|
|
|
112
|
-
def
|
|
115
|
+
def gen_market_shares_uniform(
|
|
113
116
|
_s_size: int = 10**6,
|
|
114
117
|
_dist_parms_mktshr: ArrayDouble | None = DIST_PARMS_DEFAULT,
|
|
115
118
|
_mktshr_rng_seed_seq: SeedSequence | None = None,
|
|
@@ -122,10 +125,10 @@ def _gen_market_shares_uniform(
|
|
|
122
125
|
----------
|
|
123
126
|
_s_size
|
|
124
127
|
size of sample to be drawn
|
|
125
|
-
|
|
126
|
-
market recapture rate
|
|
128
|
+
|
|
127
129
|
_mktshr_rng_seed_seq
|
|
128
130
|
seed for rng, so results can be made replicable
|
|
131
|
+
|
|
129
132
|
_nthreads
|
|
130
133
|
number of threads for random number generation
|
|
131
134
|
|
|
@@ -172,10 +175,10 @@ def _gen_market_shares_uniform(
|
|
|
172
175
|
)
|
|
173
176
|
|
|
174
177
|
|
|
175
|
-
def
|
|
178
|
+
def gen_market_shares_dirichlet_multimarket(
|
|
176
179
|
_s_size: int = 10**6,
|
|
177
|
-
_recapture_form:
|
|
178
|
-
_dist_type_dir:
|
|
180
|
+
_recapture_form: RECTypes = RECTypes.INOUT,
|
|
181
|
+
_dist_type_dir: SHRDistributions = SHRDistributions.DIR_FLAT,
|
|
179
182
|
_dist_parms_dir: ArrayDouble | None = None,
|
|
180
183
|
_firm_count_wts: ArrayDouble | None = None,
|
|
181
184
|
_fcount_rng_seed_seq: SeedSequence | None = None,
|
|
@@ -194,18 +197,22 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
194
197
|
----------
|
|
195
198
|
_s_size
|
|
196
199
|
sample size to be drawn
|
|
197
|
-
|
|
198
|
-
market recapture rate
|
|
200
|
+
|
|
199
201
|
_firm_count_wts
|
|
200
202
|
firm count weights array for sample to be drawn
|
|
203
|
+
|
|
201
204
|
_dist_type_dir
|
|
202
205
|
Whether Dirichlet is Flat or Asymmetric
|
|
206
|
+
|
|
203
207
|
_recapture_form
|
|
204
208
|
r_1 = r_2 if "proportional", otherwise MNL-consistent
|
|
209
|
+
|
|
205
210
|
_fcount_rng_seed_seq
|
|
206
211
|
seed firm count rng, for replicable results
|
|
212
|
+
|
|
207
213
|
_mktshr_rng_seed_seq
|
|
208
214
|
seed market share rng, for replicable results
|
|
215
|
+
|
|
209
216
|
_nthreads
|
|
210
217
|
number of threads for parallelized random number generation
|
|
211
218
|
|
|
@@ -219,7 +226,9 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
219
226
|
FCOUNT_WTS_DEFAULT if _firm_count_wts is None else _firm_count_wts
|
|
220
227
|
)
|
|
221
228
|
|
|
222
|
-
_min_choice_wt =
|
|
229
|
+
_min_choice_wt = (
|
|
230
|
+
0.03 if _dist_type_dir == SHRDistributions.DIR_FLAT_CONSTR else 0.00
|
|
231
|
+
)
|
|
223
232
|
_fcount_keys, _choice_wts = zip(
|
|
224
233
|
*(
|
|
225
234
|
_f
|
|
@@ -237,10 +246,10 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
237
246
|
_dir_alphas_full = (
|
|
238
247
|
[1.0] * _fc_max if _dist_parms_dir is None else _dist_parms_dir[:_fc_max]
|
|
239
248
|
)
|
|
240
|
-
if _dist_type_dir ==
|
|
249
|
+
if _dist_type_dir == SHRDistributions.DIR_ASYM:
|
|
241
250
|
_dir_alphas_full = [2.0] * 6 + [1.5] * 5 + [1.25] * min(7, _fc_max)
|
|
242
251
|
|
|
243
|
-
if _dist_type_dir ==
|
|
252
|
+
if _dist_type_dir == SHRDistributions.DIR_COND:
|
|
244
253
|
|
|
245
254
|
def _gen_dir_alphas(_fcv: int) -> ArrayDouble:
|
|
246
255
|
_dat = [2.5] * 2
|
|
@@ -272,7 +281,7 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
272
281
|
_dir_alphas_test = _gen_dir_alphas(_f_val)
|
|
273
282
|
|
|
274
283
|
try:
|
|
275
|
-
_mktshr_sample_f =
|
|
284
|
+
_mktshr_sample_f = gen_market_shares_dirichlet(
|
|
276
285
|
_dir_alphas_test,
|
|
277
286
|
len(_fcounts_match_rows),
|
|
278
287
|
_recapture_form,
|
|
@@ -310,10 +319,10 @@ def _gen_market_shares_dirichlet_multisample(
|
|
|
310
319
|
)
|
|
311
320
|
|
|
312
321
|
|
|
313
|
-
def
|
|
322
|
+
def gen_market_shares_dirichlet(
|
|
314
323
|
_dir_alphas: ArrayDouble,
|
|
315
324
|
_s_size: int = 10**6,
|
|
316
|
-
_recapture_form:
|
|
325
|
+
_recapture_form: RECTypes = RECTypes.INOUT,
|
|
317
326
|
_mktshr_rng_seed_seq: SeedSequence | None = None,
|
|
318
327
|
_nthreads: int = 16,
|
|
319
328
|
/,
|
|
@@ -324,16 +333,18 @@ def _gen_market_shares_dirichlet(
|
|
|
324
333
|
----------
|
|
325
334
|
_dir_alphas
|
|
326
335
|
Shape parameters for Dirichlet distribution
|
|
336
|
+
|
|
327
337
|
_s_size
|
|
328
338
|
sample size to be drawn
|
|
329
|
-
|
|
330
|
-
market recapture rate
|
|
339
|
+
|
|
331
340
|
_recapture_form
|
|
332
|
-
r_1 = r_2 if
|
|
333
|
-
|
|
341
|
+
r_1 = r_2 if RECTypes.FIXED, otherwise MNL-consistent. If
|
|
342
|
+
RECTypes.OUTIN; the number of columns in the output share array
|
|
334
343
|
is len(_dir_alphas) - 1.
|
|
344
|
+
|
|
335
345
|
_mktshr_rng_seed_seq
|
|
336
346
|
seed market share rng, for replicable results
|
|
347
|
+
|
|
337
348
|
_nthreads
|
|
338
349
|
number of threads for parallelized random number generation
|
|
339
350
|
|
|
@@ -346,7 +357,7 @@ def _gen_market_shares_dirichlet(
|
|
|
346
357
|
if not isinstance(_dir_alphas, np.ndarray):
|
|
347
358
|
_dir_alphas = np.array(_dir_alphas)
|
|
348
359
|
|
|
349
|
-
if _recapture_form ==
|
|
360
|
+
if _recapture_form == RECTypes.OUTIN:
|
|
350
361
|
_dir_alphas = np.concatenate((_dir_alphas, _dir_alphas[-1:]))
|
|
351
362
|
|
|
352
363
|
_mktshr_seed_seq_ch = (
|
|
@@ -380,7 +391,7 @@ def _gen_market_shares_dirichlet(
|
|
|
380
391
|
|
|
381
392
|
# If recapture_form == 'inside_out', further calculations downstream
|
|
382
393
|
_aggregate_purchase_prob = np.nan * np.empty((_s_size, 1))
|
|
383
|
-
if _recapture_form ==
|
|
394
|
+
if _recapture_form == RECTypes.OUTIN:
|
|
384
395
|
_aggregate_purchase_prob = 1 - _mktshr_array[:, [-1]]
|
|
385
396
|
_mktshr_array = _mktshr_array[:, :-1] / _aggregate_purchase_prob
|
|
386
397
|
|
|
@@ -392,18 +403,124 @@ def _gen_market_shares_dirichlet(
|
|
|
392
403
|
)
|
|
393
404
|
|
|
394
405
|
|
|
395
|
-
def
|
|
406
|
+
def gen_divr_array(
|
|
407
|
+
_recapture_form: RECTypes,
|
|
408
|
+
_recapture_rate: float | None,
|
|
409
|
+
_frmshr_array: ArrayDouble,
|
|
410
|
+
_aggregate_purchase_prob: ArrayDouble = EMPTY_ARRAY_DEFAULT,
|
|
411
|
+
/,
|
|
412
|
+
) -> ArrayDouble:
|
|
413
|
+
"""
|
|
414
|
+
Given merging-firm shares and related parameters, return diverion ratios.
|
|
415
|
+
|
|
416
|
+
If recapture is specified as :attr:`mergeron.RECTypes.OUTIN`, then the
|
|
417
|
+
choice-probability for the outside good must be supplied.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
_recapture_form
|
|
422
|
+
Enum specifying Fixed (proportional), Inside-out, or Outside-in
|
|
423
|
+
|
|
424
|
+
_recapture_rate
|
|
425
|
+
If recapture is proportional or inside-out, the recapture rate
|
|
426
|
+
for the firm with the smaller share.
|
|
427
|
+
|
|
428
|
+
_frmshr_array
|
|
429
|
+
Merging-firm shares.
|
|
430
|
+
|
|
431
|
+
_aggregate_purchase_prob
|
|
432
|
+
1 minus probability that the outside good is chosen; converts
|
|
433
|
+
market shares to choice probabilities by multiplication.
|
|
434
|
+
|
|
435
|
+
Raises
|
|
436
|
+
------
|
|
437
|
+
ValueError
|
|
438
|
+
If the firm with the smaller share does not have the larger
|
|
439
|
+
diversion ratio between the merging firms.
|
|
440
|
+
|
|
441
|
+
Returns
|
|
442
|
+
-------
|
|
443
|
+
Merging-firm diversion ratios for mergers in the sample.
|
|
444
|
+
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
_divr_array: ArrayDouble
|
|
448
|
+
if _recapture_form == RECTypes.FIXED:
|
|
449
|
+
_divr_array = _recapture_rate * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
|
|
450
|
+
|
|
451
|
+
else:
|
|
452
|
+
_purchprob_array = _aggregate_purchase_prob * _frmshr_array
|
|
453
|
+
_divr_array = _purchprob_array[:, ::-1] / (1 - _purchprob_array)
|
|
454
|
+
|
|
455
|
+
_divr_assert_test = (
|
|
456
|
+
(np.round(np.einsum("ij->i", _frmshr_array), 15) == 1)
|
|
457
|
+
| (np.argmin(_frmshr_array, axis=1) == np.argmax(_divr_array, axis=1))
|
|
458
|
+
)[:, None]
|
|
459
|
+
if not all(_divr_assert_test):
|
|
460
|
+
raise ValueError(
|
|
461
|
+
"{} {} {} {}".format(
|
|
462
|
+
"Data construction fails tests:",
|
|
463
|
+
"the index of min(s_1, s_2) must equal",
|
|
464
|
+
"the index of max(d_12, d_21), for all draws.",
|
|
465
|
+
"unless frmshr_array sums to 1.00.",
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
return _divr_array
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def gen_margin_price_data(
|
|
396
473
|
_frmshr_array: ArrayDouble,
|
|
397
474
|
_nth_firm_share: ArrayDouble,
|
|
398
475
|
_aggregate_purchase_prob: ArrayDouble,
|
|
399
476
|
_pcm_spec: PCMSpec,
|
|
400
|
-
_price_spec:
|
|
477
|
+
_price_spec: PriceSpec,
|
|
401
478
|
_hsr_filing_test_type: SSZConstants,
|
|
402
479
|
_pcm_rng_seed_seq: SeedSequence,
|
|
403
480
|
_pr_rng_seed_seq: SeedSequence | None = None,
|
|
404
481
|
_nthreads: int = 16,
|
|
405
482
|
/,
|
|
406
483
|
) -> tuple[MarginDataSample, PriceDataSample]:
|
|
484
|
+
"""Generate margin and price data for mergers in the sample.
|
|
485
|
+
|
|
486
|
+
Parameters
|
|
487
|
+
----------
|
|
488
|
+
_frmshr_array
|
|
489
|
+
Merging-firm shares; see :class:`mergeron.gen.ShareSpec`.
|
|
490
|
+
|
|
491
|
+
_nth_firm_share
|
|
492
|
+
Share of the nth firm in the sample.
|
|
493
|
+
|
|
494
|
+
_aggregate_purchase_prob
|
|
495
|
+
1 minus probability that the outside good is chosen; converts
|
|
496
|
+
market shares to choice probabilities by multiplication.
|
|
497
|
+
|
|
498
|
+
_pcm_spec
|
|
499
|
+
Enum specifying whether to use asymmetric or flat margins. see
|
|
500
|
+
:class:`mergeron.gen.PCMSpec`.
|
|
501
|
+
|
|
502
|
+
_price_spec
|
|
503
|
+
Enum specifying whether to use symmetric, positive, or negative
|
|
504
|
+
margins; see :class:`mergeron.gen.PriceSpec`.
|
|
505
|
+
|
|
506
|
+
_hsr_filing_test_type
|
|
507
|
+
Enum specifying restriction, if any, to impose on market data sample
|
|
508
|
+
to model HSR filing requirements; see :class:`mergeron.gen.SSZConstants`.
|
|
509
|
+
|
|
510
|
+
_pcm_rng_seed_seq
|
|
511
|
+
Seed sequence for generating margin data.
|
|
512
|
+
|
|
513
|
+
_pr_rng_seed_seq
|
|
514
|
+
Seed sequence for generating price data.
|
|
515
|
+
|
|
516
|
+
_nthreads
|
|
517
|
+
Number of threads to use in generating price data.
|
|
518
|
+
|
|
519
|
+
Returns
|
|
520
|
+
-------
|
|
521
|
+
Simulated margin- and price-data arrays for mergers in the sample.
|
|
522
|
+
"""
|
|
523
|
+
|
|
407
524
|
_price_array, _price_ratio_array = (
|
|
408
525
|
np.ones_like(_frmshr_array, np.float64),
|
|
409
526
|
np.empty_like(_frmshr_array, np.float64),
|
|
@@ -411,25 +528,25 @@ def _gen_margin_price_data(
|
|
|
411
528
|
|
|
412
529
|
_pr_max_ratio = 5.0
|
|
413
530
|
match _price_spec:
|
|
414
|
-
case
|
|
531
|
+
case PriceSpec.SYM:
|
|
415
532
|
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
416
|
-
case
|
|
533
|
+
case PriceSpec.POS:
|
|
417
534
|
_price_array, _nth_firm_price = (
|
|
418
535
|
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
419
536
|
)
|
|
420
|
-
case
|
|
537
|
+
case PriceSpec.NEG:
|
|
421
538
|
_price_array, _nth_firm_price = (
|
|
422
539
|
np.ceil((1 - _p) * _pr_max_ratio)
|
|
423
540
|
for _p in (_frmshr_array, _nth_firm_share)
|
|
424
541
|
)
|
|
425
|
-
case
|
|
542
|
+
case PriceSpec.ZERO:
|
|
426
543
|
_price_array_gen = prng(_pr_rng_seed_seq).choice(
|
|
427
544
|
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
428
545
|
)
|
|
429
546
|
_price_array = _price_array_gen[:, :2]
|
|
430
547
|
_nth_firm_price = _price_array_gen[:, [2]]
|
|
431
548
|
# del _price_array_gen
|
|
432
|
-
case
|
|
549
|
+
case PriceSpec.CSY:
|
|
433
550
|
# TODO:
|
|
434
551
|
# evolve FM2Constraint (save running MNL test twice); evolve copy of _mkt_sample_spec=1q
|
|
435
552
|
# generate the margin data
|
|
@@ -479,7 +596,7 @@ def _gen_margin_price_data(
|
|
|
479
596
|
f"Specitication of price distribution"
|
|
480
597
|
f' "{_price_spec.value}" is invalid.'
|
|
481
598
|
)
|
|
482
|
-
if _price_spec !=
|
|
599
|
+
if _price_spec != PriceSpec.CSY:
|
|
483
600
|
_margin_data = _gen_margin_data(
|
|
484
601
|
_frmshr_array,
|
|
485
602
|
_price_array,
|
|
@@ -535,94 +652,6 @@ def _gen_margin_price_data(
|
|
|
535
652
|
return _margin_data, PriceDataSample(_price_array, _hsr_filing_test)
|
|
536
653
|
|
|
537
654
|
|
|
538
|
-
# marked for deletion
|
|
539
|
-
def _gen_price_data(
|
|
540
|
-
_frmshr_array: ArrayDouble,
|
|
541
|
-
_nth_firm_share: ArrayDouble,
|
|
542
|
-
_price_spec: PriceConstants,
|
|
543
|
-
_hsr_filing_test_type: SSZConstants,
|
|
544
|
-
_seed_seq: SeedSequence | None = None,
|
|
545
|
-
/,
|
|
546
|
-
) -> PriceDataSample:
|
|
547
|
-
_price_array, _price_ratio_array, _hsr_filing_test = (
|
|
548
|
-
np.ones_like(_frmshr_array, np.float64),
|
|
549
|
-
np.empty_like(_frmshr_array, np.float64),
|
|
550
|
-
np.empty(len(_frmshr_array), bool),
|
|
551
|
-
)
|
|
552
|
-
|
|
553
|
-
_pr_max_ratio = 5.0
|
|
554
|
-
match _price_spec:
|
|
555
|
-
case PriceConstants.SYM:
|
|
556
|
-
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
557
|
-
case PriceConstants.POS:
|
|
558
|
-
_price_array, _nth_firm_price = (
|
|
559
|
-
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
560
|
-
)
|
|
561
|
-
case PriceConstants.NEG:
|
|
562
|
-
_price_array, _nth_firm_price = (
|
|
563
|
-
np.ceil((1 - _p) * _pr_max_ratio)
|
|
564
|
-
for _p in (_frmshr_array, _nth_firm_share)
|
|
565
|
-
)
|
|
566
|
-
case PriceConstants.ZERO:
|
|
567
|
-
_price_array_gen = prng(_seed_seq).choice(
|
|
568
|
-
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
569
|
-
)
|
|
570
|
-
_price_array = _price_array_gen[:, :2]
|
|
571
|
-
_nth_firm_price = _price_array_gen[:, [2]]
|
|
572
|
-
# del _price_array_gen
|
|
573
|
-
case PriceConstants.CSY:
|
|
574
|
-
raise ValueError("Market-wide cost-symmetry is not yet implemented.")
|
|
575
|
-
case _:
|
|
576
|
-
raise ValueError(
|
|
577
|
-
f"Specitication of price distribution"
|
|
578
|
-
f' "{_price_spec.value}" is invalid.'
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
_price_array = _price_array.astype(np.float64)
|
|
582
|
-
_rev_array = _price_array * _frmshr_array
|
|
583
|
-
_nth_firm_rev = _nth_firm_price * _nth_firm_share
|
|
584
|
-
|
|
585
|
-
# Although `_test_rev_ratio_inv` is not fixed at 10%,
|
|
586
|
-
# the ratio has not changed since inception of the HSR filing test,
|
|
587
|
-
# so we treat it as a constant of merger enforcement policy.
|
|
588
|
-
_test_rev_ratio, _test_rev_ratio_inv = 10, 1 / 10
|
|
589
|
-
|
|
590
|
-
match _hsr_filing_test_type:
|
|
591
|
-
case SSZConstants.HSR_TEN:
|
|
592
|
-
# See, https://www.ftc.gov/enforcement/premerger-notification-program/
|
|
593
|
-
# -> Procedures For Submitting Post-Consummation Filings
|
|
594
|
-
# -> Key Elements to Determine Whether a Post Consummation Filing is Required
|
|
595
|
-
# under heading, "Historical Thresholds"
|
|
596
|
-
# Revenue ratio has been 10-to-1 since inception
|
|
597
|
-
# Thus, a simple form of the HSR filing test would impose a 10-to-1
|
|
598
|
-
# ratio restriction on the merging firms' revenues
|
|
599
|
-
_rev_ratio = (_rev_array.min(axis=1) / _rev_array.max(axis=1)).round(4)
|
|
600
|
-
_hsr_filing_test = _rev_ratio >= _test_rev_ratio_inv
|
|
601
|
-
# del _rev_array, _rev_ratio
|
|
602
|
-
case SSZConstants.HSR_NTH:
|
|
603
|
-
# To get around the 10-to-1 ratio restriction, specify that the nth firm test:
|
|
604
|
-
# if the smaller merging firm matches or exceeds the n-th firm in size, and
|
|
605
|
-
# the larger merging firm has at least 10 times the size of the nth firm,
|
|
606
|
-
# the size test is considered met.
|
|
607
|
-
# Alternatively, if the smaller merging firm has 10% or greater share,
|
|
608
|
-
# the value of transaction test is considered met.
|
|
609
|
-
_rev_ratio_to_nth = np.round(np.sort(_rev_array, axis=1) / _nth_firm_rev, 4)
|
|
610
|
-
_hsr_filing_test = (
|
|
611
|
-
np.einsum(
|
|
612
|
-
"ij->i",
|
|
613
|
-
1 * (_rev_ratio_to_nth > [1, _test_rev_ratio]),
|
|
614
|
-
dtype=np.int64,
|
|
615
|
-
)
|
|
616
|
-
== _rev_ratio_to_nth.shape[1]
|
|
617
|
-
) | (_frmshr_array.min(axis=1) >= _test_rev_ratio_inv)
|
|
618
|
-
|
|
619
|
-
case _:
|
|
620
|
-
# Otherwise, all draws meet the filing test
|
|
621
|
-
_hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
622
|
-
|
|
623
|
-
return PriceDataSample(_price_array, _hsr_filing_test)
|
|
624
|
-
|
|
625
|
-
|
|
626
655
|
def _gen_margin_data(
|
|
627
656
|
_frmshr_array: ArrayDouble,
|
|
628
657
|
_price_array: ArrayDouble,
|
|
@@ -645,23 +674,23 @@ def _gen_margin_data(
|
|
|
645
674
|
)
|
|
646
675
|
|
|
647
676
|
_beta_min, _beta_max = [None] * 2 # placeholder
|
|
648
|
-
if _dist_type_pcm ==
|
|
677
|
+
if _dist_type_pcm == PCMDistributions.EMPR:
|
|
649
678
|
_pcm_array = mgn_data_resampler(
|
|
650
679
|
_pcm_array.shape, # type: ignore
|
|
651
680
|
seed_sequence=_pcm_rng_seed_seq,
|
|
652
681
|
)
|
|
653
682
|
else:
|
|
654
|
-
_dist_type = "Uniform" if _dist_type_pcm ==
|
|
655
|
-
if _dist_type_pcm ==
|
|
683
|
+
_dist_type = "Uniform" if _dist_type_pcm == PCMDistributions.UNI else "Beta"
|
|
684
|
+
if _dist_type_pcm == PCMDistributions.BETA:
|
|
656
685
|
if _dist_parms_pcm is None:
|
|
657
686
|
_dist_parms_pcm = np.ones(2, np.float64)
|
|
658
687
|
|
|
659
|
-
elif _dist_type_pcm ==
|
|
688
|
+
elif _dist_type_pcm == PCMDistributions.BETA_BND: # Bounded beta
|
|
660
689
|
if _dist_parms_pcm is None:
|
|
661
690
|
_dist_parms_pcm = np.array([0, 1, 0, 1], np.float64)
|
|
662
691
|
_dist_parms = beta_located_bound(_dist_parms_pcm)
|
|
663
692
|
else:
|
|
664
|
-
# _dist_type_pcm ==
|
|
693
|
+
# _dist_type_pcm == PCMDistributions.UNI
|
|
665
694
|
_dist_parms = (
|
|
666
695
|
DIST_PARMS_DEFAULT if _dist_parms_pcm is None else _dist_parms_pcm
|
|
667
696
|
)
|
|
@@ -676,7 +705,7 @@ def _gen_margin_data(
|
|
|
676
705
|
_pcm_rng.fill()
|
|
677
706
|
del _pcm_rng
|
|
678
707
|
|
|
679
|
-
if _dist_type_pcm ==
|
|
708
|
+
if _dist_type_pcm == PCMDistributions.BETA_BND:
|
|
680
709
|
_beta_min, _beta_max = _dist_parms_pcm[2:]
|
|
681
710
|
_pcm_array = (_beta_max - _beta_min) * _pcm_array + _beta_min
|
|
682
711
|
del _beta_min, _beta_max
|
|
@@ -685,7 +714,7 @@ def _gen_margin_data(
|
|
|
685
714
|
_pcm_array = np.column_stack((_pcm_array,) * _frmshr_array.shape[1])
|
|
686
715
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
687
716
|
# Impose FOCs from profit-maximization with MNL demand
|
|
688
|
-
if _dist_type_pcm ==
|
|
717
|
+
if _dist_type_pcm == PCMDistributions.EMPR:
|
|
689
718
|
print(
|
|
690
719
|
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
691
720
|
"the empirical distribution of margins in the source data. For "
|
|
@@ -767,3 +796,65 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
|
767
796
|
|
|
768
797
|
_bmu, _bsigma, _bmin, _bmax = _dist_parms
|
|
769
798
|
return _beta_located((_bmu - _bmin) / (_bmax - _bmin), _bsigma / (_bmax - _bmin))
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def parse_seed_seq_list(
|
|
802
|
+
_sseq_list: Sequence[SeedSequence] | None,
|
|
803
|
+
_mktshr_dist_type: SHRDistributions,
|
|
804
|
+
_price_spec: PriceSpec,
|
|
805
|
+
/,
|
|
806
|
+
) -> SeedSequenceData:
|
|
807
|
+
"""Initialize RNG seed sequences to ensure independence of distinct random streams.
|
|
808
|
+
|
|
809
|
+
The tuple of SeedSequences, is parsed in the following order
|
|
810
|
+
for generating the relevant random variates:
|
|
811
|
+
1.) quantity shares
|
|
812
|
+
2.) price-cost margins
|
|
813
|
+
3.) firm-counts, if :code:`MarketSpec.share_spec.dist_type` is a Dirichlet distribution
|
|
814
|
+
4.) prices, if :code:`MarketSpec.price_spec ==`:attr:`mergeron.gen.PriceSpec.ZERO`.
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
Parameters
|
|
819
|
+
----------
|
|
820
|
+
_sseq_list
|
|
821
|
+
List of RNG seed sequences
|
|
822
|
+
|
|
823
|
+
_mktshr_dist_type
|
|
824
|
+
Market share distribution type
|
|
825
|
+
|
|
826
|
+
_price_spec
|
|
827
|
+
Price specification
|
|
828
|
+
|
|
829
|
+
Returns
|
|
830
|
+
-------
|
|
831
|
+
Seed sequence data
|
|
832
|
+
|
|
833
|
+
"""
|
|
834
|
+
_fcount_rng_seed_seq: SeedSequence | None = None
|
|
835
|
+
_pr_rng_seed_seq: SeedSequence | None = None
|
|
836
|
+
|
|
837
|
+
if _price_spec == PriceSpec.ZERO:
|
|
838
|
+
_sseq_list, _pr_rng_seed_seq = (
|
|
839
|
+
(_sseq_list[:-1], _sseq_list[-1])
|
|
840
|
+
if _sseq_list
|
|
841
|
+
else (None, SeedSequence(pool_size=8))
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
_seed_count = 2 if _mktshr_dist_type == SHRDistributions.UNI else 3
|
|
845
|
+
|
|
846
|
+
if _sseq_list:
|
|
847
|
+
if len(_sseq_list) < _seed_count:
|
|
848
|
+
raise ValueError(
|
|
849
|
+
f"seed sequence list must contain {_seed_count} seed sequences"
|
|
850
|
+
)
|
|
851
|
+
else:
|
|
852
|
+
_sseq_list = tuple(SeedSequence(pool_size=8) for _ in range(_seed_count))
|
|
853
|
+
|
|
854
|
+
(_mktshr_rng_seed_seq, _pcm_rng_seed_seq, _fcount_rng_seed_seq) = (
|
|
855
|
+
_sseq_list if _seed_count == 3 else (*_sseq_list, None) # type: ignore
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
return SeedSequenceData(
|
|
859
|
+
_mktshr_rng_seed_seq, _pcm_rng_seed_seq, _fcount_rng_seed_seq, _pr_rng_seed_seq
|
|
860
|
+
)
|
|
@@ -21,7 +21,6 @@ from scipy.stats import beta, norm # type: ignore
|
|
|
21
21
|
from .. import ( # noqa: TID252
|
|
22
22
|
_PKG_NAME,
|
|
23
23
|
DATA_DIR,
|
|
24
|
-
TI,
|
|
25
24
|
VERSION,
|
|
26
25
|
ArrayBIGINT,
|
|
27
26
|
ArrayDouble,
|
|
@@ -165,32 +164,6 @@ hhi_delta_ranger, hhi_zone_post_ranger = (
|
|
|
165
164
|
for _f in (HHI_DELTA_KNOTS, HHI_POST_ZONE_KNOTS)
|
|
166
165
|
)
|
|
167
166
|
|
|
168
|
-
HMG_PRESUMPTION_ZONE_DICT = {
|
|
169
|
-
HHI_POST_ZONE_KNOTS[0]: {
|
|
170
|
-
HHI_DELTA_KNOTS[0]: (0, 0, 0),
|
|
171
|
-
HHI_DELTA_KNOTS[1]: (0, 0, 0),
|
|
172
|
-
HHI_DELTA_KNOTS[2]: (0, 0, 0),
|
|
173
|
-
},
|
|
174
|
-
HHI_POST_ZONE_KNOTS[1]: {
|
|
175
|
-
HHI_DELTA_KNOTS[0]: (0, 1, 1),
|
|
176
|
-
HHI_DELTA_KNOTS[1]: (1, 1, 2),
|
|
177
|
-
HHI_DELTA_KNOTS[2]: (1, 1, 2),
|
|
178
|
-
},
|
|
179
|
-
HHI_POST_ZONE_KNOTS[2]: {
|
|
180
|
-
HHI_DELTA_KNOTS[0]: (0, 2, 1),
|
|
181
|
-
HHI_DELTA_KNOTS[1]: (1, 2, 3),
|
|
182
|
-
HHI_DELTA_KNOTS[2]: (2, 2, 4),
|
|
183
|
-
},
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
ZONE_VALS = np.unique(
|
|
187
|
-
np.vstack([
|
|
188
|
-
tuple(HMG_PRESUMPTION_ZONE_DICT[_k].values())
|
|
189
|
-
for _k in HMG_PRESUMPTION_ZONE_DICT
|
|
190
|
-
]),
|
|
191
|
-
axis=0,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
167
|
ZONE_STRINGS = {
|
|
195
168
|
0: R"Green Zone (Safeharbor)",
|
|
196
169
|
1: R"Yellow Zone",
|
|
@@ -234,6 +207,31 @@ ZONE_DETAIL_STRINGS_DELTA_LATEX = {
|
|
|
234
207
|
4: Rf"\Delta HHI \geqslant \text{{{HHI_DELTA_KNOTS[2]} pts.}}",
|
|
235
208
|
}
|
|
236
209
|
|
|
210
|
+
HMG_PRESUMPTION_ZONE_MAP = {
|
|
211
|
+
HHI_POST_ZONE_KNOTS[0]: {
|
|
212
|
+
HHI_DELTA_KNOTS[0]: (0, 0, 0),
|
|
213
|
+
HHI_DELTA_KNOTS[1]: (0, 0, 0),
|
|
214
|
+
HHI_DELTA_KNOTS[2]: (0, 0, 0),
|
|
215
|
+
},
|
|
216
|
+
HHI_POST_ZONE_KNOTS[1]: {
|
|
217
|
+
HHI_DELTA_KNOTS[0]: (0, 1, 1),
|
|
218
|
+
HHI_DELTA_KNOTS[1]: (1, 1, 2),
|
|
219
|
+
HHI_DELTA_KNOTS[2]: (1, 1, 2),
|
|
220
|
+
},
|
|
221
|
+
HHI_POST_ZONE_KNOTS[2]: {
|
|
222
|
+
HHI_DELTA_KNOTS[0]: (0, 2, 1),
|
|
223
|
+
HHI_DELTA_KNOTS[1]: (1, 2, 3),
|
|
224
|
+
HHI_DELTA_KNOTS[2]: (2, 2, 4),
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
ZONE_VALS = np.unique(
|
|
229
|
+
np.vstack([
|
|
230
|
+
tuple(HMG_PRESUMPTION_ZONE_MAP[_k].values()) for _k in HMG_PRESUMPTION_ZONE_MAP
|
|
231
|
+
]),
|
|
232
|
+
axis=0,
|
|
233
|
+
)
|
|
234
|
+
|
|
237
235
|
|
|
238
236
|
def enf_stats_output(
|
|
239
237
|
_data_array_dict: fid.INVData,
|
|
@@ -475,7 +473,7 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
475
473
|
else (_hhi_delta_ranged == _hhi_zone_delta_lim)
|
|
476
474
|
)
|
|
477
475
|
|
|
478
|
-
_zone_val =
|
|
476
|
+
_zone_val = HMG_PRESUMPTION_ZONE_MAP[_hhi_zone_post_lim][
|
|
479
477
|
_hhi_zone_delta_lim
|
|
480
478
|
]
|
|
481
479
|
|
|
@@ -723,8 +721,8 @@ def stats_print_rows(
|
|
|
723
721
|
|
|
724
722
|
|
|
725
723
|
def propn_ci(
|
|
726
|
-
_npos: ArrayINT
|
|
727
|
-
_nobs: ArrayINT
|
|
724
|
+
_npos: ArrayINT | int = 4,
|
|
725
|
+
_nobs: ArrayINT | int = 10,
|
|
728
726
|
/,
|
|
729
727
|
*,
|
|
730
728
|
alpha: float = 0.05,
|