mergeron 2024.738949.1__py3-none-any.whl → 2024.738949.5__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.

@@ -5,6 +5,7 @@ with a canvas on which to draw boundaries for Guidelines standards.
5
5
  """
6
6
 
7
7
  import decimal
8
+ from collections.abc import Callable
8
9
  from dataclasses import dataclass
9
10
  from importlib.metadata import version
10
11
  from typing import Any, Literal, TypeAlias
@@ -14,6 +15,7 @@ from attrs import define, field
14
15
  from mpmath import mp, mpf # type: ignore
15
16
  from numpy.typing import NDArray
16
17
  from scipy.spatial.distance import minkowski as distance_function
18
+ from sympy import lambdify, simplify, solve, symbols
17
19
 
18
20
  from .. import _PKG_NAME # noqa: TID252
19
21
 
@@ -32,6 +34,13 @@ class GuidelinesBoundary:
32
34
  area: float
33
35
 
34
36
 
37
+ @dataclass(slots=True, frozen=True)
38
+ class GuidelinesBoundaryCallable:
39
+ boundary_function: Callable[[NDArray[np.float64]], NDArray[np.float64]]
40
+ area: float
41
+ s_naught: float = 0
42
+
43
+
35
44
  @define(slots=True, frozen=True)
36
45
  class HMGThresholds:
37
46
  delta: float
@@ -278,7 +287,12 @@ def gbd_from_dsf(
278
287
 
279
288
 
280
289
  def critical_shrratio(
281
- _gbd: float = 0.06, /, *, m_star: float = 1.00, r_bar: float = 0.80
290
+ _gbd: float = 0.06,
291
+ /,
292
+ *,
293
+ m_star: float = 1.00,
294
+ r_bar: float = 0.80,
295
+ frac: float = 1e-16,
282
296
  ) -> mpf:
283
297
  """
284
298
  Corollary to GUPPI bound.
