Spaces:
Running on Zero
Running on Zero
Download tools/measure_brain.py from rfick/brain-zero: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/rfick/brain-zero/resolve/main/tools/measure_brain.py
- Command line
-
hf download hf://spaces/rfick/brain-zero/tools/measure_brain.py
-
curl -L -o measure_brain.py https://huggingface.co/spaces/rfick/brain-zero/resolve/main/tools/measure_brain.py
10.6 kB
| """The brain source's costs, measured: the packs' pose responses (JAX on the CPU, as in the page's process and the | |
| GPU worker of a ZeroGPU Space) and every device stage of a run (the contraction, the noise, the reconstruction, the | |
| tracking and the truth's tracking at density 1, 2 and 4, the scoring, the ladder), first call and steady, the | |
| streamline counts, the truth connectome's reproducibility at one key and its spread over keys (the score's floor), | |
| and the size of the payload that crosses from the GPU worker to the page. Writes a JSON the ``[budget]`` of | |
| ``space/brain.toml`` is refit from. | |
| DISCO_RECONSTRUCTION=tournier07 python tools/measure_brain.py --asset DIR --wm-pack WM.rpk --gm-pack GM.rpk [--out brain_costs.json] | |
| With ``--saves WM1.rpk,WM2.rpk,...`` it measures only the responses' cost by the WM pack's saves spanned (the | |
| ``per_save`` of ``[budget]``'s ``response_*``): per WM pack, in a fresh interpreter (nothing compiled), ``prepare`` of | |
| one protocol (``--saves-protocol``: a manifest.json whose protocol rows are played, else the asset's own; retimed to | |
| ``--saves-class``, the scan's own class by default) cold, warm and with the field tier off, beside the saves the class spans on that pack; and the | |
| least-squares slope of the seconds over the saves per state. | |
| python tools/measure_brain.py --asset DIR --gm-pack GM.rpk --saves A.rpk,B.rpk [--saves-protocol manifest.json] [--saves-class scan] | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import pickle | |
| import sys | |
| import time | |
| from dataclasses import replace | |
| import numpy as np | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from space import pipeline as P # noqa: E402 | |
| from space.sources import brain as B # noqa: E402 | |
| def sync(): | |
| import torch | |
| if torch.cuda.is_available(): | |
| torch.cuda.synchronize() | |
| def five_shells(): | |
| """MASiVar's shape: b = 1000 / 1500 / 2000 / 2500 / 3000 x 96 directions and 15 b = 0, on the scan's timing.""" | |
| return P.Protocol(tuple(P.Shell(B.SCAN, b, 96) for b in (1000, 1500, 2000, 2500, 3000)), n_b0=15, name="five shells x 96") | |
| def saves_one(asset, wm_pack, gm_pack, protocol_manifest, shape): | |
| """``prepare``'s seconds cold (the first call), warm (the same band at another tissue) and with the field tier off | |
| (each the faster of two calls), on one WM pack in this process, with the saves the protocol's class spans on it.""" | |
| cfg = P.config(os.path.join(P.HERE, "brain.toml")) | |
| cfg["asset"] = {"local": asset} | |
| cfg["packs"] = {"wm": [{"label": "wm", "uri": wm_pack}], "gm": [{"label": "gm", "uri": gm_pack}]} | |
| src = B.Brain(cfg) | |
| panel = B.Brain.panel(cfg) | |
| v = {c.name: c.value for row in panel.rows for c in row if c.name in panel.fields} | |
| v.update(wm_pack="wm", gm_pack="gm") | |
| ph = B.Brain.physics_from(cfg, v) | |
| if protocol_manifest: | |
| with open(protocol_manifest) as f: | |
| prot = B.scan_protocol(json.load(f), name="the manifest's protocol") | |
| else: | |
| prot = B.Brain.protocol(cfg, B.Brain.presets(cfg)[0]) | |
| prot = P.retime(prot, shape) | |
| meas = src.validate(prot, ph) | |
| seg = src.segments("wm", "wm") | |
| saves = B.Brain.saves_spanned(seg, src.windows_needed("wm", "wm", meas)) | |
| seconds = {} | |
| other = lambda f: replace(ph, T2={**ph.T2, "gm": ph.T2["gm"] * f}) # the same band at another tissue | |
| for state, physics in (("cold", [ph]), ("warm", [other(1.0001), other(1.0002)]), ("no_field", [replace(ph, field=False), replace(other(1.0001), field=False)])): | |
| times = [] | |
| for phys in physics: | |
| t = time.perf_counter(); src.prepare(meas, phys); times.append(time.perf_counter() - t) | |
| seconds[state] = min(times) | |
| pk = src.packs[("wm", "wm")] | |
| return dict(pack=wm_pack, id=pk.meta.get("id"), n_walkers=int(pk.n_walkers), K=int(pk.K), segments=seg, saves=saves, | |
| n_meas=int(len(meas.bvals)), shape=shape, seconds=seconds) | |
| def measure_saves(a): | |
| """:func:`saves_one` per WM pack of ``--saves``, each in a fresh interpreter, and the slope of the seconds over the | |
| saves per state (least squares with an intercept).""" | |
| import multiprocessing as mp | |
| rows = [] | |
| for pack in a.saves.split(","): | |
| with mp.get_context("spawn").Pool(1) as pool: | |
| row = pool.apply(saves_one, (a.asset, pack, a.gm_pack, a.saves_protocol, a.saves_class)) | |
| rows.append(row) | |
| print(json.dumps(row), flush=True) | |
| x = np.array([r["saves"] for r in rows], float) | |
| fit = {} | |
| for state in ("cold", "warm", "no_field"): | |
| y = np.array([r["seconds"][state] for r in rows]) | |
| slope, icpt = np.polyfit(x, y, 1) if len(rows) > 1 else (float("nan"), float("nan")) | |
| fit[state] = dict(per_save=float(slope), intercept=float(icpt)) | |
| out = dict(cpu_threads=os.environ.get("OMP_NUM_THREADS"), rows=rows, fit=fit) | |
| print(json.dumps(fit), flush=True) | |
| with open(a.out, "w") as f: | |
| json.dump(out, f, indent=1, default=float) | |
| print("wrote", a.out) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--asset", required=True) | |
| ap.add_argument("--wm-pack") | |
| ap.add_argument("--gm-pack", required=True) | |
| ap.add_argument("--out", default="brain_costs.json") | |
| ap.add_argument("--densities", default="1,2,4") | |
| ap.add_argument("--saves", default=None, help="WM packs, comma-separated: measure the responses' cost by the saves spanned only") | |
| ap.add_argument("--saves-protocol", default=None, help="a manifest.json whose protocol the saves measurement plays") | |
| ap.add_argument("--saves-class", default=B.SCAN, help="the timing class the saves measurement plays it on") | |
| a = ap.parse_args() | |
| if a.saves: | |
| return measure_saves(a) | |
| if not a.wm_pack: | |
| ap.error("--wm-pack is required (or --saves)") | |
| cfg = P.config(os.path.join(P.HERE, "brain.toml")) | |
| cfg["asset"] = {"local": a.asset} | |
| cfg["packs"] = {"wm": [{"label": "wm", "uri": a.wm_pack}], "gm": [{"label": "gm", "uri": a.gm_pack}]} | |
| import torch | |
| out = dict(device=torch.cuda.get_device_name(0) if torch.cuda.is_available() else "cpu", reconstruction=P.reconstruction(cfg), backend=P.backend(cfg)) | |
| t0 = time.perf_counter(); src = B.Brain(cfg); out["load_seconds"] = time.perf_counter() - t0 | |
| out["voxels"] = int(src.mask.sum()); out["wm_voxels"] = int(src.asset.stop.sum()); out["grid"] = list(src.mask.shape) | |
| panel = B.Brain.panel(cfg) | |
| v = {c.name: c.value for row in panel.rows for c in row if c.name in panel.fields} | |
| v.update(wm_pack="wm", gm_pack="gm") | |
| ph = B.Brain.physics_from(cfg, v) | |
| protocols = {"scan": B.Brain.protocol(cfg, B.Brain.presets(cfg)[0]), "five shells x 96": five_shells()} | |
| host = {} | |
| for name, prot in protocols.items(): | |
| meas = src.validate(prot, ph) | |
| rows = {} | |
| for label, phys in [("every tier (cold)", ph), ("every tier (warm)", replace(ph, T2={**ph.T2, "gm": ph.T2["gm"] * 1.0001}))] + \ | |
| [(f"rung: {l}", r) for l, r in src.ladder_steps(ph)]: | |
| t = time.perf_counter(); src.prepare(meas, phys); rows[label] = time.perf_counter() - t | |
| host[name] = dict(n_meas=prot.n_meas, seconds=rows) | |
| print(name, json.dumps(host[name]), flush=True) | |
| out["host_prepare"] = host | |
| device = {} | |
| for name, prot in protocols.items(): | |
| meas = src.validate(prot, ph) | |
| k = src.cached(meas, ph) | |
| rec = dict(n_meas=prot.n_meas) | |
| for rep in ("first", "steady"): | |
| sync(); t = time.perf_counter(); dwi, floor, fac, _ = src.replay(meas, ph, k); sync(); rec[f"replay_{rep}"] = time.perf_counter() - t | |
| t = time.perf_counter(); noisy = P.add_noise(dwi, 30.0, fac, seed=0, backend="torch"); rec["noise"] = time.perf_counter() - t | |
| signal = np.isfinite(dwi[..., 0]) | |
| for rep in ("first", "steady"): | |
| sync(); t = time.perf_counter(); sh, secs, extras = src.reconstruct(noisy, fac, meas, signal); sync(); rec[f"reconstruct_{rep}"] = time.perf_counter() - t | |
| rec["reconstruct_rows"] = secs | |
| rec["roundtrip"] = extras["roundtrip"] | |
| ladder = [src.cached(meas, r) for _, r in src.ladder_steps(ph)] | |
| sync(); t = time.perf_counter(); src.ladder(meas, ph, ladder); sync(); rec["ladder"] = time.perf_counter() - t | |
| t = time.perf_counter(); src.ingredients(meas, ph, k); rec["ingredients"] = time.perf_counter() - t | |
| tracks = {} | |
| for d in [int(x) for x in a.densities.split(",")]: | |
| tr = B.Brain.tracking(cfg, density=d, max_angle=cfg["tracking"]["max_angle"], step=cfg["tracking"]["step_mm"], key=0) | |
| fld, seeds = src.tracking_inputs(sh, d) | |
| row = dict(seeds=int(len(seeds))) | |
| for rep in ("first", "steady"): | |
| sync(); t = time.perf_counter(); tg, _ = P.track(fld, seeds, tr, src.backend); sync(); row[f"track_{rep}"] = time.perf_counter() - t | |
| row["streamlines"] = len(tg); row["points"] = int(tg.n_points.sum()) | |
| src._reference.clear() | |
| sync(); t = time.perf_counter(); ref, _ = src.reference(tr, seeds); sync(); row["truth"] = time.perf_counter() - t | |
| t = time.perf_counter(); M = src.regions.matrix(tg); s = src.score(M, ref); row["score_seconds"] = time.perf_counter() - t | |
| row["score"] = s; row["truth_streamlines"] = ref["streamlines"] | |
| src._reference.clear(); again, _ = src.reference(tr, seeds) | |
| row["truth_bit_for_bit"] = bool(np.array_equal(again["matrix"], ref["matrix"])) | |
| src._reference.clear(); other, _ = src.reference(replace(tr, key=1), seeds) | |
| pairs = src.regions.pairs | |
| row["truth_key0_vs_key1_pearson_log"] = P.pearson(np.log1p(ref["matrix"][pairs]), np.log1p(other["matrix"][pairs])) | |
| src._reference.clear() | |
| tracks[d] = row | |
| print(name, d, json.dumps({k_: v_ for k_, v_ in row.items() if k_ != "score"}), json.dumps(s), flush=True) | |
| rec["tracking"] = tracks | |
| res = P.Result(prot, meas, noisy.astype(np.float32), floor, fac, sh, tg, seeds, M, s, clean=dwi.astype(np.float32), extras=extras) | |
| t = time.perf_counter(); blob = pickle.dumps(res, protocol=pickle.HIGHEST_PROTOCOL); rec["payload_mb"] = len(blob) / 1e6; rec["pickle_seconds"] = time.perf_counter() - t | |
| device[name] = rec | |
| print(name, json.dumps({k_: v_ for k_, v_ in rec.items() if k_ not in ("tracking", "roundtrip")}), flush=True) | |
| out["device"] = device | |
| with open(a.out, "w") as f: | |
| json.dump(out, f, indent=1, default=float) | |
| print("wrote", a.out) | |
| if __name__ == "__main__": | |
| main() | |