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.

@@ -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 jinja2 import FileSystemLoader
35
- from joblib import Parallel, cpu_count, delayed
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 = "inside-out"
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
- "func": gbl.shrratio_boundary_wtd_avg,
57
- "func_kwargs": {"wgtng_policy": "own-share", "recapture_spec": RECAPTURE_SPEC},
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
- "func": gbl.shrratio_boundary_wtd_avg,
64
- "func_kwargs": {
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
- "func": gbl.shrratio_boundary_min,
75
- "func_kwargs": {"recapture_spec": RECAPTURE_SPEC},
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
- "func": gbl.shrratio_boundary_wtd_avg,
88
- "func_kwargs": {"wgtng_policy": None, "recapture_spec": RECAPTURE_SPEC},
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
- "func": gbl.shrratio_boundary_wtd_avg,
95
- "func_kwargs": {
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
- "func": gbl.shrratio_boundary_wtd_avg,
112
- "func_kwargs": {
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
- "func": gbl.shrratio_boundary_wtd_avg,
122
- "func_kwargs": {
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
- "func": gbl.shrratio_boundary_max,
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
- _gbd_func = BDRY_SPECS_DICT[_bdry_spec]["func"]
258
- _within_bdry_area = _gbd_func(
259
- _delta_val, _r_val, **BDRY_SPECS_DICT[_bdry_spec].get("func_kwargs", {})
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 = _pt_mdco.black
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 = _pt_mdco.light_blue
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 = _pt_mdco.light_yellow
424
+ _plot_line_color = _pt_vbco.teal
432
425
  _zrdr = 3
433
426
  case _:
434
- _plot_line_color = _pt_mdco.dark_red
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["func"]
437
+ _bdry_func = _bdry_spec_dict.get("func", gbl.shrratio_boundary)
446
438
  if "Div Ratio" in _bdry_spec_str:
447
- _bdry_boundary = _bdry_func(
448
- _delta_val,
449
- _r_bar,
450
- **_bdry_spec_dict.get("func_kwargs", {}), # tupe: ignore
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.shrratio_boundary_max(_delta_star)
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(_ax: matplotlib.axis.Axis, _pt_xs: tuple, _pt_ys: tuple) -> float:
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
- return (_pt2[1] - _pt1[1]) / (_pt2[0] - _pt1[0])
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: