mergeron 2024.738972.0__py3-none-any.whl → 2024.739079.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mergeron might be problematic. Click here for more details.

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