mergeron 2025.739290.3__py3-none-any.whl → 2025.739290.5__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.

@@ -18,7 +18,7 @@ from .. import ( # noqa: TID252
18
18
  ArrayFloat,
19
19
  RECForm,
20
20
  )
21
- from ..core.empirical_margin_distribution import mgn_data_resampler # noqa: TID252
21
+ from ..core.empirical_margin_distribution import margin_data_resampler # noqa: TID252
22
22
  from ..core.pseudorandom_numbers import ( # noqa: TID252
23
23
  DEFAULT_BETA_DIST_PARMS,
24
24
  DEFAULT_DIST_PARMS,
@@ -26,6 +26,7 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
26
26
  prng,
27
27
  )
28
28
  from . import (
29
+ DEFAULT_BETA_BND_DIST_PARMS,
29
30
  DEFAULT_FCOUNT_WTS,
30
31
  FM2Constraint,
31
32
  MarginDataSample,
@@ -69,36 +70,23 @@ def gen_share_data(
69
70
 
70
71
  """
71
72
 
72
- _dist_type_mktshr, _firm_count_prob_wts, _dist_parms_mktshr, _recapture_form = (
73
+ dist_type_mktshr, firm_count_prob_wts, dist_parms_mktshr, recapture_form = (
73
74
  getattr(_share_spec, _f)
74
75
  for _f in ("dist_type", "firm_counts_weights", "dist_parms", "recapture_form")
75
76
  )
76
77
 
77
- _ssz = _sample_size
78
- if not len(_dist_parms_mktshr):
79
- _dist_parms_mktshr = (
80
- DEFAULT_DIST_PARMS
81
- if _dist_type_mktshr == "Uniform"
82
- else np.ones(1 + len(_firm_count_prob_wts), float)
78
+ if dist_type_mktshr == SHRDistribution.UNI:
79
+ mkt_share_sample = gen_market_shares_uniform(
80
+ _sample_size, dist_parms_mktshr, _mktshr_rng_seed_seq, _nthreads
83
81
  )
84
82
 
85
- if _dist_type_mktshr == SHRDistribution.UNI:
86
- _mkt_share_sample = gen_market_shares_uniform(
87
- _ssz, _dist_parms_mktshr, _mktshr_rng_seed_seq, _nthreads
88
- )
89
-
90
- elif _dist_type_mktshr.name.startswith("DIR_"):
91
- _firm_count_prob_wts = (
92
- None
93
- if _firm_count_prob_wts is None
94
- else np.array(_firm_count_prob_wts, float)
95
- )
96
- _mkt_share_sample = _gen_market_shares_dirichlet_multimarket(
97
- _ssz,
98
- _recapture_form,
99
- _dist_type_mktshr,
100
- _dist_parms_mktshr,
101
- _firm_count_prob_wts,
83
+ elif dist_type_mktshr.name.startswith("DIR_"):
84
+ mkt_share_sample = _gen_market_shares_dirichlet_multimarket(
85
+ _sample_size,
86
+ recapture_form,
87
+ dist_type_mktshr,
88
+ dist_parms_mktshr,
89
+ firm_count_prob_wts,
102
90
  _fcount_rng_seed_seq,
103
91
  _mktshr_rng_seed_seq,
104
92
  _nthreads,
@@ -106,21 +94,21 @@ def gen_share_data(
106
94
 
107
95
  else:
108
96
  raise ValueError(
109
- f'Unexpected type, "{_dist_type_mktshr}" for share distribution.'
97
+ f'Unexpected type, "{dist_type_mktshr}" for share distribution.'
110
98
  )
111
99
 
112
100
  # If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
113
- _frmshr_array = _mkt_share_sample.mktshr_array[:, :2]
114
- _r_bar = _share_spec.recapture_ratio or DEFAULT_REC_RATIO
115
- if _recapture_form == RECForm.INOUT:
116
- _mkt_share_sample = ShareDataSample(
117
- _mkt_share_sample.mktshr_array,
118
- _mkt_share_sample.fcounts,
119
- _mkt_share_sample.nth_firm_share,
120
- _r_bar / (1 - (1 - _r_bar) * _frmshr_array.min(axis=1, keepdims=True)),
101
+ frmshr_array = mkt_share_sample.mktshr_array[:, :2]
102
+ r_bar_ = _share_spec.recapture_ratio or DEFAULT_REC_RATIO
103
+ if recapture_form == RECForm.INOUT:
104
+ mkt_share_sample = ShareDataSample(
105
+ mkt_share_sample.mktshr_array,
106
+ mkt_share_sample.fcounts,
107
+ mkt_share_sample.nth_firm_share,
108
+ r_bar_ / (1 - (1 - r_bar_) * frmshr_array.min(axis=1, keepdims=True)),
121
109
  )
122
110
 
123
- return _mkt_share_sample
111
+ return mkt_share_sample
124
112
 
125
113
 
126
114
  def gen_market_shares_uniform(
@@ -149,46 +137,41 @@ def gen_market_shares_uniform(
149
137
 
150
138
  """
151
139
 
152
- _frmshr_array: ArrayDouble = np.empty((_s_size, 2), float)
140
+ frmshr_array: ArrayDouble = np.empty((_s_size, 2), float)
153
141
 
154
- _mrng = MultithreadedRNG(
155
- _frmshr_array,
142
+ MultithreadedRNG(
143
+ frmshr_array,
156
144
  dist_type="Uniform",
157
145
  dist_parms=_dist_parms_mktshr,
158
146
  seed_sequence=_mktshr_rng_seed_seq,
159
147
  nthreads=_nthreads,
160
- )
161
- _mrng.fill()
148
+ ).fill()
162
149
 
163
150
  # Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2 <= 1
164
- _frmshr_array = np.hstack((
165
- _frmshr_array.min(axis=1, keepdims=True),
166
- np.abs(np.diff(_frmshr_array, axis=1)),
151
+ frmshr_array = np.hstack((
152
+ frmshr_array.min(axis=1, keepdims=True),
153
+ np.abs(np.diff(frmshr_array, axis=1)),
167
154
  ))
168
155
 
169
156
  # Keep only share combinations representing feasible mergers
170
157
  # This is a no-op for 64-bit floats, but is necessary for smaller floats
171
- _frmshr_array = _frmshr_array[_frmshr_array.min(axis=1) > 0]
158
+ frmshr_array = frmshr_array[frmshr_array.min(axis=1) > 0]
172
159
 
173
160
  # Let a third column have values of "np.nan", so HHI calculations return "np.nan"
174
- _mktshr_array = np.pad(
175
- _frmshr_array, ((0, 0), (0, 1)), "constant", constant_values=np.nan
161
+ mktshr_array_ = np.pad(
162
+ frmshr_array, ((0, 0), (0, 1)), "constant", constant_values=np.nan
176
163
  )
177
164
 
178
- _fcounts = np.empty((_s_size, 1), np.int64)
179
- _nth_firm_share, _aggregate_purchase_prob = (
180
- np.empty(_fcounts.shape, np.float64)
181
- for _ in ("nth_firm_share", "aggregate_purchase_prob")
165
+ fcounts_, nth_firm_share_, aggregate_purchase_prob_ = (
166
+ np.empty((_s_size, 1), type_) for type_ in (np.uint8, np.float64, np.float64)
182
167
  )
183
168
 
184
- # This array is meant to be ignored, so a sentinel value is fine
185
- _fcounts.fill(-1)
186
-
187
- _nth_firm_share.fill(np.nan)
188
- _aggregate_purchase_prob.fill(np.nan)
169
+ fcounts_.fill(0)
170
+ nth_firm_share_.fill(np.nan)
171
+ aggregate_purchase_prob_.fill(np.nan)
189
172
 
190
173
  return ShareDataSample(
191
- _mktshr_array, _fcounts, _nth_firm_share, _aggregate_purchase_prob
174
+ mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
192
175
  )
193
176
 
194
177
 
@@ -245,83 +228,79 @@ def _gen_market_shares_dirichlet_multimarket(
245
228
  else _firm_count_wts
246
229
  )
247
230
 
248
- _min_choice_wt = 0.03 if _dist_type_dir == SHRDistribution.DIR_FLAT_CONSTR else 0.00
249
- _fcount_keys, _choice_wts = zip(
231
+ min_choice_wt = 0.03 if _dist_type_dir == SHRDistribution.DIR_FLAT_CONSTR else 0.00
232
+ fcount_keys, choice_wts = zip(
250
233
  *(
251
- _f
252
- for _f in zip(
253
- 2 + np.arange(len(_firm_count_wts)),
234
+ f_
235
+ for f_ in zip(
236
+ 2 + np.arange(len(_firm_count_wts), dtype=np.uint8),
254
237
  _firm_count_wts / _firm_count_wts.sum(),
255
238
  strict=True,
256
239
  )
257
- if _f[1] > _min_choice_wt
240
+ if f_[1] > min_choice_wt
258
241
  )
259
242
  )
260
- _choice_wts = _choice_wts / sum(_choice_wts)
243
+ choice_wts /= sum(choice_wts)
261
244
 
262
- _fc_max = _fcount_keys[-1]
263
- _dir_alphas_full = (
264
- _dist_parms_dir[:_fc_max] if len(_dist_parms_dir) else [1.0] * _fc_max
245
+ fc_max = fcount_keys[-1]
246
+ dir_alphas_full = (
247
+ _dist_parms_dir[:fc_max] if len(_dist_parms_dir) else [1.0] * fc_max
265
248
  )
266
249
  if _dist_type_dir == SHRDistribution.DIR_ASYM:
267
- _dir_alphas_full = [2.0] * 6 + [1.5] * 5 + [1.25] * min(7, _fc_max)
250
+ dir_alphas_full = [2.0] * 6 + [1.5] * 5 + [1.25] * min(7, fc_max)
268
251
 
269
252
  if _dist_type_dir == SHRDistribution.DIR_COND:
270
253
 
271
254
  def _gen_dir_alphas(_fcv: int) -> ArrayDouble:
272
- _dat = [2.5] * 2
273
- if _fcv > len(_dat):
274
- _dat += [1.0 / (_fcv - 2)] * (_fcv - 2)
275
- return np.array(_dat, float)
255
+ dat_ = [2.5] * 2
256
+ if _fcv > len(dat_):
257
+ dat_ += [1.0 / (_fcv - 2)] * (_fcv - 2)
258
+ return np.array(dat_, float)
276
259
 
277
260
  else:
278
261
 
279
262
  def _gen_dir_alphas(_fcv: int) -> ArrayDouble:
280
- return np.array(_dir_alphas_full[:_fcv], float)
263
+ return np.array(dir_alphas_full[:_fcv], float)
281
264
 
282
- _fcounts = prng(_fcount_rng_seed_seq).choice(
283
- _fcount_keys, size=(_s_size, 1), p=_choice_wts
265
+ fcounts_ = prng(_fcount_rng_seed_seq).choice(
266
+ fcount_keys, size=(_s_size, 1), p=choice_wts
284
267
  )
285
268
 
286
- _mktshr_seed_seq_ch = (
287
- _mktshr_rng_seed_seq.spawn(len(_fcount_keys))
288
- if isinstance(_mktshr_rng_seed_seq, SeedSequence)
289
- else SeedSequence(pool_size=8).spawn(len(_fcounts))
290
- )
269
+ mktshr_seed_seq_ch = _mktshr_rng_seed_seq.spawn(len(fcount_keys))
291
270
 
292
- _aggregate_purchase_prob, _nth_firm_share = (
271
+ aggregate_purchase_prob_, nth_firm_share_ = (
293
272
  np.empty((_s_size, 1)) for _ in range(2)
294
273
  )
295
- _mktshr_array = np.empty((_s_size, _fc_max), float)
296
- for _f_val, _f_sseq in zip(_fcount_keys, _mktshr_seed_seq_ch, strict=True):
297
- _fcounts_match_rows = np.where(_fcounts == _f_val)[0]
298
- _dir_alphas_test = _gen_dir_alphas(_f_val)
274
+ mktshr_array_ = np.empty((_s_size, fc_max), float)
275
+ for f_val, f_sseq in zip(fcount_keys, mktshr_seed_seq_ch, strict=True):
276
+ fcounts_match_rows = np.where(fcounts_ == f_val)[0]
277
+ dir_alphas_test = _gen_dir_alphas(f_val)
299
278
 
300
279
  try:
301
- _mktshr_sample_f = gen_market_shares_dirichlet(
302
- _dir_alphas_test,
303
- len(_fcounts_match_rows),
280
+ mktshr_sample_f = gen_market_shares_dirichlet(
281
+ dir_alphas_test,
282
+ len(fcounts_match_rows),
304
283
  _recapture_form,
305
- _f_sseq,
284
+ f_sseq,
306
285
  _nthreads,
307
286
  )
308
- except ValueError as _err:
309
- print(_f_val, len(_fcounts_match_rows))
310
- raise _err
287
+ except ValueError as err_:
288
+ print(f_val, len(fcounts_match_rows))
289
+ raise err_
311
290
 
312
291
  # Push data for present sample to parent
313
- _mktshr_array[_fcounts_match_rows] = np.pad(
314
- _mktshr_sample_f.mktshr_array,
315
- ((0, 0), (0, _fc_max - _mktshr_sample_f.mktshr_array.shape[1])),
292
+ mktshr_array_[fcounts_match_rows] = np.pad(
293
+ mktshr_sample_f.mktshr_array,
294
+ ((0, 0), (0, fc_max - mktshr_sample_f.mktshr_array.shape[1])),
316
295
  "constant",
317
296
  )
318
- _aggregate_purchase_prob[_fcounts_match_rows] = (
319
- _mktshr_sample_f.aggregate_purchase_prob
297
+ aggregate_purchase_prob_[fcounts_match_rows] = (
298
+ mktshr_sample_f.aggregate_purchase_prob
320
299
  )
321
- _nth_firm_share[_fcounts_match_rows] = _mktshr_sample_f.nth_firm_share
300
+ nth_firm_share_[fcounts_match_rows] = mktshr_sample_f.nth_firm_share
322
301
 
323
- if (_iss := np.round(np.einsum("ij->", _mktshr_array))) != _s_size or _iss != len(
324
- _mktshr_array
302
+ if (iss_ := np.round(np.einsum("ij->", mktshr_array_))) != _s_size or iss_ != len(
303
+ mktshr_array_
325
304
  ):
326
305
  raise ValueError(
327
306
  "DATA GENERATION ERROR: {} {} {}".format(
@@ -332,7 +311,7 @@ def _gen_market_shares_dirichlet_multimarket(
332
311
  )
333
312
 
334
313
  return ShareDataSample(
335
- _mktshr_array, _fcounts, _nth_firm_share, _aggregate_purchase_prob
314
+ mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
336
315
  )
337
316
 
338
317
 
@@ -377,27 +356,29 @@ def gen_market_shares_dirichlet(
377
356
  if _recapture_form == RECForm.OUTIN:
378
357
  _dir_alphas = np.concatenate((_dir_alphas, _dir_alphas[-1:]))
379
358
 
380
- _mktshr_seed_seq_ch = (
381
- _mktshr_rng_seed_seq
382
- if isinstance(_mktshr_rng_seed_seq, SeedSequence)
383
- else SeedSequence(pool_size=8)
384
- )
385
-
386
- _mktshr_array = np.empty((_s_size, len(_dir_alphas)), float)
387
- _mrng = MultithreadedRNG(
388
- _mktshr_array,
359
+ mktshr_array = np.empty((_s_size, len(_dir_alphas)), float)
360
+ MultithreadedRNG(
361
+ mktshr_array,
389
362
  dist_type="Dirichlet",
390
363
  dist_parms=_dir_alphas,
391
- seed_sequence=_mktshr_seed_seq_ch,
364
+ seed_sequence=_mktshr_rng_seed_seq,
392
365
  nthreads=_nthreads,
393
- )
394
- _mrng.fill()
395
-
396
- if (_iss := np.round(np.einsum("ij->", _mktshr_array))) != _s_size or _iss != len(
397
- _mktshr_array
366
+ ).fill()
367
+ # mrng_ = MultithreadedRNG(
368
+ # mktshr_array,
369
+ # dist_type="Dirichlet",
370
+ # dist_parms=_dir_alphas,
371
+ # seed_sequence=_mktshr_rng_seed_seq,
372
+ # nthreads=_nthreads,
373
+ # )
374
+ # mrng_.fill()
375
+ # del mrng_
376
+
377
+ if (iss_ := np.round(np.einsum("ij->", mktshr_array))) != _s_size or iss_ != len(
378
+ mktshr_array
398
379
  ):
399
- print(_dir_alphas, _iss, repr(_s_size), len(_mktshr_array))
400
- print(repr(_mktshr_array[-10:, :]))
380
+ print(_dir_alphas, iss_, repr(_s_size), len(mktshr_array))
381
+ print(repr(mktshr_array[-10:, :]))
401
382
  raise ValueError(
402
383
  "DATA GENERATION ERROR: {} {} {}".format(
403
384
  "Generation of sample shares is inconsistent:",
@@ -407,17 +388,17 @@ def gen_market_shares_dirichlet(
407
388
  )
408
389
 
409
390
  # If recapture_form == 'inside_out', further calculations downstream
410
- _aggregate_purchase_prob = np.empty((_s_size, 1), float)
411
- _aggregate_purchase_prob.fill(np.nan)
391
+ aggregate_purchase_prob_ = np.empty((_s_size, 1), float)
392
+ aggregate_purchase_prob_.fill(np.nan)
412
393
  if _recapture_form == RECForm.OUTIN:
413
- _aggregate_purchase_prob = 1 - _mktshr_array[:, [-1]] # type: ignore
414
- _mktshr_array = _mktshr_array[:, :-1] / _aggregate_purchase_prob
394
+ aggregate_purchase_prob_ = 1 - mktshr_array[:, [-1]] # type: ignore
395
+ mktshr_array = mktshr_array[:, :-1] / aggregate_purchase_prob_
415
396
 
416
397
  return ShareDataSample(
417
- _mktshr_array,
418
- (_mktshr_array.shape[-1] * np.ones((_s_size, 1))).astype(np.int64),
419
- _mktshr_array[:, [-1]],
420
- _aggregate_purchase_prob,
398
+ mktshr_array,
399
+ (mktshr_array.shape[-1] * np.ones((_s_size, 1))).astype(np.int64),
400
+ mktshr_array[:, [-1]],
401
+ aggregate_purchase_prob_,
421
402
  )
422
403
 
423
404
 
@@ -462,19 +443,19 @@ def gen_divr_array(
462
443
 
463
444
  """
464
445
 
465
- _divr_array: ArrayDouble
446
+ divr_array: ArrayDouble
466
447
  if _recapture_form == RECForm.FIXED:
467
- _divr_array = _recapture_ratio * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
448
+ divr_array = _recapture_ratio * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
468
449
 
469
450
  else:
470
- _purchprob_array = _aggregate_purchase_prob * _frmshr_array
471
- _divr_array = _purchprob_array[:, ::-1] / (1 - _purchprob_array)
451
+ purchprob_array = _aggregate_purchase_prob * _frmshr_array
452
+ divr_array = purchprob_array[:, ::-1] / (1 - purchprob_array)
472
453
 
473
- _divr_assert_test = (
454
+ divr_assert_test = (
474
455
  (np.round(np.einsum("ij->i", _frmshr_array), 15) == 1)
475
- | (np.argmin(_frmshr_array, axis=1) == np.argmax(_divr_array, axis=1))
456
+ | (np.argmin(_frmshr_array, axis=1) == np.argmax(divr_array, axis=1))
476
457
  )[:, None]
477
- if not all(_divr_assert_test):
458
+ if not all(divr_assert_test):
478
459
  raise ValueError(
479
460
  "{} {} {} {}".format(
480
461
  "Data construction fails tests:",
@@ -484,10 +465,10 @@ def gen_divr_array(
484
465
  )
485
466
  )
486
467
 
487
- return _divr_array
468
+ return divr_array
488
469
 
489
470
 
490
- def gen_margin_price_data(
471
+ def gen_margin_price_data( # noqa: PLR0914
491
472
  _frmshr_array: ArrayDouble,
492
473
  _nth_firm_share: ArrayDouble,
493
474
  _aggregate_purchase_prob: ArrayDouble,
@@ -539,86 +520,86 @@ def gen_margin_price_data(
539
520
  Simulated margin- and price-data arrays for mergers in the sample.
540
521
  """
541
522
 
542
- _margin_data = MarginDataSample(
523
+ margin_data = MarginDataSample(
543
524
  np.empty_like(_frmshr_array), np.ones(len(_frmshr_array)) == 0
544
525
  )
545
526
 
546
- _nth_firm_price: ArrayDouble
547
- _price_array: ArrayDouble = np.ones_like(_frmshr_array, np.float64)
527
+ nth_firm_price: ArrayDouble
528
+ price_array: ArrayDouble = np.ones_like(_frmshr_array, np.float64)
548
529
 
549
- _pr_max_ratio = 5.0
530
+ pr_max_ratio = 5.0
550
531
  match _price_spec:
551
532
  case PriceSpec.SYM:
552
- _nth_firm_price = np.ones((len(_frmshr_array), 1), np.float64)
533
+ nth_firm_price = np.ones((len(_frmshr_array), 1), np.float64)
553
534
  case PriceSpec.POS:
554
- _price_array, _nth_firm_price = (
555
- np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
535
+ price_array, nth_firm_price = (
536
+ np.ceil(_p * pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
556
537
  )
557
538
  case PriceSpec.NEG:
558
- _price_array, _nth_firm_price = (
559
- np.ceil((1 - _p) * _pr_max_ratio)
539
+ price_array, nth_firm_price = (
540
+ np.ceil((1 - _p) * pr_max_ratio)
560
541
  for _p in (_frmshr_array, _nth_firm_share)
561
542
  )
562
- case PriceSpec.ZERO:
563
- _price_array_gen = prng(_pr_rng_seed_seq).choice(
564
- 1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
543
+ case PriceSpec.RNG:
544
+ price_array_gen = prng(_pr_rng_seed_seq).choice(
545
+ 1 + np.arange(pr_max_ratio), size=(len(_frmshr_array), 3)
565
546
  )
566
- _price_array = _price_array_gen[:, :2]
567
- _nth_firm_price = _price_array_gen[:, [2]]
547
+ price_array = price_array_gen[:, :2]
548
+ nth_firm_price = price_array_gen[:, [2]]
568
549
  # del _price_array_gen
569
550
  case PriceSpec.CSY:
570
551
  # TODO:
571
552
  # evolve FM2Constraint (save running MNL test twice); evolve copy of _mkt_sample_spec=1q
572
553
  # generate the margin data
573
554
  # generate price and margin data
574
- _frmshr_array_plus = np.hstack((_frmshr_array, _nth_firm_share))
575
- _pcm_spec_here = evolve(_pcm_spec, firm2_pcm_constraint=FM2Constraint.IID)
576
- _margin_data = _gen_margin_data(
577
- _frmshr_array_plus,
578
- np.ones_like(_frmshr_array_plus, np.float64),
555
+ frmshr_array_plus = np.hstack((_frmshr_array, _nth_firm_share))
556
+ pcm_spec_here = evolve(_pcm_spec, firm2_pcm_constraint=FM2Constraint.IID)
557
+ margin_data = _gen_margin_data(
558
+ frmshr_array_plus,
559
+ np.ones_like(frmshr_array_plus, np.float64),
579
560
  _aggregate_purchase_prob,
580
- _pcm_spec_here,
561
+ pcm_spec_here,
581
562
  _pcm_rng_seed_seq,
582
563
  _nthreads,
583
564
  )
584
565
 
585
- _pcm_array, _mnl_test_array = (
586
- getattr(_margin_data, _f) for _f in ("pcm_array", "mnl_test_array")
566
+ pcm_array, mnl_test_array = (
567
+ getattr(margin_data, _f) for _f in ("pcm_array", "mnl_test_array")
587
568
  )
588
569
 
589
- _price_array_here = 1 / (1 - _pcm_array)
590
- _price_array = _price_array_here[:, :2]
591
- _nth_firm_price = _price_array_here[:, [-1]]
570
+ price_array_here = 1 / (1 - pcm_array)
571
+ price_array = price_array_here[:, :2]
572
+ nth_firm_price = price_array_here[:, [-1]]
592
573
  if _pcm_spec.firm2_pcm_constraint == FM2Constraint.MNL:
593
574
  # Generate i.i.d. PCMs then take PCM0 and construct PCM1
594
575
  # Regenerate MNL test
595
- _purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
596
- _pcm_array[:, 1] = np.divide(
576
+ purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
577
+ pcm_array[:, 1] = np.divide(
597
578
  (
598
- _m1_nr := np.divide(
579
+ m1_nr := np.divide(
599
580
  np.einsum(
600
581
  "ij,ij,ij->ij",
601
- _price_array[:, [0]],
602
- _pcm_array[:, [0]],
603
- 1 - _purchase_prob_array[:, [0]],
582
+ price_array[:, [0]],
583
+ pcm_array[:, [0]],
584
+ 1 - purchase_prob_array[:, [0]],
604
585
  ),
605
- 1 - _purchase_prob_array[:, [1]],
586
+ 1 - purchase_prob_array[:, [1]],
606
587
  )
607
588
  ),
608
- 1 + _m1_nr,
589
+ 1 + m1_nr,
609
590
  )
610
- _mnl_test_array = (_pcm_array[:, [1]] >= 0) & (_pcm_array[:, [1]] <= 1)
591
+ mnl_test_array = (pcm_array[:, [1]] >= 0) & (pcm_array[:, [1]] <= 1)
611
592
 
612
- _margin_data = MarginDataSample(_pcm_array[:, :2], _mnl_test_array)
613
- del _price_array_here
593
+ margin_data = MarginDataSample(pcm_array[:, :2], mnl_test_array)
594
+ del price_array_here
614
595
  case _:
615
596
  raise ValueError(
616
597
  f'Specification of price distribution, "{_price_spec.value}" is invalid.'
617
598
  )
618
599
  if _price_spec != PriceSpec.CSY:
619
- _margin_data = _gen_margin_data(
600
+ margin_data = _gen_margin_data(
620
601
  _frmshr_array,
621
- _price_array,
602
+ price_array,
622
603
  _aggregate_purchase_prob,
623
604
  _pcm_spec,
624
605
  _pcm_rng_seed_seq,
@@ -626,13 +607,13 @@ def gen_margin_price_data(
626
607
  )
627
608
 
628
609
  # _price_array = _price_array.astype(np.float64)
629
- _rev_array = _price_array * _frmshr_array
630
- _nth_firm_rev = _nth_firm_price * _nth_firm_share
610
+ rev_array = price_array * _frmshr_array
611
+ nth_firm_rev = nth_firm_price * _nth_firm_share
631
612
 
632
613
  # Although `_test_rev_ratio_inv` is not fixed at 10%,
633
614
  # the ratio has not changed since inception of the HSR filing test,
634
615
  # so we treat it as a constant of merger enforcement policy.
635
- _test_rev_ratio, _test_rev_ratio_inv = 10, 1 / 10
616
+ test_rev_ratio, test_rev_ratio_inv = 10, 1 / 10
636
617
 
637
618
  match _hsr_filing_test_type:
638
619
  case SSZConstant.HSR_TEN:
@@ -643,8 +624,8 @@ def gen_margin_price_data(
643
624
  # Revenue ratio has been 10-to-1 since inception
644
625
  # Thus, a simple form of the HSR filing test would impose a 10-to-1
645
626
  # ratio restriction on the merging firms' revenues
646
- _rev_ratio = (_rev_array.min(axis=1) / _rev_array.max(axis=1)).round(4)
647
- _hsr_filing_test = _rev_ratio >= _test_rev_ratio_inv
627
+ rev_ratio = (rev_array.min(axis=1) / rev_array.max(axis=1)).round(4)
628
+ hsr_filing_test = rev_ratio >= test_rev_ratio_inv
648
629
  # del _rev_array, _rev_ratio
649
630
  case SSZConstant.HSR_NTH:
650
631
  # To get around the 10-to-1 ratio restriction, specify that the nth firm test:
@@ -653,25 +634,26 @@ def gen_margin_price_data(
653
634
  # the size test is considered met.
654
635
  # Alternatively, if the smaller merging firm has 10% or greater share,
655
636
  # the value of transaction test is considered met.
656
- _rev_ratio_to_nth = np.round(np.sort(_rev_array, axis=1) / _nth_firm_rev, 4)
657
- _hsr_filing_test = (
637
+ rev_ratio_to_nth = np.round(np.sort(rev_array, axis=1) / nth_firm_rev, 4)
638
+ hsr_filing_test = (
658
639
  np.einsum(
659
640
  "ij->i",
660
- 1 * (_rev_ratio_to_nth > [1, _test_rev_ratio]),
641
+ 1 * (rev_ratio_to_nth > [1, test_rev_ratio]),
661
642
  dtype=np.int64,
662
643
  )
663
- == _rev_ratio_to_nth.shape[1]
644
+ == rev_ratio_to_nth.shape[1]
664
645
  )
665
646
 
666
647
  # del _nth_firm_rev, _rev_ratio_to_nth
667
648
  case _:
668
649
  # Otherwise, all draws meet the filing test
669
- _hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
670
- _hsr_filing_test = _hsr_filing_test | (
671
- _frmshr_array.min(axis=1) >= _test_rev_ratio_inv
672
- )
650
+ hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
673
651
 
674
- return _margin_data, PriceDataSample(_price_array, _hsr_filing_test)
652
+ # Assume that if minimum mergeing-firm share is 10%, merger filing required
653
+ # under value-of-transactions test or merger triggers post-consummation review
654
+ hsr_filing_test |= _frmshr_array.min(axis=1) >= test_rev_ratio_inv
655
+
656
+ return margin_data, PriceDataSample(price_array, hsr_filing_test)
675
657
 
676
658
 
677
659
  def _gen_margin_data(
@@ -683,90 +665,91 @@ def _gen_margin_data(
683
665
  _nthreads: int,
684
666
  /,
685
667
  ) -> MarginDataSample:
686
- _dist_type_pcm, _dist_firm2_pcm, _dist_parms_pcm = (
668
+ dist_type_pcm, dist_parms_pcm, dist_firm2_pcm = (
687
669
  getattr(_pcm_spec, _f)
688
- for _f in ("dist_type", "firm2_pcm_constraint", "dist_parms")
670
+ for _f in ("dist_type", "dist_parms", "firm2_pcm_constraint")
689
671
  )
690
672
 
691
- _pcm_array = (
673
+ pcm_array = (
692
674
  np.empty((len(_frmshr_array), 1), float)
693
675
  if _pcm_spec.firm2_pcm_constraint == FM2Constraint.SYM
694
676
  else np.empty_like(_frmshr_array, float)
695
677
  )
696
678
 
697
- _dist_parms: ArrayFloat
698
- _beta_min, _beta_max = [0.0] * 2 # placeholder
699
- if _dist_type_pcm == PCMDistribution.EMPR:
700
- _pcm_array = mgn_data_resampler(
701
- _pcm_array.shape, seed_sequence=_pcm_rng_seed_seq
679
+ dist_parms_: ArrayFloat
680
+ beta_min, beta_max = [0.0] * 2 # placeholder
681
+ if dist_type_pcm == PCMDistribution.EMPR:
682
+ pcm_array = margin_data_resampler(
683
+ pcm_array.shape, seed_sequence=_pcm_rng_seed_seq
702
684
  )
703
685
  else:
704
- _dist_type: Literal["Beta", "Uniform"]
705
- if _dist_type_pcm in (PCMDistribution.BETA, PCMDistribution.BETA_BND):
706
- _dist_type = "Beta"
707
- _dist_parms_pcm = (
708
- (
709
- np.array([0.5, 1, 0, 1], float)
710
- if _dist_type_pcm == PCMDistribution.BETA_BND
711
- else DEFAULT_BETA_DIST_PARMS
686
+ dist_type_: Literal["Beta", "Uniform"]
687
+ if dist_type_pcm in {PCMDistribution.BETA, PCMDistribution.BETA_BND}:
688
+ dist_type_ = "Beta"
689
+ if dist_type_pcm == PCMDistribution.BETA_BND:
690
+ dist_parms_pcm = (
691
+ DEFAULT_BETA_BND_DIST_PARMS
692
+ if dist_parms_pcm is None # Eliminated by converter
693
+ else dist_parms_pcm
694
+ )
695
+ dist_parms_ = beta_located_bound(dist_parms_pcm)
696
+ else:
697
+ dist_parms_ = (
698
+ DEFAULT_BETA_DIST_PARMS
699
+ if dist_parms_pcm is None
700
+ else dist_parms_pcm
712
701
  )
713
- if _dist_parms_pcm is None or not len(_dist_parms_pcm)
714
- else _dist_parms_pcm
715
- )
716
- _dist_parms = beta_located_bound(_dist_parms_pcm)
717
702
 
718
703
  else:
719
- _dist_type = "Uniform"
720
- _dist_parms = (
704
+ dist_type_ = "Uniform"
705
+ dist_parms_ = (
721
706
  DEFAULT_DIST_PARMS
722
- if _dist_parms_pcm is None or not len(_dist_parms_pcm)
723
- else _dist_parms_pcm
707
+ if dist_parms_pcm is None or not len(dist_parms_pcm)
708
+ else dist_parms_pcm
724
709
  )
725
710
 
726
- _pcm_rng = MultithreadedRNG(
727
- _pcm_array,
728
- dist_type=_dist_type,
729
- dist_parms=_dist_parms,
711
+ MultithreadedRNG(
712
+ pcm_array,
713
+ dist_type=dist_type_,
714
+ dist_parms=dist_parms_,
730
715
  seed_sequence=_pcm_rng_seed_seq,
731
716
  nthreads=_nthreads,
732
- )
733
- _pcm_rng.fill()
734
- del _pcm_rng
717
+ ).fill()
735
718
 
736
- if _dist_type_pcm == PCMDistribution.BETA_BND:
737
- _beta_min, _beta_max = _dist_parms_pcm[2:]
738
- _pcm_array = (_beta_max - _beta_min) * _pcm_array + _beta_min
739
- del _beta_min, _beta_max
719
+ if dist_type_pcm == PCMDistribution.BETA_BND:
720
+ beta_min, beta_max = dist_parms_pcm[2:]
721
+ pcm_array = (beta_max - beta_min) * pcm_array + beta_min
722
+ del beta_min, beta_max
740
723
 
741
- if _dist_firm2_pcm == FM2Constraint.SYM:
742
- _pcm_array = np.column_stack((_pcm_array,) * _frmshr_array.shape[1])
743
- if _dist_firm2_pcm == FM2Constraint.MNL:
724
+ if dist_firm2_pcm == FM2Constraint.SYM:
725
+ pcm_array = np.column_stack((pcm_array,) * _frmshr_array.shape[1])
726
+ if dist_firm2_pcm == FM2Constraint.MNL:
744
727
  # Impose FOCs from profit-maximization with MNL demand
745
- if _dist_type_pcm == PCMDistribution.EMPR:
728
+ if dist_type_pcm == PCMDistribution.EMPR:
746
729
  print(
747
730
  "NOTE: Estimated Firm 2 parameters will not be consistent with "
748
731
  "the empirical distribution of margins in the source data. For "
749
732
  "consistency, respecify pcm_spec.firm2_pcm_constraint = FM2Constraint.IID."
750
733
  )
751
- _purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
734
+ purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
752
735
 
753
- _pcm_array[:, [1]] = np.divide(
736
+ pcm_array[:, [1]] = np.divide(
754
737
  np.einsum(
755
738
  "ij,ij,ij->ij",
756
739
  _price_array[:, [0]],
757
- _pcm_array[:, [0]],
758
- 1 - _purchase_prob_array[:, [0]],
740
+ pcm_array[:, [0]],
741
+ 1 - purchase_prob_array[:, [0]],
759
742
  ),
760
743
  np.einsum(
761
- "ij,ij->ij", _price_array[:, [1]], 1 - _purchase_prob_array[:, [1]]
744
+ "ij,ij->ij", _price_array[:, [1]], 1 - purchase_prob_array[:, [1]]
762
745
  ),
763
746
  )
764
747
 
765
- _mnl_test_array = _pcm_array[:, 1].__ge__(0) & _pcm_array[:, 1].__le__(1)
748
+ mnl_test_array = (pcm_array[:, 1] >= 0) & (pcm_array[:, 1] <= 1)
766
749
  else:
767
- _mnl_test_array = np.ones(len(_pcm_array), dtype=bool)
750
+ mnl_test_array = np.ones(len(pcm_array), dtype=bool)
768
751
 
769
- return MarginDataSample(_pcm_array, _mnl_test_array)
752
+ return MarginDataSample(pcm_array, mnl_test_array)
770
753
 
771
754
 
772
755
  def _beta_located(
@@ -790,8 +773,8 @@ def _beta_located(
790
773
 
791
774
  """
792
775
 
793
- _mul = -1 + _mu * (1 - _mu) / _sigma**2
794
- return np.array([_mu * _mul, (1 - _mu) * _mul], float)
776
+ mul = -1 + _mu * (1 - _mu) / _sigma**2
777
+ return np.array([_mu * mul, (1 - _mu) * mul], float)
795
778
 
796
779
 
797
780
  def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
@@ -822,5 +805,5 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
822
805
  .. [8] NIST, Beta Distribution. https://www.itl.nist.gov/div898/handbook/eda/section3/eda366h.htm
823
806
  """ # noqa: RUF002
824
807
 
825
- _bmu, _bsigma, _bmin, _bmax = _dist_parms
826
- return _beta_located((_bmu - _bmin) / (_bmax - _bmin), _bsigma / (_bmax - _bmin))
808
+ bmu, bsigma, bmin, bmax = _dist_parms
809
+ return _beta_located((bmu - bmin) / (bmax - bmin), bsigma / (bmax - bmin))