Skip to content

t5lab.make_mvprop_report

make_mvprop_report

Build a self-contained CONNECTIVITY-FIRST HTML audit report for the greedy->MVProp swap.

Reads each run's history.json (per-iter metrics) + optional GIFs, and emits one HTML file with inline SVG training curves (connectivity_real leads; coverage second) + a greedy-vs-mvprop table + base64-embedded training/inference GIFs. No external assets — opens anywhere.

$PY -m t5lab.make_mvprop_report --out report/mvprop_report.html         --run "open 24² greedy=runs/mvprop/open24_greedy"         --run "open 24² mvprop=runs/mvprop/open24_mvprop"         --gif "open 24² mvprop=gifs/mvprop_open24_s0.gif,gifs/mvprop_open24_s3.gif"

build

build(runs, gifs, out, title='MVProp planner — connectivity-first audit', interim=False)

runs: list of (label, run_dir). gifs: dict label -> [gif paths]. interim=True frames a mid-training snapshot (recent values, no hard pass/fail).

Source code in experiments/t5lab/make_mvprop_report.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def build(runs, gifs, out, title="MVProp planner — connectivity-first audit", interim=False):
    """runs: list of (label, run_dir). gifs: dict label -> [gif paths]. interim=True frames a
    mid-training snapshot (recent values, no hard pass/fail)."""
    data = []
    for label, rd in runs:
        hp = os.path.join(rd, "history.json")
        hist = json.load(open(hp)) if os.path.exists(hp) else []
        data.append((label, rd, hist))

    mv = [(l, h) for (l, rd, h) in data if "mvprop" in l.lower() and h]
    if interim:
        best = max(mv, key=lambda x: _tail_mean(x[1], "coverage_pct"), default=(None, None))
        bl, bh = best
        note = (f'best mvprop arm so far — <b>{bl}</b>: connectivity_real '
                f'<b>{_tail_mean(bh, "connectivity_real"):.3f}</b>, coverage '
                f'<b>{_tail_mean(bh, "coverage_pct")*100:.1f}%</b> (peak {_peak(bh, "coverage_pct")*100:.1f}%).'
                if bh else 'no data yet.')
        banner = (f'<div class="banner"><b>MID-TRAINING SNAPSHOT — not converged.</b> Values are the '
                  f'mean of the last ~10 logged points (recent), not a final result; arms still climbing. '
                  f'{note} GIFs land when a run completes and checkpoints.</div>')
    else:
        worst_conn = min([_tail_mean(h, "connectivity_real") for _, h in mv], default=float("nan"))
        ok = worst_conn >= 0.90
        banner = (f'<div class="banner {"" if ok else "bad"}"><b>{"PASS" if ok else "CONNECTIVITY FAILURE"} '
                  f'— connectivity-first.</b> Worst mvprop connectivity_real (λ₂&gt;0.5) across worlds = '
                  f'<b>{worst_conn:.3f}</b> (target ≥0.90). Coverage graded only on runs whose graph holds.</div>')

    # comparison table (recent = mean of last ~10 points; peak = best seen)
    rows = ""
    for label, rd, hist in data:
        if not hist:
            rows += f'<tr><td class="l">{label}</td><td colspan="6"><small>no history yet ({rd})</small></td></tr>'
            continue
        cr = _tail_mean(hist, "connectivity_real"); cp = _tail_mean(hist, "connectivity_pct")
        cv = _tail_mean(hist, "coverage_pct"); pv = _peak(hist, "coverage_pct"); dl = _tail_mean(hist, "dual_lambda")
        crc = "ok" if cr >= 0.90 else ("warn" if cr >= 0.7 else "bad")
        rows += (f'<tr><td class="l">{label}</td>'
                 f'<td class="{crc}">{cr:.3f}</td><td>{cp:.3f}</td>'
                 f'<td>{cv*100:.1f}%</td><td>{pv*100:.1f}%</td><td>{dl:.2f}</td><td>{len(hist)}</td></tr>')
    table = (f'<table><tr><th class="l">arm</th><th>CONN_real ↑<br><small>λ₂&gt;0.5 (recent)</small></th>'
             f'<th>conn<br><small>λ₂&gt;1e-3</small></th><th>cov<br><small>recent</small></th>'
             f'<th>cov<br><small>peak</small></th><th>dual λ</th><th>log pts</th></tr>'
             f'{rows}</table>')

    # charts + gifs per arm
    charts = ""
    for label, rd, hist in data:
        if not hist:
            continue
        charts += (f'<h2>{label}</h2><div class="chart">{_svg_lines(hist)}'
                   f'<div class="legend"><b class="c">■</b> connectivity_real &nbsp; '
                   f'<b class="v">■</b> coverage &nbsp;(x = iterations)</div></div>')
        gl = gifs.get(label, [])
        if gl:
            cells = "".join(f'<div class="cell">{_b64_img(g)}<div class="cap">{os.path.basename(g)}</div></div>'
                            for g in gl)
            charts += f'<div class="grid">{cells}</div>'

    html = (f'<!doctype html><html><head><meta charset="utf-8"><title>{title}</title>'
            f'<style>{_CSS}</style></head><body><div class="wrap">'
            f'<h1>{title}</h1>'
            f'<p class="sub">Exact previous architecture (setpool central critic · lagrangian dual · '
            f'local_edge_margin · reach reward · λ̂₂ aux · collision-mask · no connectivity hard-mask), '
            f'greedy → distilled MVProp planner. One-variable swap.</p>'
            f'{banner}<h2>Summary (recent = mean of last ~10 logged points)</h2>{table}{charts}</div></body></html>')
    os.makedirs(os.path.dirname(out) or ".", exist_ok=True)
    open(out, "w").write(html)
    print(f"wrote {out}  ({len(html)//1024} KB)", flush=True)
    return out