diff --git a/scripts/make_recipe_diagram.py b/scripts/make_recipe_diagram.py new file mode 100644 index 0000000..0d71434 --- /dev/null +++ b/scripts/make_recipe_diagram.py @@ -0,0 +1,103 @@ +"""Generate a clean recipe-pipeline diagram for the README.""" +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +from matplotlib.patches import FancyBboxPatch, FancyArrowPatch + +plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11}) + +fig, ax = plt.subplots(figsize=(13, 6.4)) +ax.set_xlim(0, 13) +ax.set_ylim(0, 6.4) +ax.axis("off") + +stages = [ + {"n": "1", "title": "PROBLEM\nGENERATION", + "body": "Base model emits Python\nfunction + 3 asserts.\nKeep only those where\nthe canonical passes.", + "color": "#264653", "text": "white"}, + {"n": "2", "title": "DIVERSE\nSAMPLING", + "body": "Resample 4–8 attempts\nat T=0.7–0.8. Run\neach against the\nasserts.", + "color": "#2A9D8F", "text": "white"}, + {"n": "3", "title": "AT-EDGE\nMINING", + "body": "If some pass and\nsome fail → (broken,\nfixed) pair. Skip if\nall-pass or all-fail.", + "color": "#E9C46A", "text": "#222"}, + {"n": "4", "title": "LoRA\nTRAIN", + "body": "Rank 16–32, q/k/v/o\nprojections. 2 epochs,\nlr=1e-4. No human\ndata, no RL.", + "color": "#F4A261", "text": "white"}, + {"n": "5", "title": "EVALUATE", + "body": "HumanEval / HE+ /\nMBPP / GSM8K. Verify\nthe lift before\ndeclaring a win.", + "color": "#E76F51", "text": "white"}, +] + +box_w, box_h = 2.15, 3.55 +gap = 0.30 +total_w = len(stages) * box_w + (len(stages) - 1) * gap +x0 = (13 - total_w) / 2 +y0 = 1.55 # raise the row so loop arc has more space below + +for i, s in enumerate(stages): + x = x0 + i * (box_w + gap) + shadow = FancyBboxPatch( + (x + 0.05, y0 - 0.05), box_w, box_h, + boxstyle="round,pad=0.02,rounding_size=0.12", + linewidth=0, facecolor="#00000020") + ax.add_patch(shadow) + card = FancyBboxPatch( + (x, y0), box_w, box_h, + boxstyle="round,pad=0.02,rounding_size=0.12", + linewidth=1.2, edgecolor="#222", facecolor=s["color"]) + ax.add_patch(card) + + circ_x, circ_y = x + 0.32, y0 + box_h - 0.32 + ax.add_patch(plt.Circle((circ_x, circ_y), 0.24, + facecolor="white", edgecolor=s["color"], + linewidth=2, zorder=4)) + ax.text(circ_x, circ_y, s["n"], ha="center", va="center", + fontsize=13, fontweight="bold", color=s["color"], zorder=5) + + ax.text(x + box_w/2, y0 + box_h - 0.85, s["title"], + ha="center", va="center", fontsize=12.5, fontweight="bold", + color=s["text"]) + ax.text(x + box_w/2, y0 + 1.15, s["body"], + ha="center", va="center", fontsize=9.5, color=s["text"], + linespacing=1.35) + + if i < len(stages) - 1: + ax_x = x + box_w + 0.02 + ax_xe = x + box_w + gap - 0.02 + ay = y0 + box_h / 2 + ax.add_patch(FancyArrowPatch( + (ax_x, ay), (ax_xe, ay), + arrowstyle="-|>", mutation_scale=18, + color="#333", linewidth=2.0)) + +# Title +ax.text(6.5, 6.05, "The TinyForge-Zero recipe", + ha="center", va="center", fontsize=17, fontweight="bold", color="#1a1a1a") +ax.text(6.5, 5.55, + "No human-written training data — the base model bootstraps from its own divergent attempts.", + ha="center", va="center", fontsize=10.5, color="#555", style="italic") + +# Loop-back arrow well below the cards +sx = x0 + 4 * (box_w + gap) + box_w / 2 +ex = x0 + 2 * (box_w + gap) + box_w / 2 +ax.annotate("", + xy=(ex, y0 - 0.05), xytext=(sx, y0 - 0.05), + arrowprops=dict(arrowstyle="-|>", + connectionstyle="arc3,rad=0.30", + color="#999", linewidth=1.3, + linestyle=(0, (4, 3)))) +ax.text((sx + ex) / 2, y0 - 0.95, "iterate: mine more pairs, retrain", + ha="center", va="center", fontsize=9.0, color="#888", style="italic") + +# Footer +ax.text(6.5, 0.30, + "Control: replacing self-mined pairs with mechanically-corrupted external pairs yields +0 — the signal is in the content, not the format.", + ha="center", va="center", fontsize=9.0, color="#666", style="italic") + +plt.tight_layout() +plt.savefig("/Users/usman/tinyforge-zero/docs/recipe_diagram.png", + dpi=200, bbox_inches="tight", facecolor="white") +plt.savefig("/Users/usman/tinyforge-zero/docs/recipe_diagram.pdf", + bbox_inches="tight", facecolor="white") +print("Diagram regenerated.")