Spaces:
Running on Zero
Running on Zero
Diagram: ViT label, larger in-box type, AUC/AAC anchored in their regions
Browse files
app.py
CHANGED
|
@@ -92,7 +92,7 @@ def _schematic_curve():
|
|
| 92 |
"""A reentrant titration curve used only for the diagram's thumbnails."""
|
| 93 |
x = pipeline.intensity_ladder(pipeline.NUM_IMAGES).numpy()
|
| 94 |
lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
|
| 95 |
-
p = np.clip(1.55 * np.exp(-((lx - 0.
|
| 96 |
calls = (np.random.default_rng(0).random(p.size) < p).astype(int)
|
| 97 |
curve = metrics.moving_average(calls, pipeline.NUM_IMAGES // pipeline.WINDOW_DIVISOR)
|
| 98 |
return x, curve
|
|
@@ -111,8 +111,8 @@ def plot_explainer():
|
|
| 111 |
stage = fig.add_axes([0, 0, 1, 1]); stage.set_axis_off()
|
| 112 |
stage.set_xlim(0, 1); stage.set_ylim(0, 1)
|
| 113 |
|
| 114 |
-
xs = [0.
|
| 115 |
-
box_y, box_h, box_w = 0.30, 0.50, 0.
|
| 116 |
|
| 117 |
def node(cx, title, caption):
|
| 118 |
stage.add_patch(FancyBboxPatch(
|
|
@@ -120,17 +120,17 @@ def plot_explainer():
|
|
| 120 |
boxstyle="round,pad=0.012,rounding_size=0.02",
|
| 121 |
linewidth=1.1, edgecolor=MUTED, facecolor="white", zorder=2))
|
| 122 |
stage.text(cx, box_y + box_h + 0.06, title, ha="center", va="bottom",
|
| 123 |
-
fontsize=
|
| 124 |
if caption:
|
| 125 |
stage.text(cx, box_y - 0.05, caption, ha="center", va="top",
|
| 126 |
-
fontsize=
|
| 127 |
|
| 128 |
def arrow(x0, x1, label):
|
| 129 |
stage.annotate("", xy=(x1, box_y + box_h / 2), xytext=(x0, box_y + box_h / 2),
|
| 130 |
arrowprops=dict(arrowstyle="-|>", lw=1.6, color=INK,
|
| 131 |
shrinkA=0, shrinkB=0), zorder=3)
|
| 132 |
stage.text((x0 + x1) / 2, box_y + box_h / 2 + 0.045, label, ha="center",
|
| 133 |
-
va="bottom", fontsize=
|
| 134 |
linespacing=1.35, zorder=4)
|
| 135 |
|
| 136 |
node(xs[0], "IDP sequence\n+ expression level", f"{pipeline.SEQUENCE_LENGTH} aa, scanned over\n{pipeline.NUM_IMAGES} concentrations")
|
|
@@ -139,14 +139,14 @@ def plot_explainer():
|
|
| 139 |
node(xs[3], "AUC, AAC", None)
|
| 140 |
|
| 141 |
arrow(xs[0] + box_w / 2, xs[1] - box_w / 2, "CELL-FM")
|
| 142 |
-
arrow(xs[1] + box_w / 2, xs[2] - box_w / 2, "ViT
|
| 143 |
arrow(xs[2] + box_w / 2, xs[3] - box_w / 2, "integrate")
|
| 144 |
|
| 145 |
# the last node's caption replaces the bullets that used to sit under the title
|
| 146 |
stage.text(xs[3], box_y - 0.05, "AUC — range spent condensed", ha="center", va="top",
|
| 147 |
-
fontsize=
|
| 148 |
stage.text(xs[3], box_y - 0.16, "AAC — condensation lost to\nreentrant dissolution",
|
| 149 |
-
ha="center", va="top", fontsize=
|
| 150 |
linespacing=1.5, zorder=4)
|
| 151 |
|
| 152 |
def panel(cx, pad_x=0.020, pad_y=0.055):
|
|
@@ -164,11 +164,11 @@ def plot_explainer():
|
|
| 164 |
|
| 165 |
# 1. inputs: the sequence, and the concentration ladder it is scanned over
|
| 166 |
a = panel(xs[0]); a.set_axis_off(); a.set_xlim(0, 1); a.set_ylim(0, 1)
|
| 167 |
-
a.text(0.5, 0.80, "KSFGTPFGGG…", ha="center", va="center", fontsize=
|
| 168 |
family="monospace", color=INK)
|
| 169 |
a.imshow(np.linspace(0, 1, 128)[None, :], cmap="magma",
|
| 170 |
extent=(0.06, 0.94, 0.30, 0.50), aspect="auto")
|
| 171 |
-
a.text(0.5, 0.13, "low → high", ha="center", va="center", fontsize=
|
| 172 |
|
| 173 |
# 2. a ladder of images, not one -- three rungs stand in for all 512.
|
| 174 |
# Size the axes square in inches; imshow would otherwise letterbox each frame
|
|
@@ -183,7 +183,7 @@ def plot_explainer():
|
|
| 183 |
ax.imshow(_synthetic_cell(c, seed=k + 1), cmap="magma", vmin=0, vmax=1)
|
| 184 |
frame(ax, boxed=False)
|
| 185 |
stage.text(xs[1], y0 - 0.035, "low → high concentration", ha="center", va="top",
|
| 186 |
-
fontsize=
|
| 187 |
|
| 188 |
# 3. the curve those calls become
|
| 189 |
c_ax = panel(xs[2])
|
|
@@ -199,10 +199,19 @@ def plot_explainer():
|
|
| 199 |
d.plot(x, flat, color=GAP, linewidth=1.0, linestyle="--", alpha=0.85)
|
| 200 |
d.set_xscale("log"); d.set_xlim(x.min(), x.max()); d.set_ylim(-0.05, 1.08)
|
| 201 |
d.set_facecolor("white"); frame(d)
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
return fig
|
| 208 |
|
|
|
|
| 92 |
"""A reentrant titration curve used only for the diagram's thumbnails."""
|
| 93 |
x = pipeline.intensity_ladder(pipeline.NUM_IMAGES).numpy()
|
| 94 |
lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
|
| 95 |
+
p = np.clip(1.55 * np.exp(-((lx - 0.42) ** 2) / (2 * 0.17 ** 2)) - 0.16, 0, 1)
|
| 96 |
calls = (np.random.default_rng(0).random(p.size) < p).astype(int)
|
| 97 |
curve = metrics.moving_average(calls, pipeline.NUM_IMAGES // pipeline.WINDOW_DIVISOR)
|
| 98 |
return x, curve
|
|
|
|
| 111 |
stage = fig.add_axes([0, 0, 1, 1]); stage.set_axis_off()
|
| 112 |
stage.set_xlim(0, 1); stage.set_ylim(0, 1)
|
| 113 |
|
| 114 |
+
xs = [0.112, 0.371, 0.629, 0.888]
|
| 115 |
+
box_y, box_h, box_w = 0.30, 0.50, 0.196
|
| 116 |
|
| 117 |
def node(cx, title, caption):
|
| 118 |
stage.add_patch(FancyBboxPatch(
|
|
|
|
| 120 |
boxstyle="round,pad=0.012,rounding_size=0.02",
|
| 121 |
linewidth=1.1, edgecolor=MUTED, facecolor="white", zorder=2))
|
| 122 |
stage.text(cx, box_y + box_h + 0.06, title, ha="center", va="bottom",
|
| 123 |
+
fontsize=12, color=INK, fontweight="bold", zorder=4)
|
| 124 |
if caption:
|
| 125 |
stage.text(cx, box_y - 0.05, caption, ha="center", va="top",
|
| 126 |
+
fontsize=9.2, color=MUTED, linespacing=1.5, zorder=4)
|
| 127 |
|
| 128 |
def arrow(x0, x1, label):
|
| 129 |
stage.annotate("", xy=(x1, box_y + box_h / 2), xytext=(x0, box_y + box_h / 2),
|
| 130 |
arrowprops=dict(arrowstyle="-|>", lw=1.6, color=INK,
|
| 131 |
shrinkA=0, shrinkB=0), zorder=3)
|
| 132 |
stage.text((x0 + x1) / 2, box_y + box_h / 2 + 0.045, label, ha="center",
|
| 133 |
+
va="bottom", fontsize=9.5, color=CURVE, fontweight="bold",
|
| 134 |
linespacing=1.35, zorder=4)
|
| 135 |
|
| 136 |
node(xs[0], "IDP sequence\n+ expression level", f"{pipeline.SEQUENCE_LENGTH} aa, scanned over\n{pipeline.NUM_IMAGES} concentrations")
|
|
|
|
| 139 |
node(xs[3], "AUC, AAC", None)
|
| 140 |
|
| 141 |
arrow(xs[0] + box_w / 2, xs[1] - box_w / 2, "CELL-FM")
|
| 142 |
+
arrow(xs[1] + box_w / 2, xs[2] - box_w / 2, "ViT")
|
| 143 |
arrow(xs[2] + box_w / 2, xs[3] - box_w / 2, "integrate")
|
| 144 |
|
| 145 |
# the last node's caption replaces the bullets that used to sit under the title
|
| 146 |
stage.text(xs[3], box_y - 0.05, "AUC — range spent condensed", ha="center", va="top",
|
| 147 |
+
fontsize=9.2, color=CURVE, fontweight="bold", zorder=4)
|
| 148 |
stage.text(xs[3], box_y - 0.16, "AAC — condensation lost to\nreentrant dissolution",
|
| 149 |
+
ha="center", va="top", fontsize=9.2, color=GAP, fontweight="bold",
|
| 150 |
linespacing=1.5, zorder=4)
|
| 151 |
|
| 152 |
def panel(cx, pad_x=0.020, pad_y=0.055):
|
|
|
|
| 164 |
|
| 165 |
# 1. inputs: the sequence, and the concentration ladder it is scanned over
|
| 166 |
a = panel(xs[0]); a.set_axis_off(); a.set_xlim(0, 1); a.set_ylim(0, 1)
|
| 167 |
+
a.text(0.5, 0.80, "KSFGTPFGGG…", ha="center", va="center", fontsize=10.5,
|
| 168 |
family="monospace", color=INK)
|
| 169 |
a.imshow(np.linspace(0, 1, 128)[None, :], cmap="magma",
|
| 170 |
extent=(0.06, 0.94, 0.30, 0.50), aspect="auto")
|
| 171 |
+
a.text(0.5, 0.13, "low → high", ha="center", va="center", fontsize=9, color=MUTED)
|
| 172 |
|
| 173 |
# 2. a ladder of images, not one -- three rungs stand in for all 512.
|
| 174 |
# Size the axes square in inches; imshow would otherwise letterbox each frame
|
|
|
|
| 183 |
ax.imshow(_synthetic_cell(c, seed=k + 1), cmap="magma", vmin=0, vmax=1)
|
| 184 |
frame(ax, boxed=False)
|
| 185 |
stage.text(xs[1], y0 - 0.035, "low → high concentration", ha="center", va="top",
|
| 186 |
+
fontsize=8.6, color=MUTED, zorder=4)
|
| 187 |
|
| 188 |
# 3. the curve those calls become
|
| 189 |
c_ax = panel(xs[2])
|
|
|
|
| 199 |
d.plot(x, flat, color=GAP, linewidth=1.0, linestyle="--", alpha=0.85)
|
| 200 |
d.set_xscale("log"); d.set_xlim(x.min(), x.max()); d.set_ylim(-0.05, 1.08)
|
| 201 |
d.set_facecolor("white"); frame(d)
|
| 202 |
+
# place each label inside its own shaded region, computed from the curve
|
| 203 |
+
plateau = np.flatnonzero(curve > 0.9)
|
| 204 |
+
auc_i = plateau[len(plateau) // 2] if plateau.size else int(np.argmax(curve))
|
| 205 |
+
d.text(x[auc_i], 0.42, "AUC", fontsize=12, color=CURVE, fontweight="bold",
|
| 206 |
+
ha="center", va="center")
|
| 207 |
+
clear = np.flatnonzero((flat - curve > 0.45) & (curve < 0.35))
|
| 208 |
+
if clear.size:
|
| 209 |
+
# Sit just inside the open part of the gap, but never so far right that a
|
| 210 |
+
# centred label overruns the axes -- the midpoint of the region does.
|
| 211 |
+
lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
|
| 212 |
+
target = min(max(lx[clear[0]] + 0.09, 0.66), 0.78)
|
| 213 |
+
d.text(x[int(np.argmin(np.abs(lx - target)))], 0.55, "AAC", fontsize=12,
|
| 214 |
+
color=GAP, fontweight="bold", ha="center", va="center")
|
| 215 |
|
| 216 |
return fig
|
| 217 |
|