mergeron 2025.739439.10__py3-none-any.whl → 2025.739439.12__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/gen/__init__.py CHANGED
@@ -15,7 +15,7 @@ from attrs import Attribute, Converter, cmp_using, field, frozen
15
15
  from numpy.random import SeedSequence
16
16
 
17
17
  from .. import ( # noqa: TID252
18
- DEFAULT_REC_RATIO,
18
+ DEFAULT_REC,
19
19
  VERSION,
20
20
  ArrayBIGINT,
21
21
  ArrayBoolean,
@@ -89,9 +89,9 @@ class SHRDistribution(str, Enameled):
89
89
  """Share distribution for merging-firm shares has a higher peak share
90
90
 
91
91
  By default, shape parameter for merging-firm-share is 2.5, and
92
- 1.0 for all others. Defining, :attr:`.ShareSpec.dist_parms`
92
+ 1.0 for all others. Defining, :attr:`.MarketShareSpec.dist_parms`
93
93
  as a vector of shape parameters with length matching
94
- that of :attr:`.ShareSpec.dist_parms` allows flexible specification
94
+ that of :attr:`.MarketShareSpec.dist_parms` allows flexible specification
95
95
  of Dirichlet-distributed share-data generation.
96
96
  """
97
97
 
@@ -104,7 +104,7 @@ class SHRDistribution(str, Enameled):
104
104
 
105
105
 
106
106
  def _fc_wts_conv(
107
- _v: Sequence[float | int] | ArrayDouble | ArrayINT | None, _i: ShareSpec
107
+ _v: Sequence[float | int] | ArrayDouble | ArrayINT | None, _i: MarketShareSpec
108
108
  ) -> ArrayFloat | None:
109
109
  if _i.dist_type == SHRDistribution.UNI:
110
110
  return None
@@ -114,7 +114,7 @@ def _fc_wts_conv(
114
114
  return _tv if (_tv := np.asarray(_v, float)).sum() == 1 else _tv / _tv.sum()
115
115
 
116
116
 
117
- def _shr_dp_conv(_v: Sequence[float] | ArrayFloat | None, _i: ShareSpec) -> ArrayFloat:
117
+ def _shr_dp_conv(_v: Sequence[float] | ArrayFloat | None, _i: MarketShareSpec) -> ArrayFloat:
118
118
  if _v is None or len(_v) == 0 or np.array_equal(_v, DEFAULT_DIST_PARMS):
119
119
  if _i.dist_type == SHRDistribution.UNI:
120
120
  return DEFAULT_DIST_PARMS
@@ -149,7 +149,7 @@ def _shr_dp_conv(_v: Sequence[float] | ArrayFloat | None, _i: ShareSpec) -> Arra
149
149
 
150
150
 
151
151
  @frozen
152
- class ShareSpec:
152
+ class MarketShareSpec:
153
153
  """Market share specification.
154
154
 
155
155
  A salient feature of market-share specification in this package is that
@@ -163,7 +163,7 @@ class ShareSpec:
163
163
  -----
164
164
  If :attr:`.dist_type` == :attr:`.SHRDistribution.UNI`, it is then infeasible that
165
165
  :attr:`.recapture_form` == :attr:`mergeron.RECForm.OUTIN`.
166
- In other words, recapture ratios cannot be estimated using
166
+ In other words, recapture rates cannot be estimated using
167
167
  outside-good choice probabilities if the distribution of markets over firm-counts
168
168
  is unspecified.
169
169
  """
@@ -187,11 +187,11 @@ class ShareSpec:
187
187
  """
188
188
 
189
189
  @firm_counts_weights.default
190
- def __fcwd(_i: ShareSpec) -> ArrayFloat | None:
190
+ def __fcwd(_i: MarketShareSpec) -> ArrayFloat | None:
191
191
  return _fc_wts_conv(None, _i)
192
192
 
193
193
  @firm_counts_weights.validator
194
- def __fcv(_i: ShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
194
+ def __fcv(_i: MarketShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
195
195
  if _i.dist_type != SHRDistribution.UNI and not len(_v):
196
196
  raise ValueError(
197
197
  f"Attribute, {'"firm_counts_weights"'} must be populated if the share distribution is "
@@ -212,13 +212,13 @@ class ShareSpec:
212
212
  """
213
213
 
214
214
  @dist_parms.default
215
- def __dpd(_i: ShareSpec) -> ArrayFloat:
215
+ def __dpd(_i: MarketShareSpec) -> ArrayFloat:
216
216
  # converters run after defaults, and we
217
217
  # avoid redundancy and confusion here
218
218
  return _shr_dp_conv(None, _i)
219
219
 
220
220
  @dist_parms.validator
221
- def __dpv(_i: ShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
221
+ def __dpv(_i: MarketShareSpec, _a: Attribute[ArrayFloat], _v: ArrayFloat) -> None:
222
222
  if (
223
223
  _i.firm_counts_weights is not None
224
224
  and _v is not None
@@ -234,7 +234,7 @@ class ShareSpec:
234
234
  """See :class:`mergeron.RECForm`"""
235
235
 
236
236
  @recapture_form.validator
237
- def __rfv(_i: ShareSpec, _a: Attribute[RECForm], _v: RECForm) -> None:
237
+ def __rfv(_i: MarketShareSpec, _a: Attribute[RECForm], _v: RECForm) -> None:
238
238
  if _i.dist_type == SHRDistribution.UNI and _v == RECForm.OUTIN:
239
239
  raise ValueError(
240
240
  "Outside-good choice probabilities cannot be generated if the "
@@ -243,15 +243,15 @@ class ShareSpec:
243
243
  "firm-counts unspecified."
244
244
  )
245
245
 
246
- recapture_ratio: float | None = field(kw_only=True)
246
+ recapture_rate: float | None = field(kw_only=True)
247
247
  """A value between 0 and 1.
248
248
 
249
249
  :code:`None` if market share specification requires direct generation of
250
250
  outside good choice probabilities (:attr:`mergeron.RECForm.OUTIN`).
251
251
 
252
- The recapture ratio is usually calibrated to the numbers-equivalent of the
252
+ The recapture rate is usually calibrated to the numbers-equivalent of the
253
253
  HHI threshold for the presumption of harm from unilateral competitive effects
254
- in published merger guidelines. Accordingly, the recapture ratio rounded to
254
+ in published merger guidelines. Accordingly, the recapture rate rounded to
255
255
  the nearest 5% is:
256
256
 
257
257
  * 0.85, **7-to-6 merger from symmetry**; US Guidelines, 1992, 2023
@@ -264,21 +264,21 @@ class ShareSpec:
264
264
  presumptions no harm.)
265
265
 
266
266
  ALERT: If diversion ratios are estimated by specifying a choice probability for the
267
- outside good, the recapture ratio is set to None, overriding any user-specified value.
267
+ outside good, the recapture rate is set to None, overriding any user-specified value.
268
268
  """
269
269
 
270
- @recapture_ratio.default
271
- def __rrd(_i: ShareSpec) -> float | None:
272
- return None if _i.recapture_form == RECForm.OUTIN else DEFAULT_REC_RATIO
270
+ @recapture_rate.default
271
+ def __rrd(_i: MarketShareSpec) -> float | None:
272
+ return None if _i.recapture_form == RECForm.OUTIN else DEFAULT_REC
273
273
 
274
- @recapture_ratio.validator
275
- def __rrv(_i: ShareSpec, _a: Attribute[float], _v: float) -> None:
274
+ @recapture_rate.validator
275
+ def __rrv(_i: MarketShareSpec, _a: Attribute[float], _v: float) -> None:
276
276
  if _v and not (0 < _v <= 1):
277
- raise ValueError("Recapture ratio must lie in the interval, [0, 1).")
277
+ raise ValueError("Recapture rate must lie in the interval, [0, 1).")
278
278
  elif _v is None and _i.recapture_form != RECForm.OUTIN:
279
279
  raise ValueError(
280
280
  f"Recapture specification, {_i.recapture_form!r} requires that "
281
- "the market sample specification includes a recapture ratio in the "
281
+ "the market sample specification includes a recapture rate in the "
282
282
  "interval [0, 1)."
283
283
  )
284
284
 
@@ -455,7 +455,7 @@ class SSZConstant(float, Enameled):
455
455
 
456
456
 
457
457
  @frozen
458
- class MarketSampleData:
458
+ class MarketsData:
459
459
  """Container for generated market sample dataset."""
460
460
 
461
461
  frmshr_array: ArrayDouble = field(eq=cmp_using(np.array_equal))
@@ -467,7 +467,7 @@ class MarketSampleData:
467
467
  price_array: ArrayDouble = field(eq=cmp_using(np.array_equal))
468
468
  """Merging-firms' price-cost margins (PCM)"""
469
469
 
470
- divr_array: ArrayDouble = field(eq=cmp_using(np.array_equal))
470
+ divratio_array: ArrayDouble = field(eq=cmp_using(np.array_equal))
471
471
  """Diversion ratio between the merging firms"""
472
472
 
473
473
  hhi_delta: ArrayDouble = field(eq=cmp_using(np.array_equal))
@@ -481,7 +481,7 @@ class MarketSampleData:
481
481
  """
482
482
 
483
483
  @aggregate_purchase_prob.default
484
- def __appd(_i: MarketSampleData) -> ArrayDouble:
484
+ def __appd(_i: MarketsData) -> ArrayDouble:
485
485
  retval: ArrayDouble = np.empty_like(_i.frmshr_array[:, :1], float)
486
486
  retval.fill(np.nan)
487
487
  return retval
@@ -490,7 +490,7 @@ class MarketSampleData:
490
490
  """Number of firms in market"""
491
491
 
492
492
  @fcounts.default
493
- def __fcd(_i: MarketSampleData) -> ArrayINT:
493
+ def __fcd(_i: MarketsData) -> ArrayINT:
494
494
  return np.zeros_like(_i.frmshr_array[:, :1], np.uint8)
495
495
 
496
496
  nth_firm_share: ArrayDouble = field(eq=cmp_using(np.array_equal))
@@ -501,7 +501,7 @@ class MarketSampleData:
501
501
  """
502
502
 
503
503
  @nth_firm_share.default
504
- def __nfsd(_i: MarketSampleData) -> ArrayDouble:
504
+ def __nfsd(_i: MarketsData) -> ArrayDouble:
505
505
  retval: ArrayDouble = np.empty_like(_i.frmshr_array[:, :1], float)
506
506
  retval.fill(np.nan)
507
507
  return retval
@@ -510,7 +510,7 @@ class MarketSampleData:
510
510
  """Post-merger contribution to Herfindahl-Hirschman Index (HHI)"""
511
511
 
512
512
  @hhi_post.default
513
- def __hpd(_i: MarketSampleData) -> ArrayDouble:
513
+ def __hpd(_i: MarketsData) -> ArrayDouble:
514
514
  retval: ArrayDouble = np.empty_like(_i.frmshr_array[:, :1], float)
515
515
  retval.fill(np.nan)
516
516
  return retval
@@ -530,7 +530,7 @@ class MarketSampleData:
530
530
  @classmethod
531
531
  def from_h5f(
532
532
  cls, _hfh: io.BufferedReader | zipfile.ZipExtFile | IO[bytes]
533
- ) -> MarketSampleData:
533
+ ) -> MarketsData:
534
534
  """Load market sample data from HDF5 file."""
535
535
  with h5py.File(_hfh, "r") as _h5f:
536
536
  _retval = cls(**{_a: _h5f[_a][:] for _a in _h5f})
@@ -538,7 +538,7 @@ class MarketSampleData:
538
538
 
539
539
 
540
540
  @frozen
541
- class ShareSampleData:
541
+ class MarketSharesData:
542
542
  """Container for generated market shares.
543
543
 
544
544
  Includes related measures of market structure
@@ -559,7 +559,7 @@ class ShareSampleData:
559
559
 
560
560
 
561
561
  @frozen
562
- class PriceSampleData:
562
+ class PricesData:
563
563
  """Container for generated price array, and related."""
564
564
 
565
565
  price_array: ArrayDouble
@@ -570,19 +570,19 @@ class PriceSampleData:
570
570
 
571
571
 
572
572
  @frozen
573
- class MarginSampleData:
573
+ class MarginsData:
574
574
  """Container for generated margin array and related MNL test array."""
575
575
 
576
576
  pcm_array: ArrayDouble
577
577
  """Merging-firms' PCMs"""
578
578
 
579
- mnl_test_array: ArrayBoolean
579
+ mnl_test: ArrayBoolean
580
580
  """Flags infeasible observations as False and rest as True
581
581
 
582
582
  Applying restrictions from Bertrand-Nash oligopoly with MNL demand results
583
583
  in some draws of Firm 2 PCM falling outside the feasible interval, :math:`[0, 1]`
584
584
  for certain combinations of merging firms shares as initially drawn. Such draws
585
- are flagged as infeasible (False) in :code:`mnl_test_array` while draws with
585
+ are flagged as infeasible (False) in :code:`mnl_test` while draws with
586
586
  feaseible PCM values flagged True. This array is used to exclude infeasible draws
587
587
  when imposing MNL demand in simulations.
588
588
  """
@@ -658,5 +658,5 @@ class UPPTestsCounts:
658
658
  """
659
659
 
660
660
 
661
- for _typ in (SeedSequenceData, ShareSpec, PCMSpec, UPPTestsCounts, UPPTestRegime):
661
+ for _typ in (SeedSequenceData, MarketShareSpec, PCMSpec, UPPTestsCounts, UPPTestRegime):
662
662
  yamelize_attrs(_typ)
@@ -24,22 +24,22 @@ from ..core import guidelines_boundaries as gbl # noqa: TID252
24
24
  from ..core.guidelines_boundaries import HMGThresholds # noqa: TID252
25
25
  from . import (
26
26
  INVResolution, # noqa: F401
27
- MarketSampleData,
27
+ MarketsData,
28
28
  PCMDistribution,
29
29
  PCMRestriction,
30
30
  PCMSpec,
31
31
  PriceSpec,
32
32
  SeedSequenceData,
33
- ShareSpec,
33
+ MarketShareSpec,
34
34
  SHRDistribution,
35
35
  SSZConstant,
36
36
  UPPTestRegime,
37
37
  UPPTestsCounts,
38
38
  )
39
39
  from .data_generation_functions import (
40
- gen_divr_array,
41
- gen_margin_price_data,
42
- gen_share_data,
40
+ diversion_ratios_builder,
41
+ prices_sampler,
42
+ market_share_sampler,
43
43
  )
44
44
  from .upp_tests import compute_upp_test_counts
45
45
 
@@ -82,11 +82,11 @@ def _seed_data_conv(_v: SeedSequenceData | None, _i: MarketSample) -> SeedSequen
82
82
  class MarketSample:
83
83
  """Parameter specification for market data generation."""
84
84
 
85
- share_spec: ShareSpec = field(
86
- default=ShareSpec(SHRDistribution.UNI),
87
- validator=validators.instance_of(ShareSpec),
85
+ share_spec: MarketShareSpec = field(
86
+ default=MarketShareSpec(SHRDistribution.UNI),
87
+ validator=validators.instance_of(MarketShareSpec),
88
88
  )
89
- """Market-share specification, see :class:`ShareSpec`"""
89
+ """Market-share specification, see :class:`MarketShareSpec`"""
90
90
 
91
91
  pcm_spec: PCMSpec = field(
92
92
  default=PCMSpec(PCMDistribution.UNI), validator=validators.instance_of(PCMSpec)
@@ -101,7 +101,7 @@ class MarketSample:
101
101
  ):
102
102
  raise ValueError(
103
103
  f'Specification of "PCMSpec.pcm_restriction", as {PCMRestriction.MNL!r} '
104
- f'requires that "ShareSpec.recapture_form" be {RECForm.INOUT!r} '
104
+ f'requires that "MarketShareSpec.recapture_form" be {RECForm.INOUT!r} '
105
105
  f"or {RECForm.OUTIN!r}, not {RECForm.FIXED!r} as presently specified"
106
106
  )
107
107
 
@@ -153,13 +153,13 @@ class MarketSample:
153
153
  nthreads: int = field(default=NTHREADS, validator=validators.instance_of(int))
154
154
  """number of parallel threads to use"""
155
155
 
156
- dataset: MarketSampleData | None = field(default=None, init=False)
156
+ dataset: MarketsData | None = field(default=None, init=False)
157
157
 
158
158
  enf_counts: UPPTestsCounts | None = field(default=None, init=False)
159
159
 
160
- def _gen_market_sample(
160
+ def _markets_sampler(
161
161
  self, /, *, sample_size: int, seed_data: SeedSequenceData, nthreads: int
162
- ) -> MarketSampleData:
162
+ ) -> MarketsData:
163
163
  """
164
164
  Generate share, diversion ratio, price, and margin data for MarketSpec.
165
165
 
@@ -181,7 +181,7 @@ class MarketSample:
181
181
  shr_sample_size = int(shr_sample_size)
182
182
 
183
183
  # Generate share data
184
- mktshr_data = gen_share_data(
184
+ mktshr_data = market_share_sampler(
185
185
  shr_sample_size,
186
186
  self.share_spec,
187
187
  seed_data.fcounts,
@@ -195,7 +195,7 @@ class MarketSample:
195
195
  del mktshr_data
196
196
 
197
197
  # Generate merging-firm price and PCM data
198
- margin_data, price_data = gen_margin_price_data(
198
+ margin_data, price_data = prices_sampler(
199
199
  mktshr_array_[:, :2],
200
200
  nth_firm_share_,
201
201
  aggregate_purchase_prob_,
@@ -210,7 +210,7 @@ class MarketSample:
210
210
  price_array_ = price_data.price_array
211
211
 
212
212
  if shr_sample_size > sample_size:
213
- mnl_test_rows = margin_data.mnl_test_array * price_data.hsr_filing_test
213
+ mnl_test_rows = margin_data.mnl_test * price_data.hsr_filing_test
214
214
 
215
215
  mktshr_array_ = mktshr_array_[mnl_test_rows][:sample_size]
216
216
  pcm_array_ = margin_data.pcm_array[mnl_test_rows][:sample_size]
@@ -224,18 +224,18 @@ class MarketSample:
224
224
  del mnl_test_rows
225
225
 
226
226
  # Calculate diversion ratios
227
- divr_array = gen_divr_array(
227
+ divratio_array = diversion_ratios_builder(
228
228
  self.share_spec.recapture_form,
229
- self.share_spec.recapture_ratio,
229
+ self.share_spec.recapture_rate,
230
230
  mktshr_array_[:, :2],
231
231
  aggregate_purchase_prob_,
232
232
  )
233
233
 
234
- return MarketSampleData(
234
+ return MarketsData(
235
235
  mktshr_array_[:, :2],
236
236
  pcm_array_,
237
237
  price_array_,
238
- divr_array,
238
+ divratio_array,
239
239
  np.einsum("ij,ij->i", mktshr_array_[:, :2], mktshr_array_[:, [1, 0]])[
240
240
  :, None
241
241
  ],
@@ -256,7 +256,7 @@ class MarketSample:
256
256
  None
257
257
 
258
258
  """
259
- self.dataset = self._gen_market_sample(
259
+ self.dataset = self._markets_sampler(
260
260
  seed_data=self.seed_data,
261
261
  sample_size=self.sample_size,
262
262
  nthreads=self.nthreads,
@@ -299,7 +299,7 @@ class MarketSample:
299
299
  UPPTestCounts object with of test counts by firm count, ΔHHI and concentration zone
300
300
 
301
301
  """
302
- market_data_sample = self._gen_market_sample(
302
+ market_data_sample = self._markets_sampler(
303
303
  sample_size=sample_size, seed_data=seed_data, nthreads=nthreads
304
304
  )
305
305
 
@@ -343,11 +343,11 @@ class MarketSample:
343
343
 
344
344
  if (
345
345
  self.share_spec.recapture_form != RECForm.OUTIN
346
- and self.share_spec.recapture_ratio != _enf_parm_vec.rec
346
+ and self.share_spec.recapture_rate != _enf_parm_vec.rec
347
347
  ):
348
348
  raise ValueError(
349
349
  "{} {} {}".format(
350
- f"Recapture ratio from market sample spec, {self.share_spec.recapture_ratio}",
350
+ f"Recapture rate from market sample spec, {self.share_spec.recapture_rate}",
351
351
  f"must match the value, {_enf_parm_vec.rec}",
352
352
  "the guidelines thresholds vector.",
353
353
  )
@@ -488,7 +488,7 @@ class MarketSample:
488
488
  if _dt:
489
489
  with _dp.open("rb") as _hfh:
490
490
  object.__setattr__(
491
- market_sample_, "dataset", MarketSampleData.from_h5f(_hfh)
491
+ market_sample_, "dataset", MarketsData.from_h5f(_hfh)
492
492
  )
493
493
  if _et:
494
494
  object.__setattr__(