mergeron 2024.739145.6__py3-none-any.whl → 2025.739265.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mergeron might be problematic. Click here for more details.
- mergeron/__init__.py +6 -3
- mergeron/core/__init__.py +2 -2
- mergeron/core/empirical_margin_distribution.py +3 -3
- mergeron/core/ftc_merger_investigations_data.py +1 -1
- mergeron/core/guidelines_boundaries.py +8 -9
- mergeron/core/guidelines_boundary_functions.py +1 -1
- mergeron/core/guidelines_boundary_functions_extra.py +4 -4
- mergeron/core/pseudorandom_numbers.py +115 -103
- mergeron/demo/visualize_empirical_margin_distribution.py +1 -1
- mergeron/gen/__init__.py +5 -5
- mergeron/gen/data_generation.py +3 -3
- mergeron/gen/data_generation_functions.py +11 -10
- mergeron/gen/upp_tests.py +5 -5
- {mergeron-2024.739145.6.dist-info → mergeron-2025.739265.2.dist-info}/METADATA +3 -3
- mergeron-2025.739265.2.dist-info/RECORD +23 -0
- {mergeron-2024.739145.6.dist-info → mergeron-2025.739265.2.dist-info}/WHEEL +1 -1
- mergeron-2024.739145.6.dist-info/RECORD +0 -23
mergeron/__init__.py
CHANGED
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from typing import Literal
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
from numpy.typing import NDArray
|
|
8
9
|
|
|
9
10
|
_PKG_NAME: str = Path(__file__).parent.stem
|
|
10
11
|
|
|
11
|
-
VERSION = "
|
|
12
|
+
VERSION = "2025.739265.2"
|
|
12
13
|
|
|
13
14
|
__version__ = VERSION
|
|
14
15
|
|
|
@@ -23,11 +24,13 @@ if not DATA_DIR.is_dir():
|
|
|
23
24
|
|
|
24
25
|
np.set_printoptions(precision=24, floatmode="fixed")
|
|
25
26
|
|
|
27
|
+
type HMGPubYear = Literal[1982, 1984, 1992, 2010, 2023]
|
|
28
|
+
|
|
26
29
|
type ArrayBoolean = NDArray[np.bool_]
|
|
27
|
-
type ArrayFloat = NDArray[np.
|
|
30
|
+
type ArrayFloat = NDArray[np.float16 | np.float32 | np.float64 | np.float128]
|
|
28
31
|
type ArrayINT = NDArray[np.intp]
|
|
29
32
|
|
|
30
|
-
type ArrayDouble = NDArray[np.
|
|
33
|
+
type ArrayDouble = NDArray[np.float64]
|
|
31
34
|
type ArrayBIGINT = NDArray[np.int64]
|
|
32
35
|
|
|
33
36
|
DEFAULT_REC_RATIO = 0.85
|
mergeron/core/__init__.py
CHANGED
|
@@ -132,7 +132,7 @@ def mgn_data_getter( # noqa: PLR0912
|
|
|
132
132
|
_xl_row[1] = int(_xl_row[1])
|
|
133
133
|
_mgn_dict[_xl_row[0]] = dict(zip(_mgn_row_keys[1:], _xl_row[1:], strict=True))
|
|
134
134
|
|
|
135
|
-
_ = _data_archive_path.write_bytes(msgpack.packb(_mgn_dict))
|
|
135
|
+
_ = _data_archive_path.write_bytes(msgpack.packb(_mgn_dict))
|
|
136
136
|
|
|
137
137
|
return MappingProxyType(_mgn_dict)
|
|
138
138
|
|
|
@@ -188,7 +188,7 @@ def mgn_data_builder(
|
|
|
188
188
|
|
|
189
189
|
|
|
190
190
|
def mgn_data_resampler(
|
|
191
|
-
_sample_size: int | tuple[int,
|
|
191
|
+
_sample_size: int | tuple[int, ...] = (10**6, 2),
|
|
192
192
|
/,
|
|
193
193
|
*,
|
|
194
194
|
seed_sequence: SeedSequence | None = None,
|
|
@@ -221,7 +221,7 @@ def mgn_data_resampler(
|
|
|
221
221
|
_x, _w, _ = mgn_data_builder(mgn_data_getter())
|
|
222
222
|
|
|
223
223
|
_mgn_kde = stats.gaussian_kde(_x, weights=_w, bw_method="silverman")
|
|
224
|
-
_mgn_kde.set_bandwidth(bw_method=_mgn_kde.factor / 3.0)
|
|
224
|
+
_mgn_kde.set_bandwidth(bw_method=_mgn_kde.factor / 3.0)
|
|
225
225
|
|
|
226
226
|
if isinstance(_sample_size, int):
|
|
227
227
|
return np.array(
|
|
@@ -18,6 +18,7 @@ from .. import ( # noqa: TID252
|
|
|
18
18
|
DEFAULT_REC_RATIO,
|
|
19
19
|
VERSION,
|
|
20
20
|
ArrayDouble,
|
|
21
|
+
HMGPubYear,
|
|
21
22
|
RECForm,
|
|
22
23
|
UPPAggrSelector,
|
|
23
24
|
)
|
|
@@ -29,8 +30,6 @@ __version__ = VERSION
|
|
|
29
30
|
mp.dps = 32
|
|
30
31
|
mp.trap_complex = True
|
|
31
32
|
|
|
32
|
-
type HMGPubYear = Literal[1982, 1984, 1992, 2010, 2023]
|
|
33
|
-
|
|
34
33
|
|
|
35
34
|
@dataclass(frozen=True)
|
|
36
35
|
class HMGThresholds:
|
|
@@ -55,7 +54,7 @@ class GuidelinesThresholds:
|
|
|
55
54
|
"""
|
|
56
55
|
|
|
57
56
|
pub_year: HMGPubYear = field(
|
|
58
|
-
kw_only=
|
|
57
|
+
kw_only=False,
|
|
59
58
|
default=2023,
|
|
60
59
|
validator=validators.in_([1982, 1984, 1992, 2010, 2023]),
|
|
61
60
|
)
|
|
@@ -157,7 +156,7 @@ class ConcentrationBoundary:
|
|
|
157
156
|
"ΔHHI", "Combined share", "Pre-merger HHI", "Post-merger HHI"
|
|
158
157
|
] = field(kw_only=False, default="ΔHHI")
|
|
159
158
|
|
|
160
|
-
@measure_name.validator
|
|
159
|
+
@measure_name.validator
|
|
161
160
|
def __mnv(
|
|
162
161
|
_instance: ConcentrationBoundary, _attribute: Attribute[str], _value: str, /
|
|
163
162
|
) -> None:
|
|
@@ -171,7 +170,7 @@ class ConcentrationBoundary:
|
|
|
171
170
|
|
|
172
171
|
threshold: float = field(kw_only=False, default=0.01)
|
|
173
172
|
|
|
174
|
-
@threshold.validator
|
|
173
|
+
@threshold.validator
|
|
175
174
|
def __tv(
|
|
176
175
|
_instance: ConcentrationBoundary, _attribute: Attribute[float], _value: float, /
|
|
177
176
|
) -> None:
|
|
@@ -221,14 +220,14 @@ class DiversionRatioBoundary:
|
|
|
221
220
|
|
|
222
221
|
diversion_ratio: float = field(kw_only=False, default=0.065)
|
|
223
222
|
|
|
224
|
-
@diversion_ratio.validator
|
|
223
|
+
@diversion_ratio.validator
|
|
225
224
|
def __dvv(
|
|
226
225
|
_instance: DiversionRatioBoundary,
|
|
227
226
|
_attribute: Attribute[float],
|
|
228
227
|
_value: float,
|
|
229
228
|
/,
|
|
230
229
|
) -> None:
|
|
231
|
-
if not 0 <= _value <= 1:
|
|
230
|
+
if not (isinstance(_value, float) and 0 <= _value <= 1):
|
|
232
231
|
raise ValueError(
|
|
233
232
|
"Margin-adjusted benchmark share ratio must lie between 0 and 1."
|
|
234
233
|
)
|
|
@@ -260,7 +259,7 @@ class DiversionRatioBoundary:
|
|
|
260
259
|
|
|
261
260
|
"""
|
|
262
261
|
|
|
263
|
-
@recapture_form.validator
|
|
262
|
+
@recapture_form.validator
|
|
264
263
|
def __rsv(
|
|
265
264
|
_instance: DiversionRatioBoundary,
|
|
266
265
|
_attribute: Attribute[RECForm],
|
|
@@ -356,7 +355,7 @@ class DiversionRatioBoundary:
|
|
|
356
355
|
|
|
357
356
|
_upp_agg_kwargs |= {"agg_method": _aggregator, "weighting": _wgt_type}
|
|
358
357
|
|
|
359
|
-
_boundary = _upp_agg_fn(_share_ratio, self.recapture_ratio, **_upp_agg_kwargs)
|
|
358
|
+
_boundary = _upp_agg_fn(_share_ratio, self.recapture_ratio, **_upp_agg_kwargs)
|
|
360
359
|
object.__setattr__(self, "coordinates", _boundary.coordinates)
|
|
361
360
|
object.__setattr__(self, "area", _boundary.area)
|
|
362
361
|
|
|
@@ -91,7 +91,7 @@ def hhi_delta_boundary_qdtr(_dh_val: float = 0.01, /) -> GuidelinesBoundaryCalla
|
|
|
91
91
|
|
|
92
92
|
_hhi_bdry_area = 2 * (
|
|
93
93
|
_s_nought
|
|
94
|
-
+ mp.quad(lambdify(_s_1, _hhi_bdry, "mpmath"), (_s_nought, 1 - _s_nought))
|
|
94
|
+
+ mp.quad(lambdify(_s_1, _hhi_bdry, "mpmath"), (_s_nought, 1 - _s_nought))
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
return GuidelinesBoundaryCallable(
|
|
@@ -159,7 +159,7 @@ def shrratio_boundary_qdtr_wtd_avg(
|
|
|
159
159
|
2
|
|
160
160
|
* (
|
|
161
161
|
_s_naught
|
|
162
|
-
+ mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (_s_naught, _s_mid))
|
|
162
|
+
+ mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (_s_naught, _s_mid))
|
|
163
163
|
)
|
|
164
164
|
- (_s_mid**2 + _s_naught**2)
|
|
165
165
|
)
|
|
@@ -189,7 +189,7 @@ def shrratio_boundary_qdtr_wtd_avg(
|
|
|
189
189
|
),
|
|
190
190
|
(0, _s_mid),
|
|
191
191
|
)
|
|
192
|
-
).real
|
|
192
|
+
).real
|
|
193
193
|
- _s_mid**2
|
|
194
194
|
)
|
|
195
195
|
|
|
@@ -209,7 +209,7 @@ def shrratio_boundary_qdtr_wtd_avg(
|
|
|
209
209
|
|
|
210
210
|
_bdry_func = solve(_bdry_eqn, _s_2)[0]
|
|
211
211
|
_bdry_area = float(
|
|
212
|
-
2 * (mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (0, _s_mid)))
|
|
212
|
+
2 * (mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (0, _s_mid)))
|
|
213
213
|
- _s_mid**2
|
|
214
214
|
)
|
|
215
215
|
|
|
@@ -6,12 +6,15 @@ https://github.com/numpy/numpy/issues/16313.
|
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
9
11
|
import concurrent.futures
|
|
10
12
|
from collections.abc import Sequence
|
|
11
13
|
from multiprocessing import cpu_count
|
|
12
14
|
from typing import Literal
|
|
13
15
|
|
|
14
16
|
import numpy as np
|
|
17
|
+
from attrs import Attribute, define, field
|
|
15
18
|
from numpy.random import PCG64DXSM, Generator, SeedSequence
|
|
16
19
|
|
|
17
20
|
from .. import VERSION, ArrayDouble # noqa: TID252
|
|
@@ -19,7 +22,8 @@ from .. import VERSION, ArrayDouble # noqa: TID252
|
|
|
19
22
|
__version__ = VERSION
|
|
20
23
|
|
|
21
24
|
NTHREADS = 2 * cpu_count()
|
|
22
|
-
DEFAULT_DIST_PARMS = np.array([0.0, 1.0],
|
|
25
|
+
DEFAULT_DIST_PARMS: ArrayDouble = np.array([0.0, 1.0], float)
|
|
26
|
+
DEFAULT_BETA_DIST_PARMS: ArrayDouble = np.array([1.0, 1.0], float)
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def prng(_s: SeedSequence | None = None, /) -> np.random.Generator:
|
|
@@ -106,6 +110,7 @@ def gen_seed_seq_list_default(
|
|
|
106
110
|
return [SeedSequence(_s, pool_size=8) for _s in generated_entropy[:_sseq_list_len]]
|
|
107
111
|
|
|
108
112
|
|
|
113
|
+
@define
|
|
109
114
|
class MultithreadedRNG:
|
|
110
115
|
"""Fill given array on demand with pseudo-random numbers as specified.
|
|
111
116
|
|
|
@@ -114,93 +119,102 @@ class MultithreadedRNG:
|
|
|
114
119
|
If a seed sequence is provided, it is used in a thread-safe way
|
|
115
120
|
to generate repeatable i.i.d. draws. All arguments are validated
|
|
116
121
|
before commencing multithreaded random number generation.
|
|
122
|
+
"""
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
_out_array
|
|
121
|
-
The output array to which generated data are written.
|
|
122
|
-
Its dimensions define the size of the sample.
|
|
123
|
-
dist_type
|
|
124
|
-
Distribution for the generated random numbers
|
|
125
|
-
dist_parms
|
|
126
|
-
Parameters, if any, for tailoring random number generation
|
|
127
|
-
seed_sequence
|
|
128
|
-
SeedSequence object for generating repeatable draws.
|
|
129
|
-
nthreads
|
|
130
|
-
Number of threads to spawn for random number generation.
|
|
124
|
+
values: ArrayDouble = field(kw_only=False, default=None)
|
|
125
|
+
"""Output array to which generated data are over-written
|
|
131
126
|
|
|
127
|
+
Array-length defines the number of i.i.d. (vector) draws.
|
|
132
128
|
"""
|
|
133
129
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
):
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if dist_type not in (_rdts := ("Beta", "Dirichlet", "Normal", "Uniform")):
|
|
156
|
-
raise ValueError("Specified distribution must be one of {_rdts}")
|
|
157
|
-
|
|
158
|
-
if not (dist_parms is None or isinstance(dist_parms, Sequence | np.ndarray)):
|
|
159
|
-
raise ValueError(
|
|
160
|
-
"When specified, distribution parameters must be a list, tuple or Numpy array"
|
|
161
|
-
)
|
|
162
|
-
if isinstance(dist_parms, Sequence):
|
|
163
|
-
dist_parms = np.array(dist_parms)
|
|
164
|
-
elif not dist_parms.any():
|
|
165
|
-
dist_parms = None
|
|
166
|
-
|
|
167
|
-
self.dist_type = dist_type
|
|
168
|
-
|
|
169
|
-
if dist_parms is None or np.array_equal(dist_parms, DEFAULT_DIST_PARMS):
|
|
170
|
-
match dist_type:
|
|
171
|
-
case "Uniform":
|
|
172
|
-
self.dist_type = "Random"
|
|
173
|
-
case "Normal":
|
|
174
|
-
self.dist_type = "Gaussian"
|
|
175
|
-
case "Beta" | "Dirichlet":
|
|
176
|
-
raise ValueError(
|
|
177
|
-
f"parameter specification, {f'"{dist_parms}"'} "
|
|
178
|
-
f"is invalid for specified distribution, f{'"{dist_type}"'}."
|
|
179
|
-
)
|
|
180
|
-
case _:
|
|
181
|
-
raise ValueError(
|
|
182
|
-
f"Invalid distributions specified, {f'"{dist_type}"'}."
|
|
183
|
-
)
|
|
130
|
+
dist_type: Literal[
|
|
131
|
+
"Beta", "Dirichlet", "Gaussian", "Normal", "Random", "Uniform"
|
|
132
|
+
] = field(kw_only=True, default="Uniform")
|
|
133
|
+
"""Distribution for the generated random numbers.
|
|
134
|
+
|
|
135
|
+
Default is "Uniform".
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
@dist_type.validator
|
|
139
|
+
def __dtv(
|
|
140
|
+
_instance: MultithreadedRNG, _attribute: Attribute[str], _value: str, /
|
|
141
|
+
) -> None:
|
|
142
|
+
if _value not in (
|
|
143
|
+
_rdts := ("Beta", "Dirichlet", "Gaussian", "Normal", "Random", "Uniform")
|
|
144
|
+
):
|
|
145
|
+
raise ValueError(f"Specified distribution must be one of {_rdts}")
|
|
146
|
+
|
|
147
|
+
dist_parms: ArrayDouble | None = field(kw_only=True, default=DEFAULT_DIST_PARMS)
|
|
148
|
+
"""Parameters, if any, for tailoring random number generation
|
|
149
|
+
"""
|
|
184
150
|
|
|
185
|
-
|
|
186
|
-
|
|
151
|
+
@dist_parms.validator
|
|
152
|
+
def __dpv(
|
|
153
|
+
_instance: MultithreadedRNG, _attribute: Attribute[str], _value: ArrayDouble, /
|
|
154
|
+
) -> None:
|
|
155
|
+
if _value is not None:
|
|
156
|
+
if not isinstance(_value, Sequence | np.ndarray):
|
|
187
157
|
raise ValueError(
|
|
188
|
-
|
|
189
|
-
f"of size, {_out_array.shape}"
|
|
158
|
+
"When specified, distribution parameters must be a list, tuple or Numpy array"
|
|
190
159
|
)
|
|
191
160
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
161
|
+
elif (
|
|
162
|
+
_instance.dist_type != "Dirichlet"
|
|
163
|
+
and (_lrdp := len(_value)) != (_trdp := 2)
|
|
164
|
+
) or (
|
|
165
|
+
_instance.dist_type == "Dirichlet"
|
|
166
|
+
and (_lrdp := len(_value)) != (_trdp := _instance.values.shape[1])
|
|
167
|
+
):
|
|
168
|
+
raise ValueError(f"Expected {_trdp} parameters, got, {_lrdp}")
|
|
169
|
+
|
|
170
|
+
elif (
|
|
171
|
+
_instance.dist_type in ("Beta", "Dirichlet")
|
|
172
|
+
and (np.array(_value) <= 0.0).any()
|
|
173
|
+
):
|
|
174
|
+
raise ValueError(
|
|
175
|
+
"Shape and location parameters must be strictly positive"
|
|
176
|
+
)
|
|
196
177
|
|
|
197
|
-
|
|
198
|
-
|
|
178
|
+
seed_sequence: SeedSequence | None = field(kw_only=True, default=None)
|
|
179
|
+
"""Seed sequence for generating random numbers."""
|
|
199
180
|
|
|
200
|
-
|
|
181
|
+
nthreads: int = field(kw_only=True, default=NTHREADS)
|
|
182
|
+
"""Number of threads to spawn for random number generation."""
|
|
201
183
|
|
|
202
184
|
def fill(self) -> None:
|
|
203
|
-
"""Fill the provided output array with random
|
|
185
|
+
"""Fill the provided output array with random number draws as specified."""
|
|
186
|
+
|
|
187
|
+
if (
|
|
188
|
+
self.dist_parms is None
|
|
189
|
+
or not (
|
|
190
|
+
_dist_parms := np.array(self.dist_parms) # one-shot conversion
|
|
191
|
+
).any()
|
|
192
|
+
):
|
|
193
|
+
if self.dist_type == "Beta":
|
|
194
|
+
_dist_parms = DEFAULT_BETA_DIST_PARMS
|
|
195
|
+
elif self.dist_type == "Dirichlet":
|
|
196
|
+
_dist_parms = np.ones(self.values.shape[1], float)
|
|
197
|
+
else:
|
|
198
|
+
_dist_parms = DEFAULT_DIST_PARMS
|
|
199
|
+
|
|
200
|
+
if self.dist_parms is None or np.array_equal(
|
|
201
|
+
self.dist_parms, DEFAULT_DIST_PARMS
|
|
202
|
+
):
|
|
203
|
+
if self.dist_type == "Uniform":
|
|
204
|
+
_dist_type = "Random"
|
|
205
|
+
elif self.dist_type == "Normal":
|
|
206
|
+
_dist_type = "Gaussian"
|
|
207
|
+
else:
|
|
208
|
+
_dist_type = self.dist_type
|
|
209
|
+
|
|
210
|
+
_step_size = (len(self.values) / self.nthreads).__ceil__()
|
|
211
|
+
# int; function gives float unsuitable for slicing
|
|
212
|
+
|
|
213
|
+
_seed_sequence = self.seed_sequence or SeedSequence(pool_size=8)
|
|
214
|
+
|
|
215
|
+
_random_generators = tuple(
|
|
216
|
+
prng(_t) for _t in _seed_sequence.spawn(self.nthreads)
|
|
217
|
+
)
|
|
204
218
|
|
|
205
219
|
def _fill(
|
|
206
220
|
_rng: np.random.Generator,
|
|
@@ -213,37 +227,35 @@ class MultithreadedRNG:
|
|
|
213
227
|
) -> None:
|
|
214
228
|
_sz: tuple[int, ...] = _out[_first:_last].shape
|
|
215
229
|
match _dist_type:
|
|
216
|
-
case "Random":
|
|
217
|
-
_rng.random(out=_out[_first:_last])
|
|
218
|
-
case "Uniform":
|
|
219
|
-
_uni_l, _uni_h = _dist_parms
|
|
220
|
-
_out[_first:_last] = _rng.uniform(_uni_l, _uni_h, size=_sz)
|
|
221
|
-
case "Dirichlet":
|
|
222
|
-
_out[_first:_last] = _rng.dirichlet(_dist_parms, size=_sz[:-1])
|
|
223
230
|
case "Beta":
|
|
224
231
|
_shape_a, _shape_b = _dist_parms
|
|
225
232
|
_out[_first:_last] = _rng.beta(_shape_a, _shape_b, size=_sz)
|
|
233
|
+
case "Dirichlet":
|
|
234
|
+
_out[_first:_last] = _rng.dirichlet(_dist_parms, size=_sz[:-1])
|
|
235
|
+
case "Gaussian":
|
|
236
|
+
_rng.standard_normal(out=_out[_first:_last])
|
|
226
237
|
case "Normal":
|
|
227
238
|
_mu, _sigma = _dist_parms
|
|
228
239
|
_out[_first:_last] = _rng.normal(_mu, _sigma, size=_sz)
|
|
240
|
+
case "Random":
|
|
241
|
+
_rng.random(out=_out[_first:_last])
|
|
242
|
+
case "Uniform":
|
|
243
|
+
_uni_l, _uni_h = _dist_parms
|
|
244
|
+
_out[_first:_last] = _rng.uniform(_uni_l, _uni_h, size=_sz)
|
|
229
245
|
case _:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
futures
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
concurrent.futures.wait(futures)
|
|
247
|
-
|
|
248
|
-
def __del__(self) -> None:
|
|
249
|
-
self.executor.shutdown(False)
|
|
246
|
+
"Unreachable. The validator would have rejected this as invalid."
|
|
247
|
+
|
|
248
|
+
with concurrent.futures.ThreadPoolExecutor(self.nthreads) as _executor:
|
|
249
|
+
for i in range(self.nthreads):
|
|
250
|
+
_range_first = i * _step_size
|
|
251
|
+
_range_last = min(len(self.values), (i + 1) * _step_size)
|
|
252
|
+
|
|
253
|
+
_executor.submit(
|
|
254
|
+
_fill,
|
|
255
|
+
_random_generators[i],
|
|
256
|
+
_dist_type,
|
|
257
|
+
_dist_parms,
|
|
258
|
+
self.values,
|
|
259
|
+
_range_first,
|
|
260
|
+
_range_last,
|
|
261
|
+
)
|
|
@@ -45,7 +45,7 @@ with warnings.catch_warnings():
|
|
|
45
45
|
])
|
|
46
46
|
|
|
47
47
|
mgn_kde = stats.gaussian_kde(mgn_data_obs, weights=mgn_data_wts, bw_method="silverman")
|
|
48
|
-
mgn_kde.set_bandwidth(bw_method=mgn_kde.factor / 3.0)
|
|
48
|
+
mgn_kde.set_bandwidth(bw_method=mgn_kde.factor / 3.0)
|
|
49
49
|
|
|
50
50
|
mgn_ax.plot(
|
|
51
51
|
(_xv := np.linspace(0, BIN_COUNT, 10**5) / BIN_COUNT),
|
mergeron/gen/__init__.py
CHANGED
|
@@ -70,7 +70,7 @@ class SHRDistribution(enum.StrEnum):
|
|
|
70
70
|
DIR_FLAT_CONSTR = "Flat Dirichlet - Constrained"
|
|
71
71
|
"""Impose minimum probablility weight on each firm-count
|
|
72
72
|
|
|
73
|
-
Only firm-counts with probability weight of
|
|
73
|
+
Only firm-counts with probability weight of 3% or more
|
|
74
74
|
are included for data generation.
|
|
75
75
|
"""
|
|
76
76
|
|
|
@@ -119,7 +119,7 @@ class ShareSpec:
|
|
|
119
119
|
dist_type: SHRDistribution = field(default=SHRDistribution.DIR_FLAT)
|
|
120
120
|
"""See :class:`SHRDistribution`"""
|
|
121
121
|
|
|
122
|
-
@dist_type.validator
|
|
122
|
+
@dist_type.validator
|
|
123
123
|
def __dtv(
|
|
124
124
|
_i: ShareSpec, _a: Attribute[SHRDistribution], _v: SHRDistribution
|
|
125
125
|
) -> None:
|
|
@@ -152,7 +152,7 @@ class ShareSpec:
|
|
|
152
152
|
|
|
153
153
|
"""
|
|
154
154
|
|
|
155
|
-
@dist_parms.validator
|
|
155
|
+
@dist_parms.validator
|
|
156
156
|
def __dpv(
|
|
157
157
|
_i: ShareSpec,
|
|
158
158
|
_a: Attribute[ArrayFloat | ArrayINT | None],
|
|
@@ -205,7 +205,7 @@ class ShareSpec:
|
|
|
205
205
|
|
|
206
206
|
"""
|
|
207
207
|
|
|
208
|
-
@recapture_ratio.validator
|
|
208
|
+
@recapture_ratio.validator
|
|
209
209
|
def __rrv(_i: ShareSpec, _a: Attribute[float], _v: float) -> None:
|
|
210
210
|
if _v and not (0 < _v <= 1):
|
|
211
211
|
raise ValueError("Recapture ratio must lie in the interval, [0, 1).")
|
|
@@ -265,7 +265,7 @@ class PCMSpec:
|
|
|
265
265
|
|
|
266
266
|
"""
|
|
267
267
|
|
|
268
|
-
@dist_parms.validator
|
|
268
|
+
@dist_parms.validator
|
|
269
269
|
def __dpv(
|
|
270
270
|
_i: PCMSpec, _a: Attribute[ArrayDouble | None], _v: ArrayDouble | None
|
|
271
271
|
) -> None:
|
mergeron/gen/data_generation.py
CHANGED
|
@@ -85,7 +85,7 @@ class MarketSample:
|
|
|
85
85
|
)
|
|
86
86
|
"""Margin specification, see :class:`PCMSpec`"""
|
|
87
87
|
|
|
88
|
-
@pcm_spec.validator
|
|
88
|
+
@pcm_spec.validator
|
|
89
89
|
def __psv(self, _a: Attribute[PCMSpec], _v: PCMSpec, /) -> None:
|
|
90
90
|
if (
|
|
91
91
|
self.share_spec.recapture_form == RECForm.FIXED
|
|
@@ -437,8 +437,8 @@ class MarketSample:
|
|
|
437
437
|
_enf_parm_vec,
|
|
438
438
|
_sim_test_regime,
|
|
439
439
|
**_sim_enf_cnts_kwargs,
|
|
440
|
-
saved_array_name_suffix=f"{saved_array_name_suffix}_{_iter_id:0{2 + int(np.ceil(np.log10(_iter_count)))}d}",
|
|
441
|
-
seed_seq_list=_rng_seed_seq_list_ch,
|
|
440
|
+
saved_array_name_suffix=f"{saved_array_name_suffix}_{_iter_id:0{2 + int(np.ceil(np.log10(_iter_count)))}d}",
|
|
441
|
+
seed_seq_list=_rng_seed_seq_list_ch,
|
|
442
442
|
)
|
|
443
443
|
for _iter_id, _rng_seed_seq_list_ch in enumerate(_rng_seed_seq_list)
|
|
444
444
|
)
|
|
@@ -138,7 +138,7 @@ def gen_market_shares_uniform(
|
|
|
138
138
|
|
|
139
139
|
"""
|
|
140
140
|
|
|
141
|
-
_frmshr_array = np.empty((_s_size, 2), dtype=np.float64)
|
|
141
|
+
_frmshr_array: ArrayDouble = np.empty((_s_size, 2), dtype=np.float64)
|
|
142
142
|
|
|
143
143
|
_dist_parms_mktshr = _dist_parms_mktshr or DEFAULT_DIST_PARMS
|
|
144
144
|
_mrng = MultithreadedRNG(
|
|
@@ -151,10 +151,11 @@ def gen_market_shares_uniform(
|
|
|
151
151
|
_mrng.fill()
|
|
152
152
|
# Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2 <= 1
|
|
153
153
|
_frmshr_array.sort(axis=1)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
_frmshr_array[:, 1] - _frmshr_array[:, 0],
|
|
157
|
-
|
|
154
|
+
|
|
155
|
+
_frmshr_array = np.array(
|
|
156
|
+
(_frmshr_array[:, 0], _frmshr_array[:, 1] - _frmshr_array[:, 0]),
|
|
157
|
+
_frmshr_array.dtype,
|
|
158
|
+
).T # faster than np.stack() and variants
|
|
158
159
|
|
|
159
160
|
# Keep only share combinations representing feasible mergers
|
|
160
161
|
# This is a no-op for 64-bit floats, but is necessary for 32-bit floats
|
|
@@ -372,7 +373,7 @@ def gen_market_shares_dirichlet(
|
|
|
372
373
|
else SeedSequence(pool_size=8)
|
|
373
374
|
)
|
|
374
375
|
|
|
375
|
-
_mktshr_array = np.empty((_s_size, len(_dir_alphas)))
|
|
376
|
+
_mktshr_array = np.empty((_s_size, len(_dir_alphas)), dtype=np.float64)
|
|
376
377
|
_mrng = MultithreadedRNG(
|
|
377
378
|
_mktshr_array,
|
|
378
379
|
dist_type="Dirichlet",
|
|
@@ -399,8 +400,8 @@ def gen_market_shares_dirichlet(
|
|
|
399
400
|
_aggregate_purchase_prob = np.empty((_s_size, 1), dtype=np.float64)
|
|
400
401
|
_aggregate_purchase_prob.fill(np.nan)
|
|
401
402
|
if _recapture_form == RECForm.OUTIN:
|
|
402
|
-
_aggregate_purchase_prob = 1 - _mktshr_array[:, [-1]]
|
|
403
|
-
_mktshr_array = _mktshr_array[:, :-1] / _aggregate_purchase_prob
|
|
403
|
+
_aggregate_purchase_prob = 1 - _mktshr_array[:, [-1]] # type: ignore
|
|
404
|
+
_mktshr_array = _mktshr_array[:, :-1] / _aggregate_purchase_prob # type: ignore
|
|
404
405
|
|
|
405
406
|
return ShareDataSample(
|
|
406
407
|
_mktshr_array,
|
|
@@ -540,7 +541,7 @@ def gen_margin_price_data(
|
|
|
540
541
|
_pr_max_ratio = 5.0
|
|
541
542
|
match _price_spec:
|
|
542
543
|
case PriceSpec.SYM:
|
|
543
|
-
_nth_firm_price = np.ones((len(_frmshr_array), 1))
|
|
544
|
+
_nth_firm_price = np.ones((len(_frmshr_array), 1), np.float64)
|
|
544
545
|
case PriceSpec.POS:
|
|
545
546
|
_price_array, _nth_firm_price = (
|
|
546
547
|
np.ceil(_p * _pr_max_ratio) for _p in (_frmshr_array, _nth_firm_share)
|
|
@@ -555,7 +556,7 @@ def gen_margin_price_data(
|
|
|
555
556
|
1 + np.arange(_pr_max_ratio), size=(len(_frmshr_array), 3)
|
|
556
557
|
)
|
|
557
558
|
_price_array = _price_array_gen[:, :2]
|
|
558
|
-
_nth_firm_price = _price_array_gen[:, [2]]
|
|
559
|
+
_nth_firm_price = _price_array_gen[:, [2]] # type: ignore
|
|
559
560
|
# del _price_array_gen
|
|
560
561
|
case PriceSpec.CSY:
|
|
561
562
|
# TODO:
|
mergeron/gen/upp_tests.py
CHANGED
|
@@ -21,7 +21,7 @@ from .. import ( # noqa
|
|
|
21
21
|
ArrayDouble,
|
|
22
22
|
ArrayFloat,
|
|
23
23
|
ArrayINT,
|
|
24
|
-
|
|
24
|
+
HMGPubYear,
|
|
25
25
|
UPPAggrSelector,
|
|
26
26
|
)
|
|
27
27
|
from ..core import guidelines_boundaries as gbl # noqa: TID252
|
|
@@ -326,16 +326,16 @@ def _compute_test_array_seq(
|
|
|
326
326
|
|
|
327
327
|
|
|
328
328
|
def initialize_hd5(
|
|
329
|
-
_h5_path: Path, _hmg_pub_year:
|
|
329
|
+
_h5_path: Path, _hmg_pub_year: HMGPubYear, _test_regime: UPPTestRegime, /
|
|
330
330
|
) -> tuple[SaveData, str]:
|
|
331
331
|
_h5_title = f"HMG version: {_hmg_pub_year}; Test regime: {_test_regime}"
|
|
332
332
|
if _h5_path.is_file():
|
|
333
333
|
_h5_path.unlink()
|
|
334
|
-
_h5_file = ptb.open_file(_h5_path, mode="w", title=_h5_title)
|
|
334
|
+
_h5_file = ptb.open_file(_h5_path, mode="w", title=_h5_title)
|
|
335
335
|
_save_data_to_file: SaveData = (True, _h5_file, _h5_file.root)
|
|
336
336
|
_next_subgroup_name_root = "enf_{}_{}_{}_{}".format(
|
|
337
337
|
_hmg_pub_year,
|
|
338
|
-
*(getattr(_test_regime, _f.name).name for _f in _test_regime.__attrs_attrs__),
|
|
338
|
+
*(getattr(_test_regime, _f.name).name for _f in _test_regime.__attrs_attrs__),
|
|
339
339
|
)
|
|
340
340
|
return _save_data_to_file, _next_subgroup_name_root
|
|
341
341
|
|
|
@@ -383,7 +383,7 @@ def save_array_to_hdf5(
|
|
|
383
383
|
_h5_array_name,
|
|
384
384
|
atom=ptb.Atom.from_dtype(_array_obj.dtype),
|
|
385
385
|
shape=_array_obj.shape,
|
|
386
|
-
filters=ptb.Filters(complevel=3, complib="blosc:lz4hc", fletcher32=True),
|
|
386
|
+
filters=ptb.Filters(complevel=3, complib="blosc:lz4hc", fletcher32=True),
|
|
387
387
|
)
|
|
388
388
|
_h5_array[:] = _array_obj
|
|
389
389
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2025.739265.2
|
|
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
|
|
7
7
|
Author: Murthy Kambhampaty
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
mergeron/__init__.py,sha256=oDHE97JzQ9OCiSHxFPy36aGGCgrxjDDNcV6b6FNwAMU,1581
|
|
2
|
+
mergeron/core/__init__.py,sha256=qYRA1D4Gx-WSkICqiueWLWPedkWmb_CIn7fgvN7unk4,182
|
|
3
|
+
mergeron/core/empirical_margin_distribution.py,sha256=byO11ROGVQcmbZVWdVJkJ906WnHhx7jcoL5BbxPmX7I,8688
|
|
4
|
+
mergeron/core/ftc_merger_investigations_data.py,sha256=9AT-phfkn0NHJvn9lcpbH8ExWZStYW47KQjNjDGS3qc,28709
|
|
5
|
+
mergeron/core/guidelines_boundaries.py,sha256=RMUhymRYmpzMKWftmXLB1mb4c8xlpLREWTSXaaaLSl8,15207
|
|
6
|
+
mergeron/core/guidelines_boundary_functions.py,sha256=4vMz5DQ72fgj7leUlVJvXBrxdZAeDJP7FoGBEVXDdeQ,34866
|
|
7
|
+
mergeron/core/guidelines_boundary_functions_extra.py,sha256=qL1zT-Xgj2qD2EyBRR87APoVc47MkGM02xYQhNgwEvg,11216
|
|
8
|
+
mergeron/core/pseudorandom_numbers.py,sha256=iH-j9W0omZ-z-3Op7uxaQI9ubRus7cfGB6mlFaAtExg,9739
|
|
9
|
+
mergeron/data/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
10
|
+
mergeron/data/damodaran_margin_data.xls,sha256=Qggl1p5nkOMJI8YUXhkwXQRz-OhRSqBTzz57N0JQyYA,79360
|
|
11
|
+
mergeron/data/damodaran_margin_data_dict.msgpack,sha256=sr6s4L69kposEpzGI7jpPb4ULz0UpY-bEYfeNi6UlRA,57621
|
|
12
|
+
mergeron/data/ftc_invdata.msgpack,sha256=WBFHgi7Ld4R-h2zL2Zc3TOIlKqVrbVFMH1LoI4-T-M0,264664
|
|
13
|
+
mergeron/demo/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
14
|
+
mergeron/demo/visualize_empirical_margin_distribution.py,sha256=kDwPfhsBjsQdYPyhe4KZG2guB_xHhPaCrvn8fezIz4M,2354
|
|
15
|
+
mergeron/gen/__init__.py,sha256=j0_YNykI7oX6SPKMc8UlNYljURaCfldRvBHRgL9oSrY,17134
|
|
16
|
+
mergeron/gen/data_generation.py,sha256=hipqq6VefXgGWC_t8NDQ3pk6ddzobJRkiCd5c6EbYC8,16815
|
|
17
|
+
mergeron/gen/data_generation_functions.py,sha256=jgq92EK4xTigJxIGgB6U8_VsFs5IXZ7543xwvaMfQhQ,29091
|
|
18
|
+
mergeron/gen/enforcement_stats.py,sha256=ZjrV_VkFMF0D1myc-fj-W99M1EhJMA9-nCfyE5g9e54,10890
|
|
19
|
+
mergeron/gen/upp_tests.py,sha256=ijCk2IipiJacR5cl3uvCOgLXOufQ_w4tUFJZed2xTbw,12490
|
|
20
|
+
mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
21
|
+
mergeron-2025.739265.2.dist-info/METADATA,sha256=4eJmepRftUJGqPGvwEAwsTHcbN284e8gViME-qiIb3A,14494
|
|
22
|
+
mergeron-2025.739265.2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
23
|
+
mergeron-2025.739265.2.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
mergeron/__init__.py,sha256=_vCtLR7rVeUup_mj2xDW5oRMy53g-rUEsS5C4iVk5sw,1477
|
|
2
|
-
mergeron/core/__init__.py,sha256=jPGd0okmvNOdWnTu4biR4hqnL29IERaY4Olv8mS02ko,188
|
|
3
|
-
mergeron/core/empirical_margin_distribution.py,sha256=iU72c6rPwqduH8GlvES1na2uvw5qXq0IQfsTWnL_ueQ,8726
|
|
4
|
-
mergeron/core/ftc_merger_investigations_data.py,sha256=_Qno1_oWcHmDd60j8YX6BLUSLeAn-DU6KpvMOcbCIaE,28728
|
|
5
|
-
mergeron/core/guidelines_boundaries.py,sha256=uKRIwTrGXKf8CbabjjqRcMO4wGvtAUrd2MrMICzgNvg,15336
|
|
6
|
-
mergeron/core/guidelines_boundary_functions.py,sha256=SoTamDsKrThqDfoNTHeGp2ntiwhzscWmkAbIWrBtUq4,34885
|
|
7
|
-
mergeron/core/guidelines_boundary_functions_extra.py,sha256=ytTWxYk7FkGLQubkQXrniwP_2ZlYssp4rmOxy4_7480,11292
|
|
8
|
-
mergeron/core/pseudorandom_numbers.py,sha256=ps5H3n7jC5iJq_ulrmGvTAFmMeNXq-7IPWQQog7q5TY,9225
|
|
9
|
-
mergeron/data/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
10
|
-
mergeron/data/damodaran_margin_data.xls,sha256=Qggl1p5nkOMJI8YUXhkwXQRz-OhRSqBTzz57N0JQyYA,79360
|
|
11
|
-
mergeron/data/damodaran_margin_data_dict.msgpack,sha256=sr6s4L69kposEpzGI7jpPb4ULz0UpY-bEYfeNi6UlRA,57621
|
|
12
|
-
mergeron/data/ftc_invdata.msgpack,sha256=WBFHgi7Ld4R-h2zL2Zc3TOIlKqVrbVFMH1LoI4-T-M0,264664
|
|
13
|
-
mergeron/demo/__init__.py,sha256=KtjBlZOl7jwBCAUhrTJB9PdrN39YLYytNiSUSM_gRmA,62
|
|
14
|
-
mergeron/demo/visualize_empirical_margin_distribution.py,sha256=_1SWKqFJNqKV_yA3v2tmzl8Th3CC3SXpb6VGiwGGuN0,2373
|
|
15
|
-
mergeron/gen/__init__.py,sha256=6A1HVewEEwLo4JtZZ61ShzhU2phEeNfTxmTPlpi5LXY,17215
|
|
16
|
-
mergeron/gen/data_generation.py,sha256=nzCWVJBXYxloR6J6MamFM0MUbDzguhARCAEksYkX1Dw,16872
|
|
17
|
-
mergeron/gen/data_generation_functions.py,sha256=QmM89KzDP-8hz4ibjWDiEn_KVulgUN_be19j-P9LdWU,28944
|
|
18
|
-
mergeron/gen/enforcement_stats.py,sha256=ZjrV_VkFMF0D1myc-fj-W99M1EhJMA9-nCfyE5g9e54,10890
|
|
19
|
-
mergeron/gen/upp_tests.py,sha256=vv9gBHQzabYxgcOBofTPHuNael8SbUAMfNW9c2H2C84,12548
|
|
20
|
-
mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
21
|
-
mergeron-2024.739145.6.dist-info/METADATA,sha256=fyG5_nVcSbg38bZxH-vLygIBr94ILYPiBGRLL1s3nfE,14483
|
|
22
|
-
mergeron-2024.739145.6.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
23
|
-
mergeron-2024.739145.6.dist-info/RECORD,,
|