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)
81def plot_hist_pdf(samples: np.ndarray, dist, name: str, outpath: str) ->
None:
85 if not (np.isfinite(xlo)
and np.isfinite(xhi)
and xhi > xlo):
87 pad = 0.08 * (xhi - xlo)
88 xs = np.linspace(xlo - pad, xhi + pad, 700)
90 xs = np.linspace(np.min(samples), np.max(samples), 700)
92 logp = np.array([
_safe_float(dist.logpdf(float(x)))
for x
in xs], dtype=float)
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")
100 plt.ylabel(
"density")
101 plt.grid(
True, alpha=0.28)
103 plt.legend(frameon=
True)
105 plt.savefig(outpath, dpi=200)
109def plot_cdf_ecdf(samples: np.ndarray, dist, name: str, outpath: str) ->
None:
113 if not (np.isfinite(xlo)
and np.isfinite(xhi)
and xhi > xlo):
115 pad = 0.08 * (xhi - xlo)
116 xs = np.linspace(xlo - pad, xhi + pad, 700)
118 xs = np.linspace(np.min(samples), np.max(samples), 700)
120 cdf_th = np.array([
_safe_float(dist.cdf(float(x)))
for x
in xs], dtype=float)
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")
129 plt.ylim(-0.02, 1.02)
130 plt.grid(
True, alpha=0.28)
132 plt.legend(frameon=
True)
134 plt.savefig(outpath, dpi=200)
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)")
144 plt.ylabel(
"density")
147 plt.grid(
True, alpha=0.25)
149 plt.legend(frameon=
True)
151 plt.savefig(outpath, dpi=200)
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)
165 plt.grid(
True, alpha=0.22)
167 plt.savefig(outpath, dpi=200)
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")
182 plt.text(j, i, f
"{M[i, j]:.2f}", ha=
"center", va=
"center", fontsize=8)
185 plt.savefig(outpath, dpi=200)
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])
197 xs = np.linspace(q1[0] - pad1, q1[1] + pad1, grid)
198 ys = np.linspace(q2[0] - pad2, q2[1] + pad2, grid)
200 D = np.empty((grid, grid), dtype=float)
201 for i, y
in enumerate(ys):
204 lp = float(jd.logpdf([float(x), float(y)]))
205 row.append(math.exp(lp))
208 plt.figure(figsize=(7.6, 6.2))
210 D, origin=
"lower", extent=[xs[0], xs[-1], ys[0], ys[-1]], interpolation=
"nearest"
212 plt.colorbar(im, label=
"joint density")
218 plt.savefig(outpath, dpi=200)
223 outdir: str =
"joint_plots",
226 copula_kind: str =
"GAUSSIAN",
233 MarginalKind.GAUSSIAN,
234 MarginalKind.SPLIT_GAUSSIAN
235 if hasattr(MarginalKind,
"SPLIT_GAUSSIAN")
236 else MarginalKind.HALF_GAUSSIAN,
240 GaussianCfg(mu=0.6, sigma=1.15),
241 SplitGaussianCfg(mu=0.0, sigma_p=0.75, sigma_m=1.55),
244 d = len(marginal_types)
246 marginals = [MF.create(marginal_types[i], marginal_cfgs[i], seed=seed + i)
for i
in range(d)]
249 if copula_kind.upper() ==
"GAUSSIAN":
250 c_kind = CopulaKind.GAUSSIAN
251 c_cfg = GaussianCopulaCfg(R=R)
253 c_kind = CopulaKind.STUDENT_T
254 c_cfg = StudentTCopulaCfg(R=R, nu=int(nu))
256 jd = JF.create(marginal_types, marginal_cfgs, c_kind, c_cfg, seed=seed)
258 X = np.array(jd.sample(int(n)), dtype=float)
267 name=f
"Marginal {i + 1}",
268 outpath=os.path.join(outdir, f
"marg_{i + 1}_pdf.png"),
273 name=f
"Marginal {i + 1}",
274 outpath=os.path.join(outdir, f
"marg_{i + 1}_cdf.png"),
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")
282 U = np.empty_like(X, dtype=float)
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)
292 title=
"Dependence check — copula space (u1,u2)",
293 outpath=os.path.join(outdir,
"copula_u1_u2_hist2d.png"),
298 C = np.corrcoef(Z, rowvar=
False)
300 plot_heatmap(S,
"Spearman corr of U (ranks)", os.path.join(outdir,
"copula_spearman.png"))
303 "Corr of Z = Phi^-1(U) (compare to target R)",
304 os.path.join(outdir,
"copula_gaussianized_corr.png"),
311 title=
"Joint density heatmap: exp(logpdf(x))",
312 outpath=os.path.join(outdir,
"joint_density_heatmap.png"),
316 print(f
"[OK] Saved plots in: {outdir}/")
318 f
"Joint dim = {jd.dim()} ; mean logpdf(sample) ≈ {float(np.mean([jd.logpdf(row.tolist()) for row in X[:2000]])):.3f}"
320 if copula_kind.upper() ==
"GAUSSIAN":
321 print(
"Tip: for Gaussian copula, Corr(Phi^-1(U)) should be close to your R.")