Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
example_plot_jointdistrib.py
Go to the documentation of this file.
1from __future__ import annotations
2
3import os
4import math
5from typing import Any, Tuple
6
7import numpy as np
8import matplotlib.pyplot as plt
9from statistics import NormalDist
10
11from pyhyperiso.core.Math.RealMatrix import Matrix
12
13
14from pyhyperiso.core.Statistic.JointDistribution import JointDistributionFactory as JF
15
16from pyhyperiso.core.Statistic.MarginalDistribution import DistributionFactoryWrapper as MF
17
18from pyhyperiso.core.Statistic.MarginalDistribution import MarginalKind
19
20from pyhyperiso.core.Statistic.MarginalConfig import (
21 GaussianMarginalConfig as GaussianCfg,
22 SplitGaussianMarginalConfig as SplitGaussianCfg,
23 LikelihoodMarginalConfig as LikelihoodCfg,
24)
25
26from pyhyperiso.core.Statistic.Copula import CopulaKind
27
28from pyhyperiso.core.Statistic.CopulaConfig import (
29 GaussianCopulaConfigPy as GaussianCopulaCfg,
30 StudentTCopulaConfigPy as StudentTCopulaCfg,
31)
32
33
34def ensure_dir(path: str) -> None:
35 os.makedirs(path, exist_ok=True)
36
37
38def ar1_corr(d: int, rho: float) -> Matrix:
39 if not (-0.999 < rho < 0.999):
40 raise ValueError("rho doit être dans (-1,1).")
41 R = [[(rho ** abs(i - j)) for j in range(d)] for i in range(d)]
42 return Matrix(R)
43
44
46 xs = np.linspace(-5.0, 5.0, 401)
47 w = np.exp(-0.5 * ((xs - (-1.2)) / 0.7) ** 2) + 0.65 * np.exp(-0.5 * ((xs - 1.6) / 1.1) ** 2)
48 w = w / np.sum(w)
49 return LikelihoodCfg(values=xs.tolist(), weights=w.tolist())
50
51
52def _safe_float(x) -> float:
53 try:
54 return float(x)
55 except Exception:
56 return float("nan")
57
58
59def ecdf(samples: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
60 s = np.sort(samples)
61 y = np.arange(1, len(s) + 1) / len(s)
62 return s, y
63
64
65def gaussianize(U: np.ndarray) -> np.ndarray:
66 nd = NormalDist()
67 eps = 1e-12
68 Uc = np.clip(U, eps, 1 - eps)
69 inv = np.vectorize(nd.inv_cdf, otypes=[float])
70 return inv(Uc)
71
72
73def spearman_corr(U: np.ndarray) -> np.ndarray:
74 order = np.argsort(U, axis=0)
75 ranks = np.empty_like(order, dtype=float)
76 for j in range(U.shape[1]):
77 ranks[order[:, j], j] = np.arange(1, len(U) + 1, dtype=float)
78 return np.corrcoef(ranks, rowvar=False)
79
80
81def plot_hist_pdf(samples: np.ndarray, dist, name: str, outpath: str) -> None:
82 try:
83 xlo = _safe_float(dist.ppf(0.001))
84 xhi = _safe_float(dist.ppf(0.999))
85 if not (np.isfinite(xlo) and np.isfinite(xhi) and xhi > xlo):
86 raise ValueError
87 pad = 0.08 * (xhi - xlo)
88 xs = np.linspace(xlo - pad, xhi + pad, 700)
89 except Exception:
90 xs = np.linspace(np.min(samples), np.max(samples), 700)
91
92 logp = np.array([_safe_float(dist.logpdf(float(x))) for x in xs], dtype=float)
93 pdf = np.exp(logp)
94
95 plt.figure(figsize=(10.2, 5.6))
96 plt.hist(samples, bins=60, density=True, alpha=0.35, label="samples (density)")
97 plt.plot(xs, pdf, linewidth=2.3, label="marginal pdf")
98 plt.title(f"{name} — marginal PDF vs samples")
99 plt.xlabel("x")
100 plt.ylabel("density")
101 plt.grid(True, alpha=0.28)
102 plt.minorticks_on()
103 plt.legend(frameon=True)
104 plt.tight_layout()
105 plt.savefig(outpath, dpi=200)
106 plt.close()
107
108
109def plot_cdf_ecdf(samples: np.ndarray, dist, name: str, outpath: str) -> None:
110 try:
111 xlo = _safe_float(dist.ppf(0.001))
112 xhi = _safe_float(dist.ppf(0.999))
113 if not (np.isfinite(xlo) and np.isfinite(xhi) and xhi > xlo):
114 raise ValueError
115 pad = 0.08 * (xhi - xlo)
116 xs = np.linspace(xlo - pad, xhi + pad, 700)
117 except Exception:
118 xs = np.linspace(np.min(samples), np.max(samples), 700)
119
120 cdf_th = np.array([_safe_float(dist.cdf(float(x))) for x in xs], dtype=float)
121 s, y = ecdf(samples)
122
123 plt.figure(figsize=(10.2, 5.6))
124 plt.plot(xs, cdf_th, linewidth=2.3, label="theoretical CDF")
125 plt.step(s, y, where="post", linewidth=1.8, alpha=0.9, label="ECDF")
126 plt.title(f"{name} — marginal CDF vs ECDF")
127 plt.xlabel("x")
128 plt.ylabel("CDF(x)")
129 plt.ylim(-0.02, 1.02)
130 plt.grid(True, alpha=0.28)
131 plt.minorticks_on()
132 plt.legend(frameon=True)
133 plt.tight_layout()
134 plt.savefig(outpath, dpi=200)
135 plt.close()
136
137
138def plot_uniformity(u: np.ndarray, name: str, outpath: str) -> None:
139 plt.figure(figsize=(7.6, 4.6))
140 plt.hist(u, bins=50, density=True, alpha=0.45, label="u = F(x)")
141 plt.plot([0, 1], [1, 1], linestyle="--", linewidth=1.6, label="Uniform(0,1) density")
142 plt.title(f"{name} — PIT check (u should be uniform)")
143 plt.xlabel("u")
144 plt.ylabel("density")
145 plt.xlim(0, 1)
146 plt.ylim(bottom=0)
147 plt.grid(True, alpha=0.25)
148 plt.minorticks_on()
149 plt.legend(frameon=True)
150 plt.tight_layout()
151 plt.savefig(outpath, dpi=200)
152 plt.close()
153
154
155def plot_hist2d(u1: np.ndarray, u2: np.ndarray, title: str, outpath: str) -> None:
156 plt.figure(figsize=(6.9, 6.5))
157 plt.hist2d(u1, u2, bins=80, density=True)
158 plt.colorbar(label="density")
159 plt.plot([0, 1], [0, 1], linestyle="--", linewidth=1.2)
160 plt.title(title)
161 plt.xlabel("dim 1")
162 plt.ylabel("dim 2")
163 plt.xlim(0, 1)
164 plt.ylim(0, 1)
165 plt.grid(True, alpha=0.22)
166 plt.tight_layout()
167 plt.savefig(outpath, dpi=200)
168 plt.close()
169
170
171def plot_heatmap(M: np.ndarray, title: str, outpath: str) -> None:
172 plt.figure(figsize=(6.9, 6.0))
173 im = plt.imshow(M, vmin=-1, vmax=1, interpolation="nearest")
174 plt.colorbar(im, label="correlation")
175 plt.title(title)
176 plt.xlabel("dim")
177 plt.ylabel("dim")
178
179 n = M.shape[0]
180 for i in range(n):
181 for j in range(n):
182 plt.text(j, i, f"{M[i, j]:.2f}", ha="center", va="center", fontsize=8)
183
184 plt.tight_layout()
185 plt.savefig(outpath, dpi=200)
186 plt.close()
187
188
189def plot_joint_density_2d(jd, X: np.ndarray, title: str, outpath: str, grid: int = 120) -> None:
190 x1 = X[:, 0]
191 x2 = X[:, 1]
192 q1 = np.quantile(x1, [0.002, 0.998])
193 q2 = np.quantile(x2, [0.002, 0.998])
194 pad1 = 0.08 * (q1[1] - q1[0])
195 pad2 = 0.08 * (q2[1] - q2[0])
196
197 xs = np.linspace(q1[0] - pad1, q1[1] + pad1, grid)
198 ys = np.linspace(q2[0] - pad2, q2[1] + pad2, grid)
199
200 D = np.empty((grid, grid), dtype=float)
201 for i, y in enumerate(ys):
202 row = []
203 for x in xs:
204 lp = float(jd.logpdf([float(x), float(y)]))
205 row.append(math.exp(lp))
206 D[i, :] = row
207
208 plt.figure(figsize=(7.6, 6.2))
209 im = plt.imshow(
210 D, origin="lower", extent=[xs[0], xs[-1], ys[0], ys[-1]], interpolation="nearest"
211 )
212 plt.colorbar(im, label="joint density")
213 plt.title(title)
214 plt.xlabel("x1")
215 plt.ylabel("x2")
216 plt.grid(False)
217 plt.tight_layout()
218 plt.savefig(outpath, dpi=200)
219 plt.close()
220
221
223 outdir: str = "joint_plots",
224 seed: int = 123,
225 n: int = 15000,
226 copula_kind: str = "GAUSSIAN", # "GAUSSIAN" or "STUDENT_T"
227 nu: int = 6,
228 rho: float = 0.65,
229) -> None:
230 ensure_dir(outdir)
231
232 marginal_types = [
233 MarginalKind.GAUSSIAN,
234 MarginalKind.SPLIT_GAUSSIAN
235 if hasattr(MarginalKind, "SPLIT_GAUSSIAN")
236 else MarginalKind.HALF_GAUSSIAN,
237 ]
238
239 marginal_cfgs = [
240 GaussianCfg(mu=0.6, sigma=1.15),
241 SplitGaussianCfg(mu=0.0, sigma_p=0.75, sigma_m=1.55),
242 ]
243
244 d = len(marginal_types)
245
246 marginals = [MF.create(marginal_types[i], marginal_cfgs[i], seed=seed + i) for i in range(d)]
247
248 R = ar1_corr(d, rho)
249 if copula_kind.upper() == "GAUSSIAN":
250 c_kind = CopulaKind.GAUSSIAN
251 c_cfg = GaussianCopulaCfg(R=R)
252 else:
253 c_kind = CopulaKind.STUDENT_T
254 c_cfg = StudentTCopulaCfg(R=R, nu=int(nu))
255
256 jd = JF.create(marginal_types, marginal_cfgs, c_kind, c_cfg, seed=seed)
257
258 X = np.array(jd.sample(int(n)), dtype=float) # shape (n, d)
259
260 for i in range(d):
261 xi = X[:, i]
262 dist = marginals[i]
263
265 xi,
266 dist,
267 name=f"Marginal {i + 1}",
268 outpath=os.path.join(outdir, f"marg_{i + 1}_pdf.png"),
269 )
271 xi,
272 dist,
273 name=f"Marginal {i + 1}",
274 outpath=os.path.join(outdir, f"marg_{i + 1}_cdf.png"),
275 )
276
277 ui = np.array([_safe_float(dist.cdf(float(x))) for x in xi], dtype=float)
279 ui, name=f"Marginal {i + 1}", outpath=os.path.join(outdir, f"marg_{i + 1}_pit.png")
280 )
281
282 U = np.empty_like(X, dtype=float)
283 for i in range(d):
284 dist = marginals[i]
285 U[:, i] = np.array([_safe_float(dist.cdf(float(x))) for x in X[:, i]], dtype=float)
286 U = np.clip(U, 1e-12, 1 - 1e-12)
287
288 if d >= 2:
290 U[:, 0],
291 U[:, 1],
292 title="Dependence check — copula space (u1,u2)",
293 outpath=os.path.join(outdir, "copula_u1_u2_hist2d.png"),
294 )
295
296 S = spearman_corr(U)
297 Z = gaussianize(U)
298 C = np.corrcoef(Z, rowvar=False)
299
300 plot_heatmap(S, "Spearman corr of U (ranks)", os.path.join(outdir, "copula_spearman.png"))
302 C,
303 "Corr of Z = Phi^-1(U) (compare to target R)",
304 os.path.join(outdir, "copula_gaussianized_corr.png"),
305 )
306
307 if d == 2:
309 jd,
310 X,
311 title="Joint density heatmap: exp(logpdf(x))",
312 outpath=os.path.join(outdir, "joint_density_heatmap.png"),
313 grid=120,
314 )
315
316 print(f"[OK] Saved plots in: {outdir}/")
317 print(
318 f"Joint dim = {jd.dim()} ; mean logpdf(sample) ≈ {float(np.mean([jd.logpdf(row.tolist()) for row in X[:2000]])):.3f}"
319 )
320 if copula_kind.upper() == "GAUSSIAN":
321 print("Tip: for Gaussian copula, Corr(Phi^-1(U)) should be close to your R.")
322
323
324if __name__ == "__main__":
325 main(
326 outdir="joint_plots",
327 seed=123,
328 n=15000,
329 copula_kind="GAUSSIAN", # or "STUDENT_T"
330 nu=6,
331 rho=0.65,
332 )
np.ndarray gaussianize(np.ndarray U)
None plot_hist_pdf(np.ndarray samples, dist, str name, str outpath)
None plot_hist2d(np.ndarray u1, np.ndarray u2, str title, str outpath)
Matrix ar1_corr(int d, float rho)
np.ndarray spearman_corr(np.ndarray U)
Tuple[np.ndarray, np.ndarray] ecdf(np.ndarray samples)
None plot_joint_density_2d(jd, np.ndarray X, str title, str outpath, int grid=120)
None plot_heatmap(np.ndarray M, str title, str outpath)
None plot_cdf_ecdf(np.ndarray samples, dist, str name, str outpath)
None plot_uniformity(np.ndarray u, str name, str outpath)