mergeron 2025.739319.1__tar.gz → 2025.739319.3__tar.gz
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-2025.739319.1 → mergeron-2025.739319.3}/PKG-INFO +1 -5
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/README.rst +0 -4
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/pyproject.toml +1 -1
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/__init__.py +1 -1
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/empirical_margin_distribution.py +21 -13
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/guidelines_boundaries.py +2 -2
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/demo/visualize_empirical_margin_distribution.py +5 -2
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/__init__.py +19 -19
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/data_generation.py +17 -12
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/data_generation_functions.py +13 -17
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/enforcement_stats.py +7 -4
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/upp_tests.py +18 -5
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/__init__.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/ftc_merger_investigations_data.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/guidelines_boundary_functions.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/guidelines_boundary_functions_extra.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/pseudorandom_numbers.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/data/__init__.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/data/damodaran_margin_data.xls +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/data/ftc_merger_investigations_data.zip +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/demo/__init__.py +0 -0
- {mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version: 2025.739319.
|
|
3
|
+
Version: 2025.739319.3
|
|
4
4
|
Summary: Analyze merger enforcement policy using Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
@@ -93,10 +93,6 @@ backend to ``None`` to skip fine-tuning plots for PDF generation.
|
|
|
93
93
|
_ = fig.legend(loc=(0.4, 0.7), frameon=False)
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
.. image:: ./docs/readme_content/output_5_0.png
|
|
98
|
-
|
|
99
|
-
|
|
100
96
|
*Analyzing FTC Merger Investigations Data*
|
|
101
97
|
|
|
102
98
|
.. code:: python
|
{mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/empirical_margin_distribution.py
RENAMED
|
@@ -43,11 +43,12 @@ from types import MappingProxyType
|
|
|
43
43
|
|
|
44
44
|
import numpy as np
|
|
45
45
|
import urllib3
|
|
46
|
+
from joblib import Parallel, delayed
|
|
46
47
|
from numpy.random import PCG64DXSM, Generator, SeedSequence
|
|
47
48
|
from scipy import stats # type: ignore
|
|
48
49
|
from xlrd import open_workbook # type: ignore
|
|
49
50
|
|
|
50
|
-
from .. import VERSION, ArrayDouble, this_yaml # noqa: TID252
|
|
51
|
+
from .. import NTHREADS, VERSION, ArrayDouble, this_yaml # noqa: TID252
|
|
51
52
|
from .. import WORK_DIR as PKG_WORK_DIR # noqa: TID252
|
|
52
53
|
from .. import data as mdat # noqa: TID252
|
|
53
54
|
from . import _mappingproxy_from_mapping
|
|
@@ -67,7 +68,7 @@ u3pm = urllib3.PoolManager()
|
|
|
67
68
|
def margin_data_getter( # noqa: PLR0912
|
|
68
69
|
_table_name: str = "margin",
|
|
69
70
|
*,
|
|
70
|
-
data_archive_path: Path
|
|
71
|
+
data_archive_path: Path = MGNDATA_ARCHIVE_PATH,
|
|
71
72
|
data_download_flag: bool = False,
|
|
72
73
|
) -> DamodaranMarginData:
|
|
73
74
|
if _table_name != "margin": # Not validated for other tables
|
|
@@ -203,11 +204,12 @@ def margin_data_builder(
|
|
|
203
204
|
|
|
204
205
|
|
|
205
206
|
def margin_data_resampler(
|
|
206
|
-
_dist_parms:
|
|
207
|
+
_dist_parms: ArrayDouble,
|
|
207
208
|
/,
|
|
208
209
|
*,
|
|
209
|
-
sample_size: int | tuple[int, ...]
|
|
210
|
+
sample_size: int | tuple[int, ...],
|
|
210
211
|
seed_sequence: SeedSequence | None = None,
|
|
212
|
+
nthreads: int = NTHREADS,
|
|
211
213
|
) -> ArrayDouble:
|
|
212
214
|
"""
|
|
213
215
|
Generate draws from the empirical distribution bassed on Prof. Damodaran's margin data.
|
|
@@ -231,6 +233,8 @@ def margin_data_resampler(
|
|
|
231
233
|
SeedSequence for seeding random-number generator when results
|
|
232
234
|
are to be repeatable
|
|
233
235
|
|
|
236
|
+
nthreads
|
|
237
|
+
Number of threads to use in generating margin data.
|
|
234
238
|
Returns
|
|
235
239
|
-------
|
|
236
240
|
Array of margin values
|
|
@@ -241,22 +245,26 @@ def margin_data_resampler(
|
|
|
241
245
|
|
|
242
246
|
_seed = seed_sequence or SeedSequence(pool_size=8)
|
|
243
247
|
|
|
244
|
-
_x, _w = _dist_parms[:, 0], _dist_parms[:, 1]
|
|
248
|
+
_x, _w = _dist_parms[:, 0], _dist_parms[:, 1]
|
|
245
249
|
|
|
246
250
|
margin_kde = stats.gaussian_kde(_x, weights=_w, bw_method="silverman")
|
|
247
251
|
margin_kde.set_bandwidth(bw_method=margin_kde.factor / 3.0)
|
|
248
252
|
|
|
249
253
|
if isinstance(sample_size, int):
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
)
|
|
254
|
+
return margin_kde.resample(sample_size, seed=Generator(PCG64DXSM(_seed))).T
|
|
255
|
+
|
|
253
256
|
elif isinstance(sample_size, tuple) and len(sample_size) == 2:
|
|
254
|
-
_ssz, _ncol = sample_size
|
|
255
257
|
ret_array = np.empty(sample_size, float)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
)
|
|
258
|
+
|
|
259
|
+
_ssz, _ncol = sample_size
|
|
260
|
+
dat_list = Parallel(n_jobs=min(nthreads, _ncol), prefer="threads")(
|
|
261
|
+
delayed(margin_kde.resample)(_ssz, seed=Generator(PCG64DXSM(_col_seed)))
|
|
262
|
+
for _col_seed in _seed.spawn(_ncol)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
for _i in range(_ncol):
|
|
266
|
+
ret_array[:, [_i]] = dat_list[_i].T
|
|
267
|
+
|
|
260
268
|
return ret_array
|
|
261
269
|
else:
|
|
262
270
|
raise ValueError(f"Invalid sample size: {sample_size!r}")
|
{mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/guidelines_boundaries.py
RENAMED
|
@@ -109,7 +109,7 @@ class GuidelinesThresholds:
|
|
|
109
109
|
_r := gbfn.round_cust(_fc / (_fc + 1), frac=0.05),
|
|
110
110
|
_g := guppi_from_delta(dh_s, m_star=1.0, r_bar=_r),
|
|
111
111
|
_dr := 1 - _r,
|
|
112
|
-
_cmcr :=
|
|
112
|
+
_cmcr := _g, # Not strictly a Guidelines standard
|
|
113
113
|
_ipr := _g, # Not strictly a Guidelines standard
|
|
114
114
|
),
|
|
115
115
|
)
|
|
@@ -132,7 +132,7 @@ class GuidelinesThresholds:
|
|
|
132
132
|
_g,
|
|
133
133
|
(1 - _r_i) / 2,
|
|
134
134
|
_cmcr,
|
|
135
|
-
_ipr
|
|
135
|
+
_ipr,
|
|
136
136
|
)
|
|
137
137
|
if self.pub_year == 2010
|
|
138
138
|
else HMGThresholds(
|
|
@@ -21,11 +21,14 @@ WORK_DIR = globals().get("WORK_DIR", PKG_WORK_DIR)
|
|
|
21
21
|
|
|
22
22
|
SAMPLE_SIZE = 10**6
|
|
23
23
|
BIN_COUNT = 25
|
|
24
|
-
|
|
24
|
+
margin_data, margin_data_stats = emd.margin_data_builder()
|
|
25
|
+
|
|
26
|
+
margin_data_obs, margin_data_wts = margin_data[:, 0], margin_data[:, 1]
|
|
27
|
+
|
|
25
28
|
print(repr(margin_data_obs))
|
|
26
29
|
print(repr(margin_data_stats))
|
|
27
30
|
|
|
28
|
-
plt, mgn_fig, mgn_ax, set_axis_def = boundary_plot(
|
|
31
|
+
plt, mgn_fig, mgn_ax, set_axis_def = boundary_plot(mktshare_plot_flag=False)
|
|
29
32
|
mgn_fig.set_figheight(6.5)
|
|
30
33
|
mgn_fig.set_figwidth(9.0)
|
|
31
34
|
|
|
@@ -299,7 +299,7 @@ class PCMDistribution(str, Enameled):
|
|
|
299
299
|
|
|
300
300
|
@this_yaml.register_class
|
|
301
301
|
@enum.unique
|
|
302
|
-
class
|
|
302
|
+
class PCMRestriction(str, Enameled):
|
|
303
303
|
"""Firm 2 margins - derivation methods."""
|
|
304
304
|
|
|
305
305
|
IID = "i.i.d"
|
|
@@ -307,9 +307,7 @@ class FM2Constraint(str, Enameled):
|
|
|
307
307
|
SYM = "symmetric"
|
|
308
308
|
|
|
309
309
|
|
|
310
|
-
def _pcm_dp_conv(
|
|
311
|
-
_v: ArrayFloat | Sequence[float] | None, _i: PCMSpec
|
|
312
|
-
) -> ArrayFloat:
|
|
310
|
+
def _pcm_dp_conv(_v: ArrayFloat | Sequence[float] | None, _i: PCMSpec) -> ArrayFloat:
|
|
313
311
|
if _v is None or len(_v) == 0 or np.array_equal(_v, DEFAULT_DIST_PARMS):
|
|
314
312
|
if _i.dist_type == PCMDistribution.EMPR:
|
|
315
313
|
return margin_data_builder()[0]
|
|
@@ -320,10 +318,10 @@ def _pcm_dp_conv(
|
|
|
320
318
|
return DEFAULT_BETA_BND_DIST_PARMS
|
|
321
319
|
case _:
|
|
322
320
|
return DEFAULT_DIST_PARMS
|
|
323
|
-
elif
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
elif _i.dist_type == PCMDistribution.EMPR and not isinstance(_v, np.ndarray):
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"Invalid specification; use ..core.empriical_margin_distribution.margin_data_builider()[0]."
|
|
324
|
+
)
|
|
327
325
|
elif isinstance(_v, Sequence | np.ndarray):
|
|
328
326
|
return np.asarray(_v, float)
|
|
329
327
|
else:
|
|
@@ -332,8 +330,6 @@ def _pcm_dp_conv(
|
|
|
332
330
|
"sequence of Numpy arrays, or Numpy ndarray."
|
|
333
331
|
)
|
|
334
332
|
|
|
335
|
-
return _v
|
|
336
|
-
|
|
337
333
|
|
|
338
334
|
@frozen
|
|
339
335
|
class PCMSpec:
|
|
@@ -373,9 +369,7 @@ class PCMSpec:
|
|
|
373
369
|
"""
|
|
374
370
|
|
|
375
371
|
@dist_parms.default
|
|
376
|
-
def __dpwd(
|
|
377
|
-
_i: PCMSpec,
|
|
378
|
-
) -> ArrayFloat:
|
|
372
|
+
def __dpwd(_i: PCMSpec) -> ArrayFloat:
|
|
379
373
|
return _pcm_dp_conv(None, _i)
|
|
380
374
|
|
|
381
375
|
@dist_parms.validator
|
|
@@ -403,16 +397,22 @@ class PCMSpec:
|
|
|
403
397
|
f'for PCM with distribution, "{_i.dist_type}" is incorrect.'
|
|
404
398
|
)
|
|
405
399
|
|
|
406
|
-
elif (
|
|
407
|
-
_i.dist_type == PCMDistribution.EMPR
|
|
408
|
-
and not isinstance(_v, np.ndarray)
|
|
409
|
-
):
|
|
400
|
+
elif _i.dist_type == PCMDistribution.EMPR and not isinstance(_v, np.ndarray):
|
|
410
401
|
raise ValueError(
|
|
411
402
|
"Empirical distribution requires deserialzed margin data from Prof. Damodaran, NYU"
|
|
412
403
|
)
|
|
413
404
|
|
|
414
|
-
|
|
415
|
-
"""See :class:`
|
|
405
|
+
pcm_restriction: PCMRestriction = field(kw_only=True, default=PCMRestriction.IID)
|
|
406
|
+
"""See :class:`PCMRestriction`"""
|
|
407
|
+
|
|
408
|
+
@pcm_restriction.validator
|
|
409
|
+
def __prv(_i: PCMSpec, _a: Attribute[PCMRestriction], _v: PCMRestriction) -> None:
|
|
410
|
+
if _v == PCMRestriction.MNL and _i.dist_type == PCMDistribution.EMPR:
|
|
411
|
+
print(
|
|
412
|
+
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
413
|
+
"the empirical distribution of margins in the source data. For "
|
|
414
|
+
"consistency, respecify pcm_spec.pcm_restriction = PCMRestriction.IID."
|
|
415
|
+
)
|
|
416
416
|
|
|
417
417
|
|
|
418
418
|
@this_yaml.register_class
|
|
@@ -26,10 +26,10 @@ from .. import ( # noqa: TID252 # noqa
|
|
|
26
26
|
from ..core import guidelines_boundaries as gbl # noqa: TID252
|
|
27
27
|
from ..core.guidelines_boundaries import HMGThresholds # noqa: TID252
|
|
28
28
|
from . import (
|
|
29
|
-
FM2Constraint,
|
|
30
29
|
INVResolution, # noqa: F401
|
|
31
30
|
MarketSampleData,
|
|
32
31
|
PCMDistribution,
|
|
32
|
+
PCMRestriction,
|
|
33
33
|
PCMSpec,
|
|
34
34
|
PriceSpec,
|
|
35
35
|
SeedSequenceData,
|
|
@@ -44,7 +44,7 @@ from .data_generation_functions import (
|
|
|
44
44
|
gen_margin_price_data,
|
|
45
45
|
gen_share_data,
|
|
46
46
|
)
|
|
47
|
-
from .upp_tests import compute_upp_test_counts
|
|
47
|
+
from .upp_tests import compute_upp_test_counts
|
|
48
48
|
|
|
49
49
|
__version__ = VERSION
|
|
50
50
|
|
|
@@ -100,10 +100,10 @@ class MarketSample:
|
|
|
100
100
|
def _psv(self, _a: Attribute[PCMSpec], _v: PCMSpec, /) -> None:
|
|
101
101
|
if (
|
|
102
102
|
self.share_spec.recapture_form == RECForm.FIXED
|
|
103
|
-
and _v.
|
|
103
|
+
and _v.pcm_restriction == PCMRestriction.MNL
|
|
104
104
|
):
|
|
105
105
|
raise ValueError(
|
|
106
|
-
f'Specification of "PCMSpec.
|
|
106
|
+
f'Specification of "PCMSpec.pcm_restriction", as {PCMRestriction.MNL!r} '
|
|
107
107
|
f'requires that "ShareSpec.recapture_form" be {RECForm.INOUT!r} '
|
|
108
108
|
f"or {RECForm.OUTIN!r}, not {RECForm.FIXED!r} as presently specified"
|
|
109
109
|
)
|
|
@@ -179,7 +179,7 @@ class MarketSample:
|
|
|
179
179
|
shr_sample_size = sample_size * self.hsr_filing_test_type
|
|
180
180
|
shr_sample_size *= (
|
|
181
181
|
SSZConstant.MNL_DEP
|
|
182
|
-
if self.pcm_spec.
|
|
182
|
+
if self.pcm_spec.pcm_restriction == PCMRestriction.MNL
|
|
183
183
|
else 1
|
|
184
184
|
)
|
|
185
185
|
shr_sample_size = int(shr_sample_size)
|
|
@@ -379,28 +379,33 @@ class MarketSample:
|
|
|
379
379
|
|
|
380
380
|
sim_enf_cnts_kwargs: SamplingFunctionKWArgs = SamplingFunctionKWArgs({
|
|
381
381
|
"sample_size": subsample_sz,
|
|
382
|
-
"nthreads":
|
|
382
|
+
"nthreads": thread_count,
|
|
383
383
|
})
|
|
384
384
|
|
|
385
|
-
res_list = Parallel(n_jobs=thread_count, prefer="threads")(
|
|
385
|
+
res_list = Parallel(n_jobs=min(thread_count, iter_count), prefer="threads")(
|
|
386
386
|
delayed(self.__sim_enf_cnts)(
|
|
387
387
|
_enf_parm_vec,
|
|
388
388
|
_sim_test_regime,
|
|
389
389
|
**sim_enf_cnts_kwargs,
|
|
390
390
|
seed_data=_rng_seed_data_ch,
|
|
391
391
|
)
|
|
392
|
-
for
|
|
392
|
+
for _rng_seed_data_ch in rng_seed_data
|
|
393
393
|
)
|
|
394
394
|
|
|
395
395
|
res_list_stacks = UPPTestsCounts(*[
|
|
396
396
|
np.stack([getattr(_j, _k) for _j in res_list])
|
|
397
397
|
for _k in ("by_firm_count", "by_delta", "by_conczone")
|
|
398
398
|
])
|
|
399
|
+
|
|
399
400
|
upp_test_results = UPPTestsCounts(*[
|
|
400
|
-
|
|
401
|
-
(
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
(
|
|
402
|
+
np.array([], int)
|
|
403
|
+
if not (_gv := getattr(res_list_stacks, _g.name)).any()
|
|
404
|
+
else np.hstack((
|
|
405
|
+
_gv[0, :, :_h],
|
|
406
|
+
np.einsum("ijk->jk", _gv[:, :, _h:], dtype=int),
|
|
407
|
+
))
|
|
408
|
+
)
|
|
404
409
|
for _g, _h in zip(res_list_stacks.__attrs_attrs__, [1, 1, 3], strict=True)
|
|
405
410
|
])
|
|
406
411
|
del res_list, res_list_stacks
|
{mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/gen/data_generation_functions.py
RENAMED
|
@@ -28,9 +28,9 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
28
28
|
from . import (
|
|
29
29
|
DEFAULT_BETA_BND_DIST_PARMS,
|
|
30
30
|
DEFAULT_FCOUNT_WTS,
|
|
31
|
-
FM2Constraint,
|
|
32
31
|
MarginDataSample,
|
|
33
32
|
PCMDistribution,
|
|
33
|
+
PCMRestriction,
|
|
34
34
|
PCMSpec,
|
|
35
35
|
PriceDataSample,
|
|
36
36
|
PriceSpec,
|
|
@@ -549,11 +549,11 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
549
549
|
# del _price_array_gen
|
|
550
550
|
case PriceSpec.CSY:
|
|
551
551
|
# TODO:
|
|
552
|
-
# evolve
|
|
552
|
+
# evolve PCMRestriction (save running MNL test twice); evolve copy of _mkt_sample_spec=1q
|
|
553
553
|
# generate the margin data
|
|
554
554
|
# generate price and margin data
|
|
555
555
|
frmshr_array_plus = np.hstack((_frmshr_array, _nth_firm_share))
|
|
556
|
-
pcm_spec_here = evolve(_pcm_spec,
|
|
556
|
+
pcm_spec_here = evolve(_pcm_spec, pcm_restriction=PCMRestriction.IID)
|
|
557
557
|
margin_data = _gen_margin_data(
|
|
558
558
|
frmshr_array_plus,
|
|
559
559
|
np.ones_like(frmshr_array_plus, np.float64),
|
|
@@ -570,7 +570,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
570
570
|
price_array_here = 1 / (1 - pcm_array)
|
|
571
571
|
price_array = price_array_here[:, :2]
|
|
572
572
|
nth_firm_price = price_array_here[:, [-1]]
|
|
573
|
-
if _pcm_spec.
|
|
573
|
+
if _pcm_spec.pcm_restriction == PCMRestriction.MNL:
|
|
574
574
|
# Generate i.i.d. PCMs then take PCM0 and construct PCM1
|
|
575
575
|
# Regenerate MNL test
|
|
576
576
|
purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
@@ -665,14 +665,13 @@ def _gen_margin_data(
|
|
|
665
665
|
_nthreads: int,
|
|
666
666
|
/,
|
|
667
667
|
) -> MarginDataSample:
|
|
668
|
-
dist_type_pcm, dist_parms_pcm,
|
|
669
|
-
getattr(_pcm_spec, _f)
|
|
670
|
-
for _f in ("dist_type", "dist_parms", "firm2_pcm_constraint")
|
|
668
|
+
dist_type_pcm, dist_parms_pcm, pcm_restriction_ = (
|
|
669
|
+
getattr(_pcm_spec, _f) for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
671
670
|
)
|
|
672
671
|
|
|
673
672
|
pcm_array = (
|
|
674
673
|
np.empty_like(_frmshr_array[:, :1])
|
|
675
|
-
if _pcm_spec.
|
|
674
|
+
if _pcm_spec.pcm_restriction == PCMRestriction.SYM
|
|
676
675
|
else np.empty_like(_frmshr_array)
|
|
677
676
|
)
|
|
678
677
|
|
|
@@ -680,7 +679,10 @@ def _gen_margin_data(
|
|
|
680
679
|
beta_min, beta_max = [0.0] * 2 # placeholder
|
|
681
680
|
if dist_type_pcm == PCMDistribution.EMPR:
|
|
682
681
|
pcm_array = margin_data_resampler(
|
|
683
|
-
dist_parms_pcm,
|
|
682
|
+
dist_parms_pcm,
|
|
683
|
+
sample_size=pcm_array.shape,
|
|
684
|
+
seed_sequence=_pcm_rng_seed_seq,
|
|
685
|
+
nthreads=_nthreads,
|
|
684
686
|
)
|
|
685
687
|
else:
|
|
686
688
|
dist_type_: Literal["Beta", "Uniform"]
|
|
@@ -721,16 +723,10 @@ def _gen_margin_data(
|
|
|
721
723
|
pcm_array = (beta_max - beta_min) * pcm_array + beta_min
|
|
722
724
|
del beta_min, beta_max
|
|
723
725
|
|
|
724
|
-
if
|
|
726
|
+
if pcm_restriction_ == PCMRestriction.SYM:
|
|
725
727
|
pcm_array = np.hstack((pcm_array,) * _frmshr_array.shape[1])
|
|
726
|
-
if
|
|
728
|
+
if pcm_restriction_ == PCMRestriction.MNL:
|
|
727
729
|
# Impose FOCs from profit-maximization with MNL demand
|
|
728
|
-
if dist_type_pcm == PCMDistribution.EMPR:
|
|
729
|
-
print(
|
|
730
|
-
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
731
|
-
"the empirical distribution of margins in the source data. For "
|
|
732
|
-
"consistency, respecify pcm_spec.firm2_pcm_constraint = FM2Constraint.IID."
|
|
733
|
-
)
|
|
734
730
|
purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
735
731
|
|
|
736
732
|
pcm_array[:, [1]] = np.divide(
|
|
@@ -335,10 +335,13 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
335
335
|
# Logical-and of multiple vectors:
|
|
336
336
|
hhi_zone_test = (
|
|
337
337
|
1
|
|
338
|
-
* np.stack(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
* np.stack(
|
|
339
|
+
[
|
|
340
|
+
cnts_byhhipostanddelta[:, _idx] == _val
|
|
341
|
+
for _idx, _val in enumerate(zone_val)
|
|
342
|
+
],
|
|
343
|
+
axis=1,
|
|
344
|
+
)
|
|
342
345
|
).prod(axis=1) == 1
|
|
343
346
|
|
|
344
347
|
cnts_byconczone = np.vstack((
|
|
@@ -142,10 +142,6 @@ def compute_upp_test_counts( # noqa: PLR0914
|
|
|
142
142
|
hhi_delta_ranged,
|
|
143
143
|
np.ones_like(hhi_delta_ranged),
|
|
144
144
|
upp_test_arrays,
|
|
145
|
-
# *[
|
|
146
|
-
# 1 * getattr(upp_test_arrays, _a.name)
|
|
147
|
-
# for _a in upp_test_arrays.__attrs_attrs__
|
|
148
|
-
# ],
|
|
149
145
|
),
|
|
150
146
|
dtype=int,
|
|
151
147
|
)
|
|
@@ -194,11 +190,23 @@ def _compute_test_array_seq(
|
|
|
194
190
|
np.sqrt(np.einsum("ij,ij,ij->i", _wt_array[:, ::-1], _g, _g))[:, None]
|
|
195
191
|
for _g in _test_measure_seq
|
|
196
192
|
)
|
|
193
|
+
case UPPAggrSelector.CPG:
|
|
194
|
+
test_array_seq = (
|
|
195
|
+
np.expm1(
|
|
196
|
+
np.einsum("ij,ij->i", _wt_array[:, ::-1], np.log1p(_g))[:, None]
|
|
197
|
+
)
|
|
198
|
+
for _g in _test_measure_seq
|
|
199
|
+
)
|
|
197
200
|
case UPPAggrSelector.DIS:
|
|
198
201
|
test_array_seq = (
|
|
199
202
|
np.sqrt(1 / 2 * np.einsum("ij,ij->i", _g, _g))[:, None]
|
|
200
203
|
for _g in _test_measure_seq
|
|
201
204
|
)
|
|
205
|
+
case UPPAggrSelector.GMN:
|
|
206
|
+
test_array_seq = (
|
|
207
|
+
np.expm1(np.einsum("ij->i", np.log1p(_g))[:, None] / _g.shape[1])
|
|
208
|
+
for _g in _test_measure_seq
|
|
209
|
+
)
|
|
202
210
|
case UPPAggrSelector.MAX:
|
|
203
211
|
test_array_seq = (_g.max(axis=1, keepdims=True) for _g in _test_measure_seq)
|
|
204
212
|
case UPPAggrSelector.MIN:
|
|
@@ -210,7 +218,12 @@ def _compute_test_array_seq(
|
|
|
210
218
|
)
|
|
211
219
|
case UPPAggrSelector.OSD:
|
|
212
220
|
test_array_seq = (
|
|
213
|
-
np.sqrt(np.einsum("ij,ij,ij->i", _wt_array, _g, _g)
|
|
221
|
+
np.sqrt(np.einsum("ij,ij,ij->i", _wt_array, _g, _g)[:, None])
|
|
222
|
+
for _g in _test_measure_seq
|
|
223
|
+
)
|
|
224
|
+
case UPPAggrSelector.OSG:
|
|
225
|
+
test_array_seq = (
|
|
226
|
+
np.expm1(np.einsum("ij,ij->i", _wt_array, np.log1p(_g))[:, None])
|
|
214
227
|
for _g in _test_measure_seq
|
|
215
228
|
)
|
|
216
229
|
case _:
|
|
File without changes
|
|
File without changes
|
{mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/core/guidelines_boundary_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mergeron-2025.739319.1 → mergeron-2025.739319.3}/src/mergeron/data/damodaran_margin_data.xls
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|