@@ -298,7 +312,7 @@ def critical_shrratio(
298
312
  for given margin and recapture rate.
299
313
 
300
314
  """
301
- return mpf(f"{_gbd}") / mp.fmul(f"{m_star}", f"{r_bar}")
315
+ return round_cust(mpf(f"{_gbd}") / mp.fmul(f"{m_star}", f"{r_bar}"), frac=frac)
302
316
 
303
317
 
304
318
  def shr_from_gbd(
@@ -371,18 +385,18 @@ def boundary_plot(*, mktshares_plot_flag: bool = True) -> tuple[Any, ...]:
371
385
  r"\setsansfont{Fira Sans Light}",
372
386
  R"\setmonofont[Scale=MatchLowercase,]{Fira Mono}",
373
387
  R"\defaultfontfeatures[\rmfamily]{",
374
- R" Ligatures={TeX, Common},",
375
- R" Numbers={Proportional, Lining},",
376
- R" }",
388
+ R" Ligatures={TeX, Common},",
389
+ R" Numbers={Proportional, Lining},",
390
+ R" }",
377
391
  R"\defaultfontfeatures[\sffamily]{",
378
- R" Ligatures={TeX, Common},",
379
- R" Numbers={Monospaced, Lining},",
380
- R" LetterSpace=0.50,",
381
- R" }",
392
+ R" Ligatures={TeX, Common},",
393
+ R" Numbers={Monospaced, Lining},",
394
+ R" LetterSpace=0.50,",
395
+ R" }",
382
396
  R"\usepackage[",
383
- R" activate={true, nocompatibility},",
384
- R" tracking=true,",
385
- R" ]{microtype}",
397
+ R" activate={true, nocompatibility},",
398
+ R" tracking=true,",
399
+ R" ]{microtype}",
386
400
  ]),
387
401
  })
388
402
 
@@ -589,19 +603,16 @@ def delta_hhi_boundary(
589
603
  _s1_zero = 1 / 2 * (1 - mp.sqrt(1 - 2 * _dh_val))
590
604
  _s1_one = 1 - _s1_zero
591
605
 
592
- _s_mid = mp.sqrt(_dh_val / 2)
593
-
594
606
  _dh_step_sz = mp.power(10, -6)
595
- _s_1 = np.array(mp.arange(_s_mid, _s1_zero, -_dh_step_sz))
607
+ _s_1 = np.array(mp.arange(_s1_zero, _s1_one + _dh_step_sz, _dh_step_sz))
596
608
  _s_2 = _dh_val / (2 * _s_1)
597
609
 
598
610
  # Boundary points
599
- _dh_half = np.row_stack((
600
- np.column_stack((_s_1, _s_2)),
601
- np.array([(_s1_zero, _s1_one)]),
611
+ _dh_bdry_pts = np.row_stack((
602
612
  np.array([(mpf("0.0"), mpf("1.0"))]),
613
+ np.column_stack((_s_1, _s_2)),
614
+ np.array([(mpf("1.0"), mpf("0.0"))]),
603
615
  ))
604
- _dh_bdry_pts = np.row_stack((np.flip(_dh_half, 0), np.flip(_dh_half[1:], 1)))
605
616
 
606
617
  _s_1_pts, _s_2_pts = np.split(_dh_bdry_pts, 2, axis=1)
607
618
  return GuidelinesBoundary(
@@ -613,6 +624,42 @@ def delta_hhi_boundary(
613
624
  )
614
625
 
615
626
 
627
+ def delta_hhi_boundary_qdtr(_dh_val: float = 0.01) -> GuidelinesBoundaryCallable:
628
+ """
629
+ Generate the list of share combination on the ΔHHI boundary.
630
+
631
+ Parameters
632
+ ----------
633
+ _dh_val:
634
+ Merging-firms' ΔHHI bound.
635
+ dh_dps
636
+ Number of decimal places for rounding reported shares.
637
+
638
+ Returns
639
+ -------
640
+ Callable to generate array of share-pairs, area under boundary.
641
+
642
+ """
643
+
644
+ _dh_val = mpf(f"{_dh_val}")
645
+
646
+ _s_1, _s_2 = symbols("s_1, s_2", positive=True)
647
+
648
+ _hhi_eqn = _s_2 - 0.01 / (2 * _s_1)
649
+
650
+ _hhi_bdry = solve(_hhi_eqn, _s_2)[0]
651
+ _s_nought = float(solve(_hhi_eqn.subs({_s_2: 1 - _s_1}), _s_1)[0])
652
+
653
+ _hhi_bdry_area = 2 * (
654
+ _s_nought
655
+ + mp.quad(lambdify(_s_1, _hhi_bdry, "mpmath"), (_s_nought, 1 - _s_nought))
656
+ )
657
+
658
+ return GuidelinesBoundaryCallable(
659
+ lambdify(_s_1, _hhi_bdry, "numpy"), _hhi_bdry_area, _s_nought
660
+ )
661
+
662
+
616
663
  def combined_share_boundary(
617
664
  _s_intcpt: float = 0.0625, /, *, bdry_dps: int = 10
618
665
  ) -> GuidelinesBoundary:
@@ -801,6 +848,130 @@ def shrratio_boundary_min(
801
848
  )
802
849
 
803
850
 
851
+ def shrratio_boundary_qdtr_wtd_avg(
852
+ _delta_star: float = 0.075,
853
+ _r_val: float = 0.80,
854
+ /,
855
+ *,
856
+ wgtng_policy: Literal["own-share", "cross-product-share"] | None = "own-share",
857
+ recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
858
+ ) -> GuidelinesBoundaryCallable:
859
+ """
860
+ Share combinations for the share-weighted average GUPPI boundary with symmetric
861
+ merging-firm margins.
862
+
863
+ Parameters
864
+ ----------
865
+ _delta_star
866
+ corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
867
+ _r_val
868
+ recapture ratio
869
+ wgtng_policy
870
+ Whether "own-share" or "cross-product-share" (or None for simple, unweighted average)
871
+ recapture_spec
872
+ Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
873
+ value for both merging firms ("proportional").
874
+
875
+ Returns
876
+ -------
877
+ Array of share-pairs, area under boundary.
878
+
879
+ """
880
+
881
+ if _delta_star > 1:
882
+ raise ValueError(
883
+ "Margin-adjusted benchmark share ratio, `_delta_star` cannot exceed 1."
884
+ )
885
+
886
+ _delta_star = mpf(f"{_delta_star}")
887
+ _s_mid = _delta_star / (1 + _delta_star)
888
+ _s_naught = 0
889
+
890
+ _s_1, _s_2 = symbols("s_1:3", positive=True)
891
+
892
+ match wgtng_policy:
893
+ case "own-share":
894
+ _bdry_eqn = (
895
+ _s_1 * _s_2 / (1 - _s_1)
896
+ + _s_2
897
+ * _s_1
898
+ / (
899
+ (1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
900
+ if recapture_spec == "inside-out"
901
+ else (1 - _s_2)
902
+ )
903
+ - (_s_1 + _s_2) * _delta_star
904
+ )
905
+
906
+ _bdry_func = solve(_bdry_eqn, _s_2)[0]
907
+ _s_naught = (
908
+ float(solve(simplify(_bdry_eqn.subs({_s_2: 1 - _s_1})), _s_1)[0])
909
+ if recapture_spec == "inside-out"
910
+ else 0
911
+ )
912
+ _bdry_area = float(
913
+ 2
914
+ * (
915
+ _s_naught
916
+ + mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (_s_naught, _s_mid))
917
+ )
918
+ - (_s_mid**2 + _s_naught**2)
919
+ )
920
+
921
+ case "cross-product-share":
922
+ mp.trap_complex = False
923
+ _d_star = symbols("d", positive=True)
924
+ _bdry_eqn = (
925
+ _s_2 * _s_2 / (1 - _s_1)
926
+ + _s_1
927
+ * _s_1
928
+ / (
929
+ (1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
930
+ if recapture_spec == "inside-out"
931
+ else (1 - _s_2)
932
+ )
933
+ - (_s_1 + _s_2) * _d_star
934
+ )
935
+
936
+ _bdry_func = solve(_bdry_eqn, _s_2)[1]
937
+ _bdry_area = float(
938
+ 2
939
+ * (
940
+ mp.quad(
941
+ lambdify(
942
+ _s_1, _bdry_func.subs({_d_star: _delta_star}), "mpmath"
943
+ ),
944
+ (0, _s_mid),
945
+ )
946
+ ).real
947
+ - _s_mid**2
948
+ )
949
+
950
+ case _:
951
+ _bdry_eqn = (
952
+ 1 / 2 * _s_2 / (1 - _s_1)
953
+ + 1
954
+ / 2
955
+ * _s_1
956
+ / (
957
+ (1 - (_r_val * _s_2 + (1 - _r_val) * _s_1))
958
+ if recapture_spec == "inside-out"
959
+ else (1 - _s_2)
960
+ )
961
+ - _delta_star
962
+ )
963
+
964
+ _bdry_func = solve(_bdry_eqn, _s_2)[0]
965
+ _bdry_area = float(
966
+ 2 * (mp.quad(lambdify(_s_1, _bdry_func, "mpmath"), (0, _s_mid)))
967
+ - _s_mid**2
968
+ )
969
+
970
+ return GuidelinesBoundaryCallable(
971
+ lambdify(_s_1, _bdry_func, "numpy"), _bdry_area, _s_naught
972
+ )
973
+
974
+
804
975
  def shrratio_boundary_wtd_avg(
805
976
  _delta_star: float = 0.075,
806
977
  _r_val: float = 0.80,
@@ -815,16 +986,36 @@ def shrratio_boundary_wtd_avg(
815
986
  Share combinations for the share-weighted average GUPPI boundary with symmetric
816
987
  merging-firm margins.
817
988
 
989
+ Parameters
990
+ ----------
991
+ _delta_star
992
+ corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
993
+ _r_val
994
+ recapture ratio
995
+ avg_method
996
+ Whether "arithmetic", "geometric", or "distance".
997
+ wgtng_policy
998
+ Whether "own-share" or "cross-product-share" (or None for simple, unweighted average).
999
+ recapture_spec
1000
+ Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
1001
+ value for both merging firms ("proportional").
1002
+ gbd_dps
1003
+ Number of decimal places for rounding returned shares and area.
1004
+
1005
+ Returns
1006
+ -------
1007
+ Array of share-pairs, area under boundary.
1008
+
818
1009
  Notes
819
1010
  -----
820
1011
  An analytical expression for the share-weighted arithmetic mean boundary
821
1012
  is derived and plotted from y-intercept to the ray of symmetry as follows::
822
1013
 
823
1014
  from sympy import plot as symplot, solve, symbols
824
- s_1, s_2, delta_star = symbols("s_1 s_2 \\delta^*")
1015
+ s_1, s_2 = symbols("s_1 s_2", positive=True)
825
1016
 
826
1017
  g_val, r_val, m_val = 0.06, 0.80, 0.30
827
- d_hat = g_val / (r_val * m_val)
1018
+ delta_star = g_val / (r_val * m_val)
828
1019
 
829
1020
  # recapture_spec == "inside-out"
830
1021
  oswag = solve(
@@ -834,7 +1025,7 @@ def shrratio_boundary_wtd_avg(
834
1025
  s_2
835
1026
  )[0]
836
1027
  symplot(
837
- oswag.subs({delta_star: d_hat}),
1028
+ oswag,
838
1029
  (s_1, 0., d_hat / (1 + d_hat)),
839
1030
  ylabel=s_2
840
1031
  )
@@ -846,7 +1037,7 @@ def shrratio_boundary_wtd_avg(
846
1037
  s_2
847
1038
  )[1]
848
1039
  symplot(
849
- cpwag.subs({delta_star: d_hat}),
1040
+ cpwag,
850
1041
  (s_1, 0., d_hat / (1 + d_hat)),
851
1042
  ylabel=s_2
852
1043
  )
@@ -859,7 +1050,7 @@ def shrratio_boundary_wtd_avg(
859
1050
  s_2
860
1051
  )[0]
861
1052
  symplot(
862
- oswag.subs({delta_star: d_hat}),
1053
+ oswag,
863
1054
  (s_1, 0., d_hat / (1 + d_hat)),
864
1055
  ylabel=s_2
865
1056
  )
@@ -871,30 +1062,11 @@ def shrratio_boundary_wtd_avg(
871
1062
  s_2
872
1063
  )[1]
873
1064
  symplot(
874
- cpswag.subs({delta_star: d_hat}),
1065
+ cpswag,
875
1066
  (s_1, 0.0, d_hat / (1 + d_hat)),
876
1067
  ylabel=s_2
877
1068
  )
878
1069
 
879
- Parameters
880
- ----------
881
- _delta_star
882
- corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
883
- _r_val
884
- recapture ratio
885
- avg_method
886
- Whether "arithmetic", "geometric", or "distance".
887
- wgtng_policy
888
- Whether "own-share" or "cross-product-share".
889
- recapture_spec
890
- Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
891
- value for both merging firms ("proportional").
892
- gbd_dps
893
- Number of decimal places for rounding returned shares and area.
894
-
895
- Returns
896
- -------
897
- Array of share-pairs, area under boundary.
898
1070
 
899
1071
  """
900
1072
 
@@ -921,7 +1093,7 @@ def shrratio_boundary_wtd_avg(
921
1093
  _s_2 = _s_2_pre * (1 + _theta)
922
1094
 
923
1095
  if (_s_1 + _s_2) > mpf("0.99875"):
924
- # 1: # We lose accuracy at 3-9s and up
1096
+ # Loss of accuracy at 3-9s and up
925
1097
  break
926
1098
 
927
1099
  while True:
@@ -948,10 +1120,11 @@ def shrratio_boundary_wtd_avg(
948
1120
  case _:
949
1121
  _delta_test = lerp(_de_1, _de_2, _r)
950
1122
 
951
- if wgtng_policy == "cross-product-share":
952
- _test_flag, _incr_decr = (_delta_test > _delta_star, -1)
953
- else:
954
- _test_flag, _incr_decr = (_delta_test < _delta_star, 1)
1123
+ _test_flag, _incr_decr = (
1124
+ (_delta_test > _delta_star, -1)
1125
+ if wgtng_policy == "cross-product-share"
1126
+ else (_delta_test < _delta_star, 1)
1127
+ )
955
1128
 
956
1129
  if _test_flag:
957
1130
  _s_2 += _incr_decr * _gbd_step_sz
@@ -970,31 +1143,41 @@ def shrratio_boundary_wtd_avg(
970
1143
  _s_2_pre = _s_2
971
1144
  _s_1_pre = _s_1
972
1145
 
973
- _gbd_prtlarea = _gbd_step_sz * (
974
- (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _delta_star) / 3
975
- if wgtng_policy == "cross-product-share"
976
- else (
977
- (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
978
- + _s_1_pre * (1 + _s_2_pre) / 2
979
- )
1146
+ if _s_2_oddval:
1147
+ _s_2_evnsum -= _s_2_pre
1148
+ else:
1149
+ _s_2_oddsum -= _s_1_pre
1150
+
1151
+ _s_intcpt = _shrratio_boundary_intcpt(
1152
+ _s_1_pre,
1153
+ _delta_star,
1154
+ _r_val,
1155
+ recapture_spec=recapture_spec,
1156
+ avg_method=avg_method,
1157
+ wgtng_policy=wgtng_policy,
980
1158
  )
981
1159
 
982
- # Area under boundary
983
- _gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, 2)
1160
+ if wgtng_policy == "own-share":
1161
+ _gbd_prtlarea = (
1162
+ _gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
1163
+ )
1164
+ # Area under boundary
1165
+ _gbdry_area_total = float(
1166
+ 2 * (_s_1_pre + _gbd_prtlarea)
1167
+ - (mp.power(_s_mid, "2") + mp.power(_s_1_pre, "2"))
1168
+ )
984
1169
 
985
- match wgtng_policy:
986
- case "cross-product-share":
987
- _s_intcpt = _delta_star
988
- case "own-product-share":
989
- _s_intcpt = mpf("1.0")
990
- case None if avg_method == "distance":
991
- _s_intcpt = _delta_star * mp.sqrt("2")
992
- case _:
993
- _s_intcpt = _s_2_pre
1170
+ else:
1171
+ _gbd_prtlarea = (
1172
+ _gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_intcpt) / 3
1173
+ )
1174
+ # Area under boundary
1175
+ _gbdry_area_total = float(2 * _gbd_prtlarea - mp.power(_s_mid, "2"))
994
1176
 
995
1177
  _gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
996
1178
  np.float64
997
1179
  )
1180
+
998
1181
  # Points defining boundary to point-of-symmetry
999
1182
  return GuidelinesBoundary(
1000
1183
  np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
@@ -1281,10 +1464,10 @@ def shrratio_boundary_avg(
1281
1464
  _s_intcpt = _s_2
1282
1465
 
1283
1466
  _gbd_prtlarea = 2 * _gbd_step_sz * (
1284
- mp.fmul(4 / 3, _s_2_oddsum)
1285
- + mp.fmul(2 / 3, _s_2_evnsum)
1286
- + mp.fmul(1 / 3, _s_mid + _s_intcpt)
1287
- ) - mp.power(_s_mid, 2)
1467
+ mp.fmul(4, _s_2_oddsum)
1468
+ + mp.fmul(2, _s_2_evnsum)
1469
+ + mp.fmul(1, _s_mid + _s_intcpt)
1470
+ ) / 3 - mp.power(_s_mid, 2)
1288
1471
 
1289
1472
  _gbdry_points = np.array(_gbdry_points, np.float64)
1290
1473
  return GuidelinesBoundary(
@@ -1310,8 +1493,8 @@ def shrratio_boundary_distance(
1310
1493
  Reimplements the arithmetic-averages and distance estimations from function,
1311
1494
  `shrratio_boundary_wtd_avg`but uses the Minkowski-distance function,
1312
1495
  `scipy.spatial.distance.minkowski` for all aggregators. This reimplementation
1313
- is primarifly useful for testing the output of `shrratio_boundary_wtd_avg`
1314
- as it tests considerably slower.
1496
+ is useful for testing the output of `shrratio_boundary_wtd_avg`
1497
+ but runs considerably slower.
1315
1498
 
1316
1499
  Parameters
1317
1500
  ----------
@@ -1392,10 +1575,11 @@ def shrratio_boundary_distance(
1392
1575
  (_de_1, _de_2), (0.0, 0.0), p=2, w=_weights_i
1393
1576
  )
1394
1577
 
1395
- if wgtng_policy == "cross-product-share":
1396
- _test_flag, _incr_decr = (_delta_test > _delta_star, -1)
1397
- else:
1398
- _test_flag, _incr_decr = (_delta_test < _delta_star, 1)
1578
+ _test_flag, _incr_decr = (
1579
+ (_delta_test > _delta_star, -1)
1580
+ if wgtng_policy == "cross-product-share"
1581
+ else (_delta_test < _delta_star, 1)
1582
+ )
1399
1583
 
1400
1584
  if _test_flag:
1401
1585
  _s_2 += _incr_decr * _gbd_step_sz
@@ -1414,22 +1598,60 @@ def shrratio_boundary_distance(
1414
1598
  _s_2_pre = _s_2
1415
1599
  _s_1_pre = _s_1
1416
1600
 
1417
- _gbd_prtlarea = _gbd_step_sz * (
1418
- (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _delta_star) / 3
1419
- if wgtng_policy == "cross-product-share"
1420
- else (
1421
- (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
1422
- + _s_1_pre * (1 + _s_2_pre) / 2
1601
+ if _s_2_oddval:
1602
+ _s_2_evnsum -= _s_2_pre
1603
+ else:
1604
+ _s_2_oddsum -= _s_1_pre
1605
+
1606
+ _s_intcpt = _shrratio_boundary_intcpt(
1607
+ _s_1_pre,
1608
+ _delta_star,
1609
+ _r_val,
1610
+ recapture_spec=recapture_spec,
1611
+ avg_method=avg_method,
1612
+ wgtng_policy=wgtng_policy,
1613
+ )
1614
+
1615
+ if wgtng_policy == "own-share":
1616
+ _gbd_prtlarea = (
1617
+ _gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
1423
1618
  )
1619
+ # Area under boundary
1620
+ _gbdry_area_total = 2 * (_s_1_pre + _gbd_prtlarea) - (
1621
+ mp.power(_s_mid, "2") + mp.power(_s_1_pre, "2")
1622
+ )
1623
+
1624
+ else:
1625
+ _gbd_prtlarea = (
1626
+ _gbd_step_sz * (4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_intcpt) / 3
1627
+ )
1628
+ # Area under boundary
1629
+ _gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, "2")
1630
+
1631
+ _gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
1632
+ np.float64
1633
+ )
1634
+ # Points defining boundary to point-of-symmetry
1635
+ return GuidelinesBoundary(
1636
+ np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
1637
+ round(float(_gbdry_area_total), gbd_dps),
1424
1638
  )
1425
1639
 
1426
- # Area under boundary
1427
- _gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, 2)
1428
1640
 
1641
+ def _shrratio_boundary_intcpt(
1642
+ _s_2_pre: float,
1643
+ _delta_star: mpf,
1644
+ _r_val: mpf,
1645
+ /,
1646
+ *,
1647
+ recapture_spec: Literal["inside-out", "proportional"],
1648
+ avg_method: Literal["arithmetic", "geometric", "distance"],
1649
+ wgtng_policy: Literal["cross-product-share", "own-share"] | None,
1650
+ ) -> float:
1429
1651
  match wgtng_policy:
1430
1652
  case "cross-product-share":
1431
1653
  _s_intcpt = _delta_star
1432
- case "own-product-share":
1654
+ case "own-share":
1433
1655
  _s_intcpt = mpf("1.0")
1434
1656
  case None if avg_method == "distance":
1435
1657
  _s_intcpt = _delta_star * mp.sqrt("2")
@@ -1445,11 +1667,4 @@ def shrratio_boundary_distance(
1445
1667
  case _:
1446
1668
  _s_intcpt = _s_2_pre
1447
1669
 
1448
- _gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
1449
- np.float64
1450
- )
1451
- # Points defining boundary to point-of-symmetry
1452
- return GuidelinesBoundary(
1453
- np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
1454
- round(float(_gbdry_area_total), gbd_dps),
1455
- )
1670
+ return _s_intcpt
@@ -27,14 +27,15 @@ from pathlib import Path
27
27
  from typing import Any, Literal
28
28
 
29
29
  import matplotlib.axes as mpa
30
- from joblib import Parallel, cpu_count, delayed
31
- from numpy import pi
32
- from xlsxwriter import Workbook
33
-
34
30
  import mergeron.core.excel_helper as xlh
35
31
  import mergeron.core.guidelines_boundaries as gbl
36
32
  import mergeron.ext.tol_colors as ptcolor
33
+ import mergeron.gen.investigations_stats as isl
34
+ from jinja2 import FileSystemLoader
35
+ from joblib import Parallel, cpu_count, delayed
37
36
  from mergeron import DATA_DIR
37
+ from numpy import pi
38
+ from xlsxwriter import Workbook
38
39
 
39
40
  PROG_PATH = Path(__file__)
40
41
 
@@ -79,6 +80,13 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
79
80
  "func_str": R"s_M",
80
81
  "func": gbl.combined_share_boundary,
81
82
  },
83
+ "SAG Average Div Ratio": {
84
+ "title_str": "Aggregated-diversion-ratio boundary, simple average",
85
+ "sheet_name": "SAG, average",
86
+ "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},
89
+ },
82
90
  "SAG Div Ratio Distance": {
83
91
  "title_str": "Aggregated-diversion-ratio boundary, distance",
84
92
  "sheet_name": "SAG, distance",
@@ -90,13 +98,6 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
90
98
  "avg_method": "distance",
91
99
  },
92
100
  },
93
- "SAG Average Div Ratio": {
94
- "title_str": "Aggregated-diversion-ratio boundary, simple average",
95
- "sheet_name": "SAG, average",
96
- "func_str": R"(d_{12} + d_{21}) / 2",
97
- "func": gbl.shrratio_boundary_xact_avg,
98
- "func_kwargs": {"recapture_spec": RECAPTURE_SPEC},
99
- },
100
101
  "CPSWAG Premerger HHI-contribution": {
101
102
  "title_str": "Premerger HHI-contribution boundary",
102
103
  "sheet_name": "CPSWAG, HHI-contrib-pre",
@@ -133,7 +134,7 @@ BDRY_SPECS_DICT: Mapping[str, Mapping[str, Any]] = {
133
134
  }
134
135
 
135
136
 
136
- def tabulate_boundary_stats(_gpubyr: Literal[1992, 2010, 2023], /) -> None:
137
+ def tabulate_boundary_stats(_gpubyr: gbl.HMGPubYear, /) -> None:
137
138
  """
138
139
  Parameters
139
140
  ----------
@@ -142,15 +143,17 @@ def tabulate_boundary_stats(_gpubyr: Literal[1992, 2010, 2023], /) -> None:
142
143
  are drawn
143
144
 
144
145
  """
146
+ _invres_rate_table_content = isl.StatsContainer()
147
+
145
148
  gso = gbl.GuidelinesThresholds(_gpubyr)
146
149
  _dhhi_val, _r_val, _g_val = (
147
150
  getattr(gso.presumption, _f) for _f in ("delta", "rec", "guppi")
148
151
  )
149
152
 
150
153
  _dhhi_seq = (
151
- (0.01, 0.02, 0.03125, 0.05, _dhhi_val)
154
+ (0.005, 0.01, 0.02, gso.imputed_presumption.delta, 0.08)
152
155
  if _gpubyr == 2010
153
- else (0.005, 0.01, 0.02, 0.03125, _dhhi_val)
156
+ else (0.005, 0.01, 0.02, gso.imputed_presumption.delta, 0.08)
154
157
  )
155
158
 
156
159
  _bdry_approx_data_dict = {
@@ -166,7 +169,7 @@ def tabulate_boundary_stats(_gpubyr: Literal[1992, 2010, 2023], /) -> None:
166
169
  ),
167
170
  )
168
171
  for _k in BDRY_SPECS_DICT
169
- if not _k.endswith("Distance")
172
+ # if not _k.endswith("Distance")
170
173
  }
171
174
  }
172
175
  _bdry_approx_data_dict |= {
@@ -176,32 +179,46 @@ def tabulate_boundary_stats(_gpubyr: Literal[1992, 2010, 2023], /) -> None:
176
179
  }
177
180
 
178
181
  _bdry_data = Parallel(n_jobs=-1)(
179
- delayed(_dhhi_stats)(_dhhi_val, _r_val, _g_val) for _dhhi_val in _dhhi_seq
182
+ delayed(_dhhi_stats)(_dhhi_val, _r_val) for _dhhi_val in _dhhi_seq
180
183
  )
181
184
  _bdry_approx_data_dict |= dict(_bdry_data)
182
185
 
183
- print(" & ".join(_k for _k in _bdry_approx_data_dict), R" \\")
186
+ _data_str = ""
187
+ _data_str = "{} \\\\ \n".format(
188
+ " & ".join(
189
+ _k.replace("Criterion", R"{\text{} \\ Criterion}") # \phantom{Criterion}
190
+ for _k in _bdry_approx_data_dict
191
+ )
192
+ )
184
193
  for _sk in _bdry_approx_data_dict["Criterion"]:
185
- print(
186
- " & ".join(
187
- _bdry_approx_data_dict[_k][_sk] for _k in _bdry_approx_data_dict
188
- ),
189
- R"\\",
194
+ _data_str += "{} \\\\ \n".format(
195
+ " & ".join(_bdry_approx_data_dict[_k][_sk] for _k in _bdry_approx_data_dict)
190
196
  )
197
+ print(_data_str)
198
+
199
+ _invres_rate_table_content.data_str = _data_str
200
+
201
+ _j2_env = isl.latex_jinja_env
202
+ _j2_env.loader = FileSystemLoader(str(PROG_PATH.parent / "templates"))
203
+ _j2_templ = _j2_env.get_template(
204
+ "concentration_as_diversion_intrinsic_enforcement_rates.tex.jinja2"
205
+ )
206
+ PROG_PATH.parents[1].joinpath(
207
+ f"{PROG_PATH.stem}_intrinsic_enforcement_rates_{_gpubyr}.tex"
208
+ ).write_text(_j2_templ.render(tmpl_data=_invres_rate_table_content))
191
209
 
192
210
 
193
- def _dhhi_stats(
194
- _dhhi_val: float, _r_val: float, _g_val: float
195
- ) -> tuple[str, dict[str, str]]:
211
+ def _dhhi_stats(_dhhi_val: float, _r_val: float) -> tuple[str, dict[str, str]]:
196
212
  _dhhi_val = round(_dhhi_val, 5)
197
213
 
198
- _r_val = round(_r_val, 4)
199
- _s_mid = sqrt(_dhhi_val / 2)
214
+ _divr_val = gbl.gbd_from_dsf(_dhhi_val, r_bar=_r_val)
215
+ _delta_val = gbl.critical_shrratio(_divr_val, r_bar=_r_val)
216
+ # _s_mid = sqrt(_dhhi_val / 2)
200
217
 
201
- _delta_val = _s_mid / (1 - _s_mid)
202
- if _dhhi_val * 1e4 in (50, 100, 200):
203
- _delta_val = gbl.round_cust(_r_val * _delta_val) / _r_val
204
- _divr_val = _r_val * _delta_val
218
+ # _delta_val = _s_mid / (1 - _s_mid)
219
+ # if _dhhi_val * 1e4 in (50, 100, 200):
220
+ # _delta_val = gbl.critical_shrratio()(_r_val * _delta_val) / _r_val
221
+ # _divr_val = _r_val * _delta_val
205
222
 
206
223
  print(
207
224
  "Processing data for ΔHHI = {0:.{1}f} points;".format(
@@ -238,15 +255,16 @@ def _bdry_stats_col(
238
255
  return _bdry_spec, f"{_hhi_m_pre_prob:6.5f}"
239
256
  case _ if "Div Ratio" in _bdry_spec:
240
257
  _gbd_func = BDRY_SPECS_DICT[_bdry_spec]["func"]
241
- _, _within_bdry_area = _gbd_func(
258
+ _within_bdry_area = _gbd_func(
242
259
  _delta_val, _r_val, **BDRY_SPECS_DICT[_bdry_spec].get("func_kwargs", {})
243
- )
260
+ ).area
244
261
  _within_bdry_prob = 2 * _within_bdry_area
245
- _within_conc_bdry_prob = (
246
- _hhi_m_pre_prob
247
- if _bdry_spec.startswith("CPSWAG")
248
- else (_cs_prob if _bdry_spec.startswith("SAG") else _dhhi_prob)
249
- )
262
+ if _bdry_spec.startswith("CPSWAG"):
263
+ _within_conc_bdry_prob = _hhi_m_pre_prob
264
+ elif _bdry_spec.startswith("SAG"):
265
+ _within_conc_bdry_prob = _cs_prob
266
+ else:
267
+ _within_conc_bdry_prob = _dhhi_prob
250
268
 
251
269
  return _bdry_spec, R"{{ {:6.5f} \\ {:.2f}\% }}".format(
252
270
  _within_bdry_prob,
@@ -257,7 +275,7 @@ def _bdry_stats_col(
257
275
 
258
276
 
259
277
  def plot_and_save_boundary_coords(
260
- _gpubyr: Literal[1992, 2010, 2023],
278
+ _gpubyr: gbl.HMGPubYear,
261
279
  _xl_book: Workbook,
262
280
  /,
263
281
  layout: Literal["collected", "distributed"] = "collected",
@@ -599,8 +617,9 @@ def boundary_data_to_worksheet(
599
617
 
600
618
 
601
619
  if __name__ == "__main__":
602
- for gpubyr in [1992, 2010, 2023][2:]:
603
- # tabulate_boundary_stats(gpubyr)
620
+ gpubyrs: list[gbl.HMGPubYear] = [1992, 2010, 2023]
621
+ for gpubyr in gpubyrs[2:][:1]:
622
+ tabulate_boundary_stats(gpubyr)
604
623
 
605
624
  # Initiliaze workbook for saving boundary coordinates
606
625
  with Workbook(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mergeron
3
- Version: 2024.738949.1
3
+ Version: 2024.738949.5
4
4
  Summary: Analysis of standards defined in Horizontal Merger Guidelines
5
5
  License: MIT
6
6
  Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
@@ -5,11 +5,11 @@ mergeron/core/__init__.py,sha256=iyfxkX3-SoMS4ZQZKHKPn8JEMN536vpty9oSZf0LHv8,115
5
5
  mergeron/core/damodaran_margin_data.py,sha256=DHTQdFjuZ5Yl3Dbq0db0QR4FHUqJpZj4yi5zdUncLtg,8166
6
6
  mergeron/core/excel_helper.py,sha256=wU3Y325r_YNfoyr92y605m_9ZCDJ01WiR-vahi4KGmc,8094
7
7
  mergeron/core/ftc_merger_investigations_data.py,sha256=YYkgeF-b_2_Tk8dsy_4dMz5QpV5UQEr7tB9ORiqKmww,26760
8
- mergeron/core/guidelines_boundaries.py,sha256=wAoX5Es83g87jXAw1s65C8xrKwzLxwa81aP-N3LdgKk,44733
8
+ mergeron/core/guidelines_boundaries.py,sha256=E38g6aonHqqFiSP0Duh_UfUGaHkZlKQt-23Q7EAEtBA,50541
9
9
  mergeron/core/proportions_tests.py,sha256=tCrbya1el5u1OFOXphODP6yWOGywuNY6z9LBTsNRKzM,15320
10
10
  mergeron/core/pseudorandom_numbers.py,sha256=mSAMJ1zZ58k8j9duT3Ohi_1TCjZaFG1Ud1P_JI1ryYY,9430
11
11
  mergeron/examples/__init__.py,sha256=iyfxkX3-SoMS4ZQZKHKPn8JEMN536vpty9oSZf0LHv8,115
12
- mergeron/examples/concentration_as_diversion.py,sha256=p9-VzqC8Tb3fMJD1mLnqmMnvOCrCTULjIPCEG0RhASQ,20645
12
+ mergeron/examples/concentration_as_diversion.py,sha256=oDo5InVGLL-pON_ror8XWOMwC6S2dnDvei7Yw_AXgFw,21620
13
13
  mergeron/examples/enforcement_boundaries_for_mergers_with_asymmetric_shares.py,sha256=-jOHETKp788pqKTv7U25t28UzVtJNMmUn1FVH7jCiCU,16020
14
14
  mergeron/examples/enforcement_boundaries_for_symmetric_firm_mergers.py,sha256=DAljm5NJquE56f-x4pLXzKCdhYQyVpWaM8uGlD6rIEs,5779
15
15
  mergeron/examples/example_parameterizations.py,sha256=VP-hi7L0j30ffcEzmJ3P8mOj1VjwEWKCTZSx_CaVQxA,4197
@@ -37,6 +37,6 @@ mergeron/jinja_LaTex_templates/mergeron.cls,sha256=AV2mk4-uERvAuMkE95Ka7el6LZsb0
37
37
  mergeron/jinja_LaTex_templates/mergeron_table_collection_template.tex.jinja2,sha256=nr6xUI0_2KHG4Sz9k1JFVQjs2h9qS9BGt1MeE6Tygs8,2429
38
38
  mergeron/jinja_LaTex_templates/setup_tikz_tables.tex.jinja2,sha256=WKVxtp3eoMchfGliQAJMj4w2FtBkWG5z2V3-hBYUYUQ,3292
39
39
  mergeron/py.typed,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
40
- mergeron-2024.738949.1.dist-info/METADATA,sha256=JMtJF46he9UZoHGhQaGjWHEgFXP3y1jEprH4C5gxvoc,6362
41
- mergeron-2024.738949.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
42
- mergeron-2024.738949.1.dist-info/RECORD,,
40
+ mergeron-2024.738949.5.dist-info/METADATA,sha256=v_U_sf-0kwTsgcg3SpLqUv5X9iaTHBOVhuMAt4N7xb0,6362
41
+ mergeron-2024.738949.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
42
+ mergeron-2024.738949.5.dist-info/RECORD,,