MEIDNet-Matter / engine /meidnet_eval /plot_ablation.py
Babu09's picture
Deploy MEIDNet Matter 0.3.0 (fc3332f)
d454209 verified
Raw History Blame Contribute Delete
4.32 kB
"""Small multiples: for every ablation setting, the band gap of each returned structure (DFT where Perov-5 has it,
else the CGCNN judge on the relaxed cell) against the target, with the label the engine shows and the diagonal."""
import os
import numpy as np
import pandas as pd
import matplotlib
def main():
"""The script's work; nothing runs on import."""
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
HERE = os.path.dirname(os.path.abspath(__file__))
RES = f"{HERE}/results"
SURFACE, INK, INK2, GRID = "#fcfcfb", "#0b0b0b", "#5f5e58", "#e4e3dc"
TRUE_C, LABEL_C = "#2a78d6", "#eb6834" # categorical slots 1 and 2 of the reference palette
allc = pd.read_csv(f"{RES}/all_candidates_enriched.csv")
tab = pd.read_csv(f"{RES}/ablation_table.csv").set_index("tag")
order = [t for t in ["R", "O", "G0", "G2", "G5", "AR", "OA", "OAall", "ARs1", "OAs1", "G7", "S0", "G1", "G6", "G3", "G4", "G5w1000",
"G8", "G9", "G10", "V2S", "V2S1S"] if t in tab.index]
allc["truth"] = allc.dft_gap.fillna(allc.cgcnn_gap)
ncol = 4; nrow = int(np.ceil(len(order) / ncol))
plt.rcParams.update({"font.size": 9, "axes.edgecolor": GRID, "axes.labelcolor": INK2, "xtick.color": INK2,
"ytick.color": INK2, "text.color": INK, "font.family": "DejaVu Sans"})
fig, axes = plt.subplots(nrow, ncol, figsize=(3.4 * ncol, 2.9 * nrow + 1.1), sharex=True, sharey=True, facecolor=SURFACE)
axes = np.atleast_2d(axes)
rng = np.random.RandomState(0)
for k, ax in enumerate(axes.flat):
if k >= len(order):
ax.axis("off"); continue
tag = order[k]; d = allc[allc.tag == tag]; r = tab.loc[tag]
ax.set_facecolor(SURFACE); ax.grid(True, color=GRID, linewidth=0.7); ax.set_axisbelow(True)
for s in ("top", "right"):
ax.spines[s].set_visible(False)
ax.plot([0, 6.5], [0, 6.5], color=INK2, linestyle=(0, (4, 3)), linewidth=1.0, zorder=1)
jit = rng.uniform(-0.16, 0.16, len(d))
if d.label_gap.notna().any():
ax.scatter(d.target + jit - 0.1, d.label_gap, s=14, color=LABEL_C, edgecolor=SURFACE, linewidth=0.8, zorder=3)
ax.scatter(d.target + jit + 0.1, d.truth, s=14, color=TRUE_C, edgecolor=SURFACE, linewidth=0.8, zorder=3)
m = d.groupby("target").truth.mean()
if len(m) > 1:
ax.plot(m.index + 0.1, m.values, color=TRUE_C, linewidth=1.8, zorder=4)
rho = r.rho_dft
ax.set_title(f"{tag}: {r.model}", loc="left", fontsize=9.5, color=INK, pad=14)
ax.text(0.0, 1.015, f"labels {r.labels} 路 latent {r.latent} 路 pull {r.manifold}", transform=ax.transAxes, fontsize=7.5, color=INK2)
ax.text(0.03, 0.96, f"蟻(DFT) {rho:+.2f}" if np.isfinite(rho) else "蟻(DFT) n/a", transform=ax.transAxes, fontsize=8.5,
color=INK, va="top", fontweight="bold")
ax.text(0.03, 0.85, f"{int(r.returned)} returned 路 {int(r.distinct)} distinct", transform=ax.transAxes, fontsize=7.5, color=INK2, va="top")
ax.set_xlim(-0.5, 6.5); ax.set_ylim(-0.3, 7.3); ax.set_xticks(range(7))
for ax in axes[-1]:
ax.set_xlabel("Target gap (eV)")
for ax in axes[:, 0]:
ax.set_ylabel("Band gap (eV)")
handles = [Line2D([], [], marker="o", linestyle="", color=TRUE_C, markersize=5), Line2D([], [], color=TRUE_C, linewidth=1.8),
Line2D([], [], marker="o", linestyle="", color=LABEL_C, markersize=5), Line2D([], [], color=INK2, linestyle=(0, (4, 3)))]
labels = ["Gap of the returned structure (DFT, else CGCNN on relaxed cell)", "Mean per target",
"Gap the engine shows (label)", "Perfect conditioning"]
fig.suptitle("Ablation: which part of the pipeline makes generation follow the band-gap target?", x=0.01, ha="left",
fontsize=12.5, fontweight="bold", color=INK)
fig.legend(handles, labels, loc="upper left", ncol=4, frameon=False, bbox_to_anchor=(0.005, 1 - 0.55 / fig.get_figheight()),
fontsize=8.5, labelcolor=INK)
fig.tight_layout(rect=(0, 0, 1, 1 - 1.0 / fig.get_figheight()))
fig.savefig(f"{RES}/ablation_chart.png", dpi=150, facecolor=SURFACE)
print("saved", f"{RES}/ablation_chart.png")
if __name__ == "__main__":
main()