bayesianconstraints.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # bayesianconstraints.py
  2. import numpy as np
  3. import pandas as pd
  4. import matplotlib.pyplot as plt
  5. def logistic(z):
  6. z = np.clip(z, -60, 60)
  7. return 1.0 / (1.0 + np.exp(-z))
  8. def logit(p):
  9. p = np.clip(p, 1e-12, 1 - 1e-12)
  10. return np.log(p) - np.log(1 - p)
  11. def dE_gamma(x, a, b, k, theta, s=1.0):
  12. x = np.asarray(x, float)
  13. return (a - k) * np.log(x) - (a + b) * np.log1p(x / s) + x / theta
  14. def dE_lognorm(x, a, b, mu, sigma, s=1.0):
  15. x = np.asarray(x, float)
  16. return ((mu - np.log(x)) ** 2) / (2.0 * sigma ** 2) + a * np.log(x) - (a + b) * np.log1p(x / s)
  17. def check_gamma_constraints(a, b, k, theta, s):
  18. positivity = (a > 0) and (b > 0) and (k > 0) and (theta > 0) and (s > 0)
  19. if not positivity:
  20. status = "FAIL"
  21. note = "all parameters must be > 0"
  22. elif a > k:
  23. status = "MEET"
  24. note = "a > k"
  25. elif a == k:
  26. status = "EDGE"
  27. note = "a = k"
  28. else:
  29. status = "FAIL"
  30. note = "a < k"
  31. return {
  32. "model": "BetaPrime vs Gamma",
  33. "a": a,
  34. "b": b,
  35. "k": k,
  36. "theta": theta,
  37. "s": s,
  38. "all_positive": positivity,
  39. "a_ge_k": positivity and (a >= k),
  40. "status": status,
  41. "note": note,
  42. }
  43. def check_lognorm_constraints(a, b, mu, sigma, s):
  44. positivity = (a > 0) and (b > 0) and (sigma > 0) and (s > 0)
  45. if not positivity:
  46. status = "FAIL"
  47. note = "a,b,sigma,s must be > 0"
  48. else:
  49. status = "FAIL"
  50. note = "left tail goes to 1, not 0"
  51. return {
  52. "model": "BetaPrime vs LogNormal",
  53. "a": a,
  54. "b": b,
  55. "mu": mu,
  56. "sigma": sigma,
  57. "s": s,
  58. "all_positive": positivity,
  59. "status": status,
  60. "note": note,
  61. }
  62. def make_gamma_constraint_table(param_list):
  63. return pd.DataFrame([check_gamma_constraints(**p) for p in param_list])
  64. def make_lognorm_constraint_table(param_list):
  65. return pd.DataFrame([check_lognorm_constraints(**p) for p in param_list])
  66. def plot_gamma_constraint_curves(param_meet, param_edge_fail, p_prior=5/58,
  67. x=None, figsize=(10, 6)):
  68. if x is None:
  69. x = np.logspace(-12, 3, 900)
  70. c0 = logit(p_prior)
  71. fig, ax = plt.subplots(figsize=figsize)
  72. for P in param_meet:
  73. p = logistic(c0 + dE_gamma(x, **P))
  74. label = f"MEET (a={P['a']}, b={P['b']}, k={P['k']}, θ={P['theta']}, s={P['s']})"
  75. ax.plot(x, p, label=label)
  76. for P in param_edge_fail:
  77. p = logistic(c0 + dE_gamma(x, **P))
  78. tag = "EDGE" if P["a"] == P["k"] else "FAIL"
  79. label = f"{tag} (a={P['a']}, b={P['b']}, k={P['k']}, θ={P['theta']}, s={P['s']})"
  80. ax.plot(x, p, linestyle="--", label=label)
  81. ax.set_xscale("log")
  82. ax.set_ylim(-0.05, 1.05)
  83. ax.set_xlabel("x")
  84. ax.set_ylabel("P(AE | x)")
  85. ax.set_title("Gamma NC — posterior-like curves")
  86. ax.grid(True, which="both")
  87. ax.legend(fontsize=8)
  88. fig.tight_layout()
  89. return fig, ax
  90. def plot_lognorm_constraint_curves(param_list, p_prior=5/58,
  91. x=None, figsize=(10, 6)):
  92. if x is None:
  93. x = np.logspace(-12, 3, 900)
  94. c0 = logit(p_prior)
  95. fig, ax = plt.subplots(figsize=figsize)
  96. for P in param_list:
  97. p = logistic(c0 + dE_lognorm(x, **P))
  98. label = f"(a={P['a']}, b={P['b']}, μ={P['mu']}, σ={P['sigma']}, s={P['s']})"
  99. ax.plot(x, p, label=label)
  100. ax.set_xscale("log")
  101. ax.set_ylim(-0.05, 1.05)
  102. ax.set_xlabel("x")
  103. ax.set_ylabel("P(AE | x)")
  104. ax.set_title("LogNormal NC — posterior-like curves")
  105. ax.grid(True, which="both")
  106. ax.legend(fontsize=8)
  107. fig.tight_layout()
  108. return fig, ax