mergeron 2024.739099.1__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/core/guidelines_boundary_functions.py +3 -3
- mergeron/gen/__init__.py +140 -160
- mergeron/gen/data_generation.py +456 -221
- mergeron/gen/{_data_generation_functions.py → data_generation_functions.py} +225 -139
- mergeron/gen/enforcement_stats.py +31 -33
- mergeron/gen/upp_tests.py +70 -212
- {mergeron-2024.739099.1.dist-info → mergeron-2024.739104.1.dist-info}/METADATA +1 -1
- {mergeron-2024.739099.1.dist-info → mergeron-2024.739104.1.dist-info}/RECORD +12 -13
- mergeron/gen/market_sample.py +0 -143
- {mergeron-2024.739099.1.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
|
|
|
@@ -148,7 +151,7 @@ def _gen_market_shares_uniform(
|
|
|
148
151
|
)
|
|
149
152
|
_mrng.fill()
|
|
150
153
|
# Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2 <= 1
|
|
151
|
-
_frmshr_array
|
|
154
|
+
_frmshr_array.sort(axis=1)
|
|
152
155
|
_frmshr_array = np.column_stack((
|
|
153
156
|
_frmshr_array[:, 0],
|
|
154
157
|
_frmshr_array[:, 1] - _frmshr_array[:, 0],
|
|
@@ -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
|
|
@@ -471,11 +588,6 @@ def _gen_margin_price_data(
|
|
|
471
588
|
1 + _m1_nr,
|
|
472
589
|
)
|
|
473
590
|
_mnl_test_array = (_pcm_array[:, [1]] >= 0) & (_pcm_array[:, [1]] <= 1)
|
|
474
|
-
else:
|
|
475
|
-
# Generate i.i.d. PCMs
|
|
476
|
-
# Construct price_array = 1/ (1 - pcm_array)
|
|
477
|
-
# Rgenerate MNL test
|
|
478
|
-
pass
|
|
479
591
|
|
|
480
592
|
_margin_data = MarginDataSample(_pcm_array[:, :2], _mnl_test_array)
|
|
481
593
|
del _price_array_here
|
|
@@ -484,7 +596,7 @@ def _gen_margin_price_data(
|
|
|
484
596
|
f"Specitication of price distribution"
|
|
485
597
|
f' "{_price_spec.value}" is invalid.'
|
|
486
598
|
)
|
|
487
|
-
if _price_spec !=
|
|
599
|
+
if _price_spec != PriceSpec.CSY:
|
|
488
600
|
_margin_data = _gen_margin_data(
|
|
489
601
|
_frmshr_array,
|
|
490
602
|
_price_array,
|
|
@@ -540,94 +652,6 @@ def _gen_margin_price_data(
|
|
|
540
652
|
return _margin_data, PriceDataSample(_price_array, _hsr_filing_test)
|
|
541
653
|
|
|
542
654
|
|
|
543
|
-
# marked for deletion
|
|
544
|
-
def _gen_price_data(
|
|
545
|
-
_frmshr_array: ArrayDouble,
|
|
546
|
-
_nth_firm_share: ArrayDouble,
|
|
547
|
-
_price_spec: PriceConstants,
|
|
548
|
-
_hsr_filing_test_type: SSZConstants,
|
|
549
|
-
_seed_seq: SeedSequence | None = None,
|
|
550
|
-
/,
|
|
551
|
-
) -> PriceDataSample:
|
|
552
|
-
_price_array, _price_ratio_array, _hsr_filing_test = (
|
|
553
|
-
np.ones_like(_frmshr_array, np.float64),
|
|
554
|
-
np.empty_like(_frmshr_array, np.float64),
|
|
555
|
-
np.empty(len(_frmshr_array), bool),
|
|
556
|
-
)
|
|
557
|
-
|
|
558
|
-
_pr_max_ratio = 5.0
|
|
559
|
-
match _price_spec:
|
|
560
|
-
case PriceConstants.SYM:
|
|
561
|
-
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
562
|
-
case PriceConstants.POS:
|
|
563
|
-
_price_array, _nth_firm_price = (
|
|
564
|
-
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
565
|
-
)
|
|
566
|
-
case PriceConstants.NEG:
|
|
567
|
-
_price_array, _nth_firm_price = (
|
|
568
|
-
np.ceil((1 - _p) * _pr_max_ratio)
|
|
569
|
-
for _p in (_frmshr_array, _nth_firm_share)
|
|
570
|
-
)
|
|
571
|
-
case PriceConstants.ZERO:
|
|
572
|
-
_price_array_gen = prng(_seed_seq).choice(
|
|
573
|
-
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
574
|
-
)
|
|
575
|
-
_price_array = _price_array_gen[:, :2]
|
|
576
|
-
_nth_firm_price = _price_array_gen[:, [2]]
|
|
577
|
-
# del _price_array_gen
|
|
578
|
-
case PriceConstants.CSY:
|
|
579
|
-
raise ValueError("Market-wide cost-symmetry is not yet implemented.")
|
|
580
|
-
case _:
|
|
581
|
-
raise ValueError(
|
|
582
|
-
f"Specitication of price distribution"
|
|
583
|
-
f' "{_price_spec.value}" is invalid.'
|
|
584
|
-
)
|
|
585
|
-
|
|
586
|
-
_price_array = _price_array.astype(np.float64)
|
|
587
|
-
_rev_array = _price_array * _frmshr_array
|
|
588
|
-
_nth_firm_rev = _nth_firm_price * _nth_firm_share
|
|
589
|
-
|
|
590
|
-
# Although `_test_rev_ratio_inv` is not fixed at 10%,
|
|
591
|
-
# the ratio has not changed since inception of the HSR filing test,
|
|
592
|
-
# so we treat it as a constant of merger enforcement policy.
|
|
593
|
-
_test_rev_ratio, _test_rev_ratio_inv = 10, 1 / 10
|
|
594
|
-
|
|
595
|
-
match _hsr_filing_test_type:
|
|
596
|
-
case SSZConstants.HSR_TEN:
|
|
597
|
-
# See, https://www.ftc.gov/enforcement/premerger-notification-program/
|
|
598
|
-
# -> Procedures For Submitting Post-Consummation Filings
|
|
599
|
-
# -> Key Elements to Determine Whether a Post Consummation Filing is Required
|
|
600
|
-
# under heading, "Historical Thresholds"
|
|
601
|
-
# Revenue ratio has been 10-to-1 since inception
|
|
602
|
-
# Thus, a simple form of the HSR filing test would impose a 10-to-1
|
|
603
|
-
# ratio restriction on the merging firms' revenues
|
|
604
|
-
_rev_ratio = (_rev_array.min(axis=1) / _rev_array.max(axis=1)).round(4)
|
|
605
|
-
_hsr_filing_test = _rev_ratio >= _test_rev_ratio_inv
|
|
606
|
-
# del _rev_array, _rev_ratio
|
|
607
|
-
case SSZConstants.HSR_NTH:
|
|
608
|
-
# To get around the 10-to-1 ratio restriction, specify that the nth firm test:
|
|
609
|
-
# if the smaller merging firm matches or exceeds the n-th firm in size, and
|
|
610
|
-
# the larger merging firm has at least 10 times the size of the nth firm,
|
|
611
|
-
# the size test is considered met.
|
|
612
|
-
# Alternatively, if the smaller merging firm has 10% or greater share,
|
|
613
|
-
# the value of transaction test is considered met.
|
|
614
|
-
_rev_ratio_to_nth = np.round(np.sort(_rev_array, axis=1) / _nth_firm_rev, 4)
|
|
615
|
-
_hsr_filing_test = (
|
|
616
|
-
np.einsum(
|
|
617
|
-
"ij->i",
|
|
618
|
-
1 * (_rev_ratio_to_nth > [1, _test_rev_ratio]),
|
|
619
|
-
dtype=np.int64,
|
|
620
|
-
)
|
|
621
|
-
== _rev_ratio_to_nth.shape[1]
|
|
622
|
-
) | (_frmshr_array.min(axis=1) >= _test_rev_ratio_inv)
|
|
623
|
-
|
|
624
|
-
case _:
|
|
625
|
-
# Otherwise, all draws meet the filing test
|
|
626
|
-
_hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
627
|
-
|
|
628
|
-
return PriceDataSample(_price_array, _hsr_filing_test)
|
|
629
|
-
|
|
630
|
-
|
|
631
655
|
def _gen_margin_data(
|
|
632
656
|
_frmshr_array: ArrayDouble,
|
|
633
657
|
_price_array: ArrayDouble,
|
|
@@ -650,23 +674,23 @@ def _gen_margin_data(
|
|
|
650
674
|
)
|
|
651
675
|
|
|
652
676
|
_beta_min, _beta_max = [None] * 2 # placeholder
|
|
653
|
-
if _dist_type_pcm ==
|
|
677
|
+
if _dist_type_pcm == PCMDistributions.EMPR:
|
|
654
678
|
_pcm_array = mgn_data_resampler(
|
|
655
679
|
_pcm_array.shape, # type: ignore
|
|
656
680
|
seed_sequence=_pcm_rng_seed_seq,
|
|
657
681
|
)
|
|
658
682
|
else:
|
|
659
|
-
_dist_type = "Uniform" if _dist_type_pcm ==
|
|
660
|
-
if _dist_type_pcm ==
|
|
683
|
+
_dist_type = "Uniform" if _dist_type_pcm == PCMDistributions.UNI else "Beta"
|
|
684
|
+
if _dist_type_pcm == PCMDistributions.BETA:
|
|
661
685
|
if _dist_parms_pcm is None:
|
|
662
686
|
_dist_parms_pcm = np.ones(2, np.float64)
|
|
663
687
|
|
|
664
|
-
elif _dist_type_pcm ==
|
|
688
|
+
elif _dist_type_pcm == PCMDistributions.BETA_BND: # Bounded beta
|
|
665
689
|
if _dist_parms_pcm is None:
|
|
666
690
|
_dist_parms_pcm = np.array([0, 1, 0, 1], np.float64)
|
|
667
691
|
_dist_parms = beta_located_bound(_dist_parms_pcm)
|
|
668
692
|
else:
|
|
669
|
-
# _dist_type_pcm ==
|
|
693
|
+
# _dist_type_pcm == PCMDistributions.UNI
|
|
670
694
|
_dist_parms = (
|
|
671
695
|
DIST_PARMS_DEFAULT if _dist_parms_pcm is None else _dist_parms_pcm
|
|
672
696
|
)
|
|
@@ -681,7 +705,7 @@ def _gen_margin_data(
|
|
|
681
705
|
_pcm_rng.fill()
|
|
682
706
|
del _pcm_rng
|
|
683
707
|
|
|
684
|
-
if _dist_type_pcm ==
|
|
708
|
+
if _dist_type_pcm == PCMDistributions.BETA_BND:
|
|
685
709
|
_beta_min, _beta_max = _dist_parms_pcm[2:]
|
|
686
710
|
_pcm_array = (_beta_max - _beta_min) * _pcm_array + _beta_min
|
|
687
711
|
del _beta_min, _beta_max
|
|
@@ -690,7 +714,7 @@ def _gen_margin_data(
|
|
|
690
714
|
_pcm_array = np.column_stack((_pcm_array,) * _frmshr_array.shape[1])
|
|
691
715
|
if _dist_firm2_pcm == FM2Constants.MNL:
|
|
692
716
|
# Impose FOCs from profit-maximization with MNL demand
|
|
693
|
-
if _dist_type_pcm ==
|
|
717
|
+
if _dist_type_pcm == PCMDistributions.EMPR:
|
|
694
718
|
print(
|
|
695
719
|
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
696
720
|
"the empirical distribution of margins in the source data. For "
|
|
@@ -772,3 +796,65 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
|
772
796
|
|
|
773
797
|
_bmu, _bsigma, _bmin, _bmax = _dist_parms
|
|
774
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
|
+
)
|