mergeron 2024.738949.5__py3-none-any.whl → 2024.738949.6__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 +29 -0
- mergeron/core/__init__.py +65 -1
- mergeron/core/excel_helper.py +5 -12
- mergeron/core/guidelines_boundaries.py +116 -537
- mergeron/core/guidelines_boundaries_specialized_functions.py +341 -0
- mergeron/core/pseudorandom_numbers.py +1 -1
- mergeron/examples/concentration_as_diversion.py +45 -50
- mergeron/examples/enforcement_boundaries_for_mergers_with_asymmetric_shares.py +13 -7
- mergeron/examples/investigations_stats_obs_tables.py +13 -11
- mergeron/examples/investigations_stats_sim_tables.py +11 -12
- mergeron/examples/plotSafeHarbs_symbolically.py +7 -5
- mergeron/examples/sound_guppi_safeharbor.py +6 -7
- mergeron/examples/visualize_guidelines_tests.py +3 -5
- mergeron/gen/__init__.py +9 -33
- mergeron/gen/_data_generation_functions_nonpublic.py +5 -6
- mergeron/gen/data_generation.py +1 -2
- mergeron/gen/upp_tests.py +7 -7
- {mergeron-2024.738949.5.dist-info → mergeron-2024.738949.6.dist-info}/METADATA +1 -1
- {mergeron-2024.738949.5.dist-info → mergeron-2024.738949.6.dist-info}/RECORD +20 -19
- {mergeron-2024.738949.5.dist-info → mergeron-2024.738949.6.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Specialized routines for defining and analyzing boundaries for Guidelines standards.
|
|
3
|
+
|
|
4
|
+
These routines provide improved precision or demonstrate additional methods, but tend
|
|
5
|
+
to have poor performance
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from importlib.metadata import version
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from mpmath import mp, mpf # type: ignore
|
|
14
|
+
from scipy.spatial.distance import minkowski as distance_function # type: ignore
|
|
15
|
+
from sympy import lambdify, simplify, solve, symbols
|
|
16
|
+
|
|
17
|
+
from .. import _PKG_NAME # noqa: TID252
|
|
18
|
+
from .guidelines_boundaries import (
|
|
19
|
+
GuidelinesBoundary,
|
|
20
|
+
GuidelinesBoundaryCallable,
|
|
21
|
+
_shrratio_boundary_intcpt,
|
|
22
|
+
lerp,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__version__ = version(_PKG_NAME)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
mp.prec = 80
|
|
29
|
+
mp.trap_complex = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def delta_hhi_boundary_qdtr(_dh_val: float = 0.01) -> GuidelinesBoundaryCallable:
|
|
33
|
+
"""
|
|
34
|
+
Generate the list of share combination on the ΔHHI boundary.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
_dh_val:
|
|
39
|
+
Merging-firms' ΔHHI bound.
|
|
40
|
+
prec
|
|
41
|
+
Number of decimal places for rounding reported shares.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
Callable to generate array of share-pairs, area under boundary.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
_dh_val = mpf(f"{_dh_val}")
|
|
50
|
+
|
|
51
|
+
_s_1, _s_2 = symbols("s_1, s_2", positive=True)
|
|
52
|
+
|
|
53
|
+
_hhi_eqn = _s_2 - 0.01 / (2 * _s_1)
|
|
54
|
+
|
|
55
|
+
_hhi_bdry = solve(_hhi_eqn, _s_2)[0] # type: ignore
|
|
56
|
+
_s_nought = float(solve(_hhi_eqn.subs({_s_2: 1 - _s_1}), _s_1)[0]) # type: ignore
|
|
57
|
+
|
|
58
|
+
_hhi_bdry_area = 2 * (
|
|
59
|
+
_s_nought
|
|
60
|
+
+ mp.quad(lambdify(_s_1, _hhi_bdry, "mpmath"), (_s_nought, 1 - _s_nought))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return GuidelinesBoundaryCallable(
|
|
64
|
+
lambdify(_s_1, _hhi_bdry, "numpy"), _hhi_bdry_area, _s_nought
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def shrratio_boundary_qdtr_wtd_avg(
|
|
69
|
+
_delta_star: float = 0.075,
|
|
70
|
+
_r_val: float = 0.80,
|
|
71
|
+
/,
|
|
72
|
+
*,
|
|
73
|
+
weighting: Literal["own-share", "cross-product-share"] | None = "own-share",
|
|
74
|
+
recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
|
|
75
|
+
) -> GuidelinesBoundaryCallable:
|
|
76
|
+
"""
|
|
77
|
+
Share combinations for the share-weighted average GUPPI boundary with symmetric
|
|
78
|
+
merging-firm margins.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
_delta_star
|
|
83
|
+
corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
|
|
84
|
+
_r_val
|
|
85
|
+
recapture ratio
|
|
86
|
+
weighting
|
|
87
|
+
Whether "own-share" or "cross-product-share" (or None for simple, unweighted average)
|
|
88
|
+
recapture_spec
|
|
89
|
+
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
90
|
+
value for both merging firms ("proportional").
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
Array of share-pairs, area under boundary.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
_delta_star = mpf(f"{_delta_star}")
|
|
99
|
+
_s_mid = _delta_star / (1 + _delta_star)
|
|
100
|
+
_s_naught = 0
|
|
101
|
+
|
|
102
|
+
_s_1, _s_2 = symbols("s_1:3", positive=True)
|
|
103
|
+
|
|
104
|
+
match weighting:
|
|
105
|
+
case "own-share":
|
|
106
|
+
_bdry_eqn = (
|
|
107
|
+
_s_1 * _s_2 / (1 - _s_1)
|
|
108
|
+
+ _s_2
|
|
109
|
+
* _s_1
|
|
110
|
+
/ (
|
|
111
|
+
(1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
|
|
112
|
+
if recapture_spec == "inside-out"
|
|
113
|
+
else (1 - _s_2)
|
|
114
|
+
)
|
|
115
|
+
- (_s_1 + _s_2) * _delta_star
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
_bdry_func = solve(_bdry_eqn, _s_2)[0] # type: ignore
|
|
119
|
+
_s_naught = (
|
|
120
|
+
float(solve(simplify(_bdry_eqn.subs({_s_2: 1 - _s_1})), _s_1)[0]) # type: ignore
|
|
121
|
+
if recapture_spec == "inside-out"
|
|
122
|
+
else 0
|
|
123
|
+
)
|
|
124
|
+
_bdry_area = float(
|
|
125
|
+
2
|
|
126
|
+
* (
|
|
127
|
+
_s_naught
|
|
128
|
+
+ mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (_s_naught, _s_mid))
|
|
129
|
+
)
|
|
130
|
+
- (_s_mid**2 + _s_naught**2)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
case "cross-product-share":
|
|
134
|
+
mp.trap_complex = False
|
|
135
|
+
_d_star = symbols("d", positive=True)
|
|
136
|
+
_bdry_eqn = (
|
|
137
|
+
_s_2 * _s_2 / (1 - _s_1)
|
|
138
|
+
+ _s_1
|
|
139
|
+
* _s_1
|
|
140
|
+
/ (
|
|
141
|
+
(1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
|
|
142
|
+
if recapture_spec == "inside-out"
|
|
143
|
+
else (1 - _s_2)
|
|
144
|
+
)
|
|
145
|
+
- (_s_1 + _s_2) * _d_star
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
_bdry_func = solve(_bdry_eqn, _s_2)[1] # type: ignore
|
|
149
|
+
_bdry_area = float(
|
|
150
|
+
2
|
|
151
|
+
* (
|
|
152
|
+
mp.quad(
|
|
153
|
+
lambdify(
|
|
154
|
+
_s_1, _bdry_func.subs({_d_star: _delta_star}), "mpmath"
|
|
155
|
+
),
|
|
156
|
+
(0, _s_mid),
|
|
157
|
+
)
|
|
158
|
+
).real
|
|
159
|
+
- _s_mid**2
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
case _:
|
|
163
|
+
_bdry_eqn = (
|
|
164
|
+
1 / 2 * _s_2 / (1 - _s_1)
|
|
165
|
+
+ 1
|
|
166
|
+
/ 2
|
|
167
|
+
* _s_1
|
|
168
|
+
/ (
|
|
169
|
+
(1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
|
|
170
|
+
if recapture_spec == "inside-out"
|
|
171
|
+
else (1 - _s_2)
|
|
172
|
+
)
|
|
173
|
+
- _delta_star
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
_bdry_func = solve(_bdry_eqn, _s_2)[0] # type: ignore
|
|
177
|
+
_bdry_area = float(
|
|
178
|
+
2 * (mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (0, _s_mid)))
|
|
179
|
+
- _s_mid**2
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return GuidelinesBoundaryCallable(
|
|
183
|
+
lambdify(_s_1, _bdry_func, "numpy"), _bdry_area, _s_naught
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def shrratio_boundary_distance(
|
|
188
|
+
_delta_star: float = 0.075,
|
|
189
|
+
_r_val: float = 0.80,
|
|
190
|
+
/,
|
|
191
|
+
*,
|
|
192
|
+
agg_method: Literal["arithmetic", "distance"] = "arithmetic",
|
|
193
|
+
weighting: Literal["own-share", "cross-product-share"] | None = "own-share",
|
|
194
|
+
recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
|
|
195
|
+
prec: int = 5,
|
|
196
|
+
) -> GuidelinesBoundary:
|
|
197
|
+
"""
|
|
198
|
+
Share combinations for the GUPPI boundaries using various aggregators with
|
|
199
|
+
symmetric merging-firm margins.
|
|
200
|
+
|
|
201
|
+
Reimplements the arithmetic-averages and distance estimations from function,
|
|
202
|
+
`shrratio_boundary_wtd_avg`but uses the Minkowski-distance function,
|
|
203
|
+
`scipy.spatial.distance.minkowski` for all aggregators. This reimplementation
|
|
204
|
+
is useful for testing the output of `shrratio_boundary_wtd_avg`
|
|
205
|
+
but runs considerably slower.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
_delta_star
|
|
210
|
+
corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
|
|
211
|
+
_r_val
|
|
212
|
+
recapture ratio
|
|
213
|
+
agg_method
|
|
214
|
+
Whether "arithmetic", "geometric", or "distance".
|
|
215
|
+
weighting
|
|
216
|
+
Whether "own-share" or "cross-product-share".
|
|
217
|
+
recapture_spec
|
|
218
|
+
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
219
|
+
value for both merging firms ("proportional").
|
|
220
|
+
prec
|
|
221
|
+
Number of decimal places for rounding returned shares and area.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
Array of share-pairs, area under boundary.
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
_delta_star = mpf(f"{_delta_star}")
|
|
230
|
+
_s_mid = _delta_star / (1 + _delta_star)
|
|
231
|
+
|
|
232
|
+
# initial conditions
|
|
233
|
+
_gbdry_points = [(_s_mid, _s_mid)]
|
|
234
|
+
_s_1_pre, _s_2_pre = _s_mid, _s_mid
|
|
235
|
+
_s_2_oddval, _s_2_oddsum, _s_2_evnsum = True, 0, 0
|
|
236
|
+
|
|
237
|
+
# parameters for iteration
|
|
238
|
+
_weights_base = (mpf("0.5"),) * 2
|
|
239
|
+
_gbd_step_sz = mp.power(10, -prec)
|
|
240
|
+
_theta = _gbd_step_sz * (10 if weighting == "cross-product-share" else 1)
|
|
241
|
+
for _s_1 in mp.arange(_s_mid - _gbd_step_sz, 0, -_gbd_step_sz):
|
|
242
|
+
# The wtd. avg. GUPPI is not always convex to the origin, so we
|
|
243
|
+
# increment _s_2 after each iteration in which our algorithm
|
|
244
|
+
# finds (s1, s2) on the boundary
|
|
245
|
+
_s_2 = _s_2_pre * (1 + _theta)
|
|
246
|
+
|
|
247
|
+
if (_s_1 + _s_2) > mpf("0.99875"):
|
|
248
|
+
# 1: # We lose accuracy at 3-9s and up
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
while True:
|
|
252
|
+
_de_1 = _s_2 / (1 - _s_1)
|
|
253
|
+
_de_2 = (
|
|
254
|
+
_s_1 / (1 - lerp(_s_1, _s_2, _r_val))
|
|
255
|
+
if recapture_spec == "inside-out"
|
|
256
|
+
else _s_1 / (1 - _s_2)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
_weights_i = (
|
|
260
|
+
(
|
|
261
|
+
_w1 := mp.fdiv(
|
|
262
|
+
_s_2 if weighting == "cross-product-share" else _s_1,
|
|
263
|
+
_s_1 + _s_2,
|
|
264
|
+
),
|
|
265
|
+
1 - _w1,
|
|
266
|
+
)
|
|
267
|
+
if weighting
|
|
268
|
+
else _weights_base
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
match agg_method:
|
|
272
|
+
case "arithmetic":
|
|
273
|
+
_delta_test = distance_function(
|
|
274
|
+
(_de_1, _de_2), (0.0, 0.0), p=1, w=_weights_i
|
|
275
|
+
)
|
|
276
|
+
case "distance":
|
|
277
|
+
_delta_test = distance_function(
|
|
278
|
+
(_de_1, _de_2), (0.0, 0.0), p=2, w=_weights_i
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
_test_flag, _incr_decr = (
|
|
282
|
+
(_delta_test > _delta_star, -1)
|
|
283
|
+
if weighting == "cross-product-share"
|
|
284
|
+
else (_delta_test < _delta_star, 1)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if _test_flag:
|
|
288
|
+
_s_2 += _incr_decr * _gbd_step_sz
|
|
289
|
+
else:
|
|
290
|
+
break
|
|
291
|
+
|
|
292
|
+
# Build-up boundary points
|
|
293
|
+
_gbdry_points.append((_s_1, _s_2))
|
|
294
|
+
|
|
295
|
+
# Build up area terms
|
|
296
|
+
_s_2_oddsum += _s_2 if _s_2_oddval else 0
|
|
297
|
+
_s_2_evnsum += _s_2 if not _s_2_oddval else 0
|
|
298
|
+
_s_2_oddval = not _s_2_oddval
|
|
299
|
+
|
|
300
|
+
# Hold share points
|
|
301
|
+
_s_2_pre = _s_2
|
|
302
|
+
_s_1_pre = _s_1
|
|
303
|
+
|
|
304
|
+
if _s_2_oddval:
|
|
305
|
+
_s_2_evnsum -= _s_2_pre
|
|
306
|
+
else:
|
|
307
|
+
_s_2_oddsum -= _s_1_pre
|
|
308
|
+
|
|
309
|
+
_s_intcpt = _shrratio_boundary_intcpt(
|
|
310
|
+
_s_1_pre,
|
|
311
|
+
_delta_star,
|
|
312
|
+
_r_val,
|
|
313
|
+
recapture_spec=recapture_spec,
|
|
314
|
+
agg_method=agg_method,
|
|
315
|
+
weighting=weighting,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
if weighting == "own-share":
|
|
319
|
+
_gbd_prtlarea = (
|
|
320
|
+
_gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
|
|
321
|
+
)
|
|
322
|
+
# Area under boundary
|
|
323
|
+
_gbdry_area_total = 2 * (_s_1_pre + _gbd_prtlarea) - (
|
|
324
|
+
mp.power(_s_mid, "2") + mp.power(_s_1_pre, "2")
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
else:
|
|
328
|
+
_gbd_prtlarea = (
|
|
329
|
+
_gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_intcpt) / 3
|
|
330
|
+
)
|
|
331
|
+
# Area under boundary
|
|
332
|
+
_gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, "2")
|
|
333
|
+
|
|
334
|
+
_gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
|
|
335
|
+
np.float64
|
|
336
|
+
)
|
|
337
|
+
# Points defining boundary to point-of-symmetry
|
|
338
|
+
return GuidelinesBoundary(
|
|
339
|
+
np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
|
|
340
|
+
round(float(_gbdry_area_total), prec),
|
|
341
|
+
)
|
|
@@ -145,7 +145,7 @@ class MultithreadedRNG:
|
|
|
145
145
|
dist_type: Literal[
|
|
146
146
|
"Beta", "Dirichlet", "Gaussian", "Normal", "Random", "Uniform"
|
|
147
147
|
] = "Uniform",
|
|
148
|
-
dist_parms: NDArray[np.floating[TF]] | None = DIST_PARMS_DEFAULT,
|
|
148
|
+
dist_parms: NDArray[np.floating[TF]] | None = DIST_PARMS_DEFAULT, # type: ignore
|
|
149
149
|
seed_sequence: SeedSequence | None = None,
|
|
150
150
|
nthreads: int = NTHREADS,
|
|
151
151
|
):
|
|
@@ -27,19 +27,21 @@ from pathlib import Path
|
|
|
27
27
|
from typing import Any, Literal
|
|
28
28
|
|
|
29
29
|
import matplotlib.axes as mpa
|
|
30
|
+
from jinja2 import FileSystemLoader
|
|
31
|
+
from joblib import Parallel, cpu_count, delayed # type: ignore
|
|
32
|
+
from numpy import pi
|
|
33
|
+
from xlsxwriter import Workbook # type: ignore
|
|
34
|
+
|
|
30
35
|
import mergeron.core.excel_helper as xlh
|
|
31
36
|
import mergeron.core.guidelines_boundaries as gbl
|
|
32
37
|
import mergeron.ext.tol_colors as ptcolor
|
|
33
38
|
import mergeron.gen.investigations_stats as isl
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from mergeron import DATA_DIR
|
|
37
|
-
from numpy import pi
|
|
38
|
-
from xlsxwriter import Workbook
|
|
39
|
+
from mergeron import DATA_DIR, RECConstants, UPPAggrSelector
|
|
40
|
+
from mergeron.core import UPPBoundarySpec
|
|
39
41
|
|
|
40
42
|
PROG_PATH = Path(__file__)
|
|
41
43
|
|
|
42
|
-
RECAPTURE_SPEC =
|
|
44
|
+
RECAPTURE_SPEC = RECConstants.INOUT
|
|
43
45
|
# Map boundary forms to titles and generating-function names, with
|
|
44
46
|
# additional parameters as relevant
|
|
45
47
|
BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
|
|
@@ -53,26 +55,22 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
|
|
|
53
55
|
"title_str": "Aggregated-diversion-ratio boundary, own-share wtd. avg.",
|
|
54
56
|
"sheet_name": "OSWAG, wtd avg",
|
|
55
57
|
"func_str": R"(s_1 d_{12} + s_2 d_{21}) / s_M",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
+
"agg_method": UPPAggrSelector.OSA,
|
|
59
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
58
60
|
},
|
|
59
61
|
"OSWAG Own-shr-wtd Div Ratio Distance": {
|
|
60
62
|
"title_str": "Aggregated-diversion-ratio boundary, own-shr. wtd. distance",
|
|
61
63
|
"sheet_name": "OSWAG, distance",
|
|
62
64
|
"func_str": R"\surd (s_1 d_{12}^2 / s_M + s_2 d_{21}^2 / s_M)",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"wgtng_policy": "own-share",
|
|
66
|
-
"recapture_spec": RECAPTURE_SPEC,
|
|
67
|
-
"avg_method": "distance",
|
|
68
|
-
},
|
|
65
|
+
"agg_method": UPPAggrSelector.OSD,
|
|
66
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
69
67
|
},
|
|
70
68
|
"OSWAG Min Div Ratio": {
|
|
71
69
|
"title_str": "Aggregated-diversion-ratio boundary, minimum",
|
|
72
70
|
"sheet_name": "OSWAG, minimum",
|
|
73
71
|
"func_str": R"\min (d_{12}, d_{21})",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
72
|
+
"agg_method": UPPAggrSelector.MIN,
|
|
73
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
76
74
|
},
|
|
77
75
|
"SAG Combined Share": {
|
|
78
76
|
"title_str": "Combined Share boundary",
|
|
@@ -84,19 +82,15 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
|
|
|
84
82
|
"title_str": "Aggregated-diversion-ratio boundary, simple average",
|
|
85
83
|
"sheet_name": "SAG, average",
|
|
86
84
|
"func_str": R"(d_{12} + d_{21}) / 2",
|
|
87
|
-
"
|
|
88
|
-
"
|
|
85
|
+
"agg_method": UPPAggrSelector.AVG,
|
|
86
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
89
87
|
},
|
|
90
88
|
"SAG Div Ratio Distance": {
|
|
91
89
|
"title_str": "Aggregated-diversion-ratio boundary, distance",
|
|
92
90
|
"sheet_name": "SAG, distance",
|
|
93
91
|
"func_str": R"\surd (d_{12}^2 / 2 + d_{21}^2 / 2)",
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"wgtng_policy": None,
|
|
97
|
-
"recapture_spec": RECAPTURE_SPEC,
|
|
98
|
-
"avg_method": "distance",
|
|
99
|
-
},
|
|
92
|
+
"agg_method": UPPAggrSelector.DIS,
|
|
93
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
100
94
|
},
|
|
101
95
|
"CPSWAG Premerger HHI-contribution": {
|
|
102
96
|
"title_str": "Premerger HHI-contribution boundary",
|
|
@@ -108,28 +102,22 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
|
|
|
108
102
|
"title_str": "Aggregated-diversion-ratio boundary, cross-product-share wtd. avg.",
|
|
109
103
|
"sheet_name": "CPSWAG, wtd avg",
|
|
110
104
|
"func_str": R"(s_2 d_{12} / s_M + s_1 d_{21} / s_M)",
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"wgtng_policy": "cross-product-share",
|
|
114
|
-
"recapture_spec": RECAPTURE_SPEC,
|
|
115
|
-
},
|
|
105
|
+
"agg_method": UPPAggrSelector.CPA,
|
|
106
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
116
107
|
},
|
|
117
108
|
"CPSWAG Cross-product-shr-wtd Div Ratio Distance": {
|
|
118
109
|
"title_str": "Aggregated-diversion-ratio boundary, cross-prod-shr. wtd. distance",
|
|
119
110
|
"sheet_name": "CPSWAG, distance",
|
|
120
111
|
"func_str": R"\surd (s_2 d_{12}^2 / s_M + s_1 d_{21}^2 / s_M)",
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"wgtng_policy": "cross-product-share",
|
|
124
|
-
"recapture_spec": RECAPTURE_SPEC,
|
|
125
|
-
"avg_method": "distance",
|
|
126
|
-
},
|
|
112
|
+
"agg_method": UPPAggrSelector.CPD,
|
|
113
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
127
114
|
},
|
|
128
115
|
"CPSWAG Max Div Ratio": {
|
|
129
116
|
"title_str": "Aggregated-diversion-ratio boundary, maximum",
|
|
130
117
|
"sheet_name": "CPSWAG, maximum",
|
|
131
118
|
"func_str": R"\max (d_{12}, d_{21})",
|
|
132
|
-
"
|
|
119
|
+
"agg_method": UPPAggrSelector.MAX,
|
|
120
|
+
"recapture_spec": RECAPTURE_SPEC,
|
|
133
121
|
},
|
|
134
122
|
}
|
|
135
123
|
|
|
@@ -254,9 +242,13 @@ def _bdry_stats_col(
|
|
|
254
242
|
case "CPSWAG Premerger HHI-contribution":
|
|
255
243
|
return _bdry_spec, f"{_hhi_m_pre_prob:6.5f}"
|
|
256
244
|
case _ if "Div Ratio" in _bdry_spec:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
245
|
+
_within_bdry_area = gbl.shrratio_boundary(
|
|
246
|
+
UPPBoundarySpec(
|
|
247
|
+
_delta_val,
|
|
248
|
+
_r_val,
|
|
249
|
+
agg_method=BDRY_SPECS_DICT[_bdry_spec]["agg_method"],
|
|
250
|
+
recapture_spec=BDRY_SPECS_DICT[_bdry_spec]["recapture_spec"],
|
|
251
|
+
)
|
|
260
252
|
).area
|
|
261
253
|
_within_bdry_prob = 2 * _within_bdry_area
|
|
262
254
|
if _bdry_spec.startswith("CPSWAG"):
|
|
@@ -266,7 +258,7 @@ def _bdry_stats_col(
|
|
|
266
258
|
else:
|
|
267
259
|
_within_conc_bdry_prob = _dhhi_prob
|
|
268
260
|
|
|
269
|
-
return _bdry_spec, R"{{ {:6.5f} \\ {:.2f}\% }}".format(
|
|
261
|
+
return _bdry_spec, R"{{ {:6.5f} \\ {:.2f}\% }}".format( # noqa: UP032
|
|
270
262
|
_within_bdry_prob,
|
|
271
263
|
100 * (1 - (_within_conc_bdry_prob / _within_bdry_prob)),
|
|
272
264
|
)
|
|
@@ -408,10 +400,11 @@ def gen_plot_boundary(
|
|
|
408
400
|
print(_bdry_spec_dict["title_str"])
|
|
409
401
|
|
|
410
402
|
_pt_mdco: ptcolor.Mcset = ptcolor.tol_cset("medium-contrast") # type: ignore
|
|
403
|
+
_pt_vbco: ptcolor.Vcset = ptcolor.tol_cset("vibrant") # type: ignore
|
|
411
404
|
|
|
412
405
|
_plot_line_width = 1.0
|
|
413
406
|
_plot_line_alpha = 0.8
|
|
414
|
-
_plot_line_color =
|
|
407
|
+
_plot_line_color = _pt_vbco.black
|
|
415
408
|
_plot_line_style = {"OSWAG": "-", "SAG": "-.", "CPSWAG": "--"}.get(
|
|
416
409
|
_bdry_spec_str.split(" ")[0], "-"
|
|
417
410
|
)
|
|
@@ -425,15 +418,14 @@ def gen_plot_boundary(
|
|
|
425
418
|
case _ if _bdry_spec_str.startswith(("SAG Combined", "CPSWAG Premerger")):
|
|
426
419
|
_zrdr = 2
|
|
427
420
|
case _ if "Distance" in _bdry_spec_str:
|
|
428
|
-
_plot_line_color =
|
|
421
|
+
_plot_line_color = _pt_vbco.blue
|
|
429
422
|
_zrdr = 3
|
|
430
423
|
case _ if "shr-wtd" in _bdry_spec_str or "Mean" in _bdry_spec_str:
|
|
431
|
-
_plot_line_color =
|
|
424
|
+
_plot_line_color = _pt_vbco.teal
|
|
432
425
|
_zrdr = 3
|
|
433
426
|
case _:
|
|
434
|
-
_plot_line_color =
|
|
427
|
+
_plot_line_color = _pt_vbco.red
|
|
435
428
|
|
|
436
|
-
_dh_bar = _gso.safeharbor.divr
|
|
437
429
|
_g_val = _gso.safeharbor.guppi
|
|
438
430
|
|
|
439
431
|
_r_bar = _gso.presumption.rec
|
|
@@ -442,12 +434,15 @@ def gen_plot_boundary(
|
|
|
442
434
|
_s_mid = sqrt(_dhhi_val / 2)
|
|
443
435
|
_delta_val = _g_val / _r_bar if _gs_str == "safeharbor" else _s_mid / (1 - _s_mid)
|
|
444
436
|
|
|
445
|
-
_bdry_func = _bdry_spec_dict
|
|
437
|
+
_bdry_func = _bdry_spec_dict.get("func", gbl.shrratio_boundary)
|
|
446
438
|
if "Div Ratio" in _bdry_spec_str:
|
|
447
|
-
_bdry_boundary =
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
439
|
+
_bdry_boundary = gbl.shrratio_boundary(
|
|
440
|
+
UPPBoundarySpec(
|
|
441
|
+
_delta_val,
|
|
442
|
+
_r_bar,
|
|
443
|
+
agg_method=_bdry_spec_dict["agg_method"],
|
|
444
|
+
recapture_spec=_bdry_spec_dict["recapture_spec"],
|
|
445
|
+
)
|
|
451
446
|
)
|
|
452
447
|
_plot_label_mag, _plot_label_uom = _r_bar * _delta_val * 1e2, "%"
|
|
453
448
|
elif _bdry_spec_str.endswith("Combined Share"):
|
|
@@ -15,7 +15,8 @@ from matplotlib.ticker import StrMethodFormatter
|
|
|
15
15
|
from numpy import arange, arctan, array, hsplit, insert, rad2deg, round, sqrt, vstack
|
|
16
16
|
|
|
17
17
|
import mergeron.core.guidelines_boundaries as gbl
|
|
18
|
-
from mergeron import DATA_DIR
|
|
18
|
+
from mergeron import DATA_DIR, UPPAggrSelector
|
|
19
|
+
from mergeron.core import UPPBoundarySpec
|
|
19
20
|
|
|
20
21
|
PROG_PATH = Path(__file__)
|
|
21
22
|
|
|
@@ -34,7 +35,7 @@ def plot_delta_boundaries(
|
|
|
34
35
|
_recapture_spec: Literal["inside-out", "proportional"],
|
|
35
36
|
_color_kwargs: _CMAPArgs = _color_kwargs,
|
|
36
37
|
/,
|
|
37
|
-
):
|
|
38
|
+
) -> None:
|
|
38
39
|
_print_guppi_max_bndry_envs_flag = _print_guppi_max_bndry_envs_flag or False
|
|
39
40
|
|
|
40
41
|
print("ΔHHI safeharbor boundary")
|
|
@@ -81,7 +82,7 @@ def plot_delta_boundaries(
|
|
|
81
82
|
_smin_nr = _dstar * (1 - _r_bar)
|
|
82
83
|
_smax_nr = 1 - _dstar * _r_bar
|
|
83
84
|
_guppi_bdry_env_dr = _smin_nr + _smax_nr
|
|
84
|
-
_guppi_bdry_env_xs = (
|
|
85
|
+
_guppi_bdry_env_xs = ( # type: ignore
|
|
85
86
|
0,
|
|
86
87
|
_smin_nr / _guppi_bdry_env_dr,
|
|
87
88
|
_symshr,
|
|
@@ -138,7 +139,7 @@ def plot_guppi_boundaries( # noqa PLR0915
|
|
|
138
139
|
_recapture_spec: Literal["proportional", "inside-out"],
|
|
139
140
|
_color_kwargs: _CMAPArgs = _color_kwargs,
|
|
140
141
|
/,
|
|
141
|
-
):
|
|
142
|
+
) -> None:
|
|
142
143
|
if recapture_spec not in (_recspecs := ("inside-out", "proportional")):
|
|
143
144
|
raise ValueError(f"Recapture specification must be one of, {_recspecs!r}")
|
|
144
145
|
|
|
@@ -318,7 +319,9 @@ def plot_guppi_boundaries( # noqa PLR0915
|
|
|
318
319
|
_delta_star = gbl.critical_shrratio(
|
|
319
320
|
_guppi_bench, m_star=_m_star_bench, r_bar=_r_bar
|
|
320
321
|
)
|
|
321
|
-
guppi_boundary = gbl.
|
|
322
|
+
guppi_boundary = gbl.shrratio_boundary(
|
|
323
|
+
UPPBoundarySpec(_delta_star, _r_bar, agg_method=UPPAggrSelector.MAX)
|
|
324
|
+
)
|
|
322
325
|
_x_drt, _y_drt = zip(*guppi_boundary.coordinates, strict=True)
|
|
323
326
|
|
|
324
327
|
_ax1.plot(
|
|
@@ -429,7 +432,9 @@ def plot_guppi_boundaries( # noqa PLR0915
|
|
|
429
432
|
del _dh_dat_x, _dh_dat_y
|
|
430
433
|
|
|
431
434
|
|
|
432
|
-
def grad_est(
|
|
435
|
+
def grad_est(
|
|
436
|
+
_ax: matplotlib.axis.Axis, _pt_xs: tuple[float, ...], _pt_ys: tuple[float, ...]
|
|
437
|
+
) -> float:
|
|
433
438
|
if (_pt_len := max(len(_pt_xs), len(_pt_ys))) > 2:
|
|
434
439
|
raise ValueError(
|
|
435
440
|
"Expecting only 2 points for calculation of line-gradient; got {_pt_len}."
|
|
@@ -438,7 +443,8 @@ def grad_est(_ax: matplotlib.axis.Axis, _pt_xs: tuple, _pt_ys: tuple) -> float:
|
|
|
438
443
|
_ax.transData.transform_point((_pt_xs[_i], _pt_ys[_i])) # type: ignore
|
|
439
444
|
for _i in range(2)
|
|
440
445
|
)
|
|
441
|
-
|
|
446
|
+
_grad: float = (_pt2[1] - _pt1[1]) / (_pt2[0] - _pt1[0])
|
|
447
|
+
return _grad
|
|
442
448
|
|
|
443
449
|
|
|
444
450
|
def get_hmg_thresholds_by_key(_guppi_bench_key: str, /) -> gbl.HMGThresholds:
|