coredipper commited on
Commit
0ccae65
Β·
verified Β·
1 Parent(s): ca653bc

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. README.md +21 -5
  2. __pycache__/app.cpython-311.pyc +0 -0
  3. app.py +494 -0
  4. requirements.txt +2 -0
README.md CHANGED
@@ -1,12 +1,28 @@
1
  ---
2
  title: Operon Diffusion
3
- emoji: πŸ”₯
4
- colorFrom: pink
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 6.6.0
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Operon Diffusion
3
+ emoji: 🌊
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: "6.5.1"
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ short_description: Morphogen gradient formation on graph topologies
12
  ---
13
 
14
+ # 🌊 Diffusion β€” Morphogen Gradient Visualizer
15
+
16
+ Simulate **morphogen diffusion** across graph topologies and watch concentration gradients form β€” the spatial coordination layer of Operon.
17
+
18
+ ## Features
19
+
20
+ - **Tab 1 β€” Linear Chain**: Emit morphogen from a chosen node in a linear graph, run N diffusion steps, and visualize concentration bars per node
21
+ - **Tab 2 β€” Topologies**: Choose from Linear, Star, Ring, Grid (2Γ—3), or Binary Tree graphs and see how topology shapes the gradient
22
+ - **Tab 3 β€” Competing Sources**: Place two different morphogens at different nodes and observe overlapping gradients
23
+
24
+ ## How It Works
25
+
26
+ `DiffusionField` manages a graph of nodes connected by edges. `MorphogenSource` objects emit at fixed rates. Each step: (1) emit, (2) diffuse β€” a fraction flows to neighbors, (3) decay β€” concentrations degrade, (4) clamp β€” cap at 1.0, snap near-zero to 0. `get_local_gradient()` bridges each node's concentrations to the `MorphogenGradient` API for agent-level strategy hints.
27
+
28
+ [GitHub](https://github.com/coredipper/operon) | [PyPI](https://pypi.org/project/operon-ai/)
__pycache__/app.cpython-311.pyc ADDED
Binary file (30.5 kB). View file
 
app.py ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Operon Diffusion β€” Morphogen Gradient Visualizer (Gradio Demo)
3
+ ==============================================================
4
+
5
+ Three-tab demo:
6
+ 1. Linear Chain β€” gradient formation on a line graph
7
+ 2. Topologies β€” preset graph shapes (star, ring, grid, tree)
8
+ 3. Competing Sources β€” two morphogens diffusing simultaneously
9
+
10
+ Run locally:
11
+ pip install gradio
12
+ python space-diffusion/app.py
13
+ """
14
+
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ import gradio as gr
19
+
20
+ # Allow importing operon_ai from the repo root when running locally
21
+ _repo_root = Path(__file__).resolve().parent.parent
22
+ if str(_repo_root) not in sys.path:
23
+ sys.path.insert(0, str(_repo_root))
24
+
25
+ from operon_ai import (
26
+ MorphogenType,
27
+ MorphogenSource,
28
+ DiffusionParams,
29
+ DiffusionField,
30
+ )
31
+
32
+ # ── Topology builders ────────────────────────────────────────────────────
33
+
34
+ NODE_COLORS = [
35
+ "#6366f1", "#3b82f6", "#22c55e", "#eab308",
36
+ "#f97316", "#ef4444", "#ec4899", "#8b5cf6",
37
+ ]
38
+
39
+
40
+ def _build_linear(n: int) -> dict[str, list[str]]:
41
+ """A β†’ B β†’ C β†’ ... linear chain."""
42
+ nodes = [chr(65 + i) for i in range(n)]
43
+ adj: dict[str, list[str]] = {nd: [] for nd in nodes}
44
+ for i in range(n - 1):
45
+ adj[nodes[i]].append(nodes[i + 1])
46
+ adj[nodes[i + 1]].append(nodes[i])
47
+ return adj
48
+
49
+
50
+ def _build_star(n: int) -> dict[str, list[str]]:
51
+ """Hub 'A' connected to B, C, D, ..."""
52
+ nodes = [chr(65 + i) for i in range(n)]
53
+ adj: dict[str, list[str]] = {nd: [] for nd in nodes}
54
+ hub = nodes[0]
55
+ for spoke in nodes[1:]:
56
+ adj[hub].append(spoke)
57
+ adj[spoke].append(hub)
58
+ return adj
59
+
60
+
61
+ def _build_ring(n: int) -> dict[str, list[str]]:
62
+ """A β†’ B β†’ C β†’ ... β†’ A cycle."""
63
+ nodes = [chr(65 + i) for i in range(n)]
64
+ adj: dict[str, list[str]] = {nd: [] for nd in nodes}
65
+ for i in range(n):
66
+ nxt = (i + 1) % n
67
+ adj[nodes[i]].append(nodes[nxt])
68
+ adj[nodes[nxt]].append(nodes[i])
69
+ # Deduplicate
70
+ for nd in adj:
71
+ adj[nd] = list(dict.fromkeys(adj[nd]))
72
+ return adj
73
+
74
+
75
+ def _build_grid() -> dict[str, list[str]]:
76
+ """2x3 grid: A-B-C / D-E-F."""
77
+ adj: dict[str, list[str]] = {
78
+ "A": ["B", "D"], "B": ["A", "C", "E"], "C": ["B", "F"],
79
+ "D": ["A", "E"], "E": ["B", "D", "F"], "F": ["C", "E"],
80
+ }
81
+ return adj
82
+
83
+
84
+ def _build_binary_tree() -> dict[str, list[str]]:
85
+ """A root, B/C children, D/E under B, F/G under C."""
86
+ adj: dict[str, list[str]] = {
87
+ "A": ["B", "C"],
88
+ "B": ["A", "D", "E"],
89
+ "C": ["A", "F", "G"],
90
+ "D": ["B"], "E": ["B"],
91
+ "F": ["C"], "G": ["C"],
92
+ }
93
+ return adj
94
+
95
+
96
+ TOPOLOGIES = {
97
+ "Linear": lambda: _build_linear(5),
98
+ "Star": lambda: _build_star(6),
99
+ "Ring": lambda: _build_ring(5),
100
+ "Grid (2x3)": _build_grid,
101
+ "Binary Tree": _build_binary_tree,
102
+ }
103
+
104
+ # ── Visualization helpers ────────────────────────────────────────────────
105
+
106
+
107
+ def _concentration_bars(snapshot: dict[str, dict[str, float]], morphogen_filter: str | None = None) -> str:
108
+ """Render concentration bars per node."""
109
+ if not snapshot:
110
+ return "<p style='color:#888'>No data yet.</p>"
111
+
112
+ rows = []
113
+ for i, (node_id, concs) in enumerate(sorted(snapshot.items())):
114
+ color = NODE_COLORS[i % len(NODE_COLORS)]
115
+ node_rows = []
116
+ if morphogen_filter:
117
+ items = [(morphogen_filter, concs.get(morphogen_filter, 0.0))]
118
+ else:
119
+ items = sorted(concs.items()) if concs else [("(none)", 0.0)]
120
+
121
+ for mtype, val in items:
122
+ pct = max(0, min(100, val * 100))
123
+ node_rows.append(
124
+ f'<div style="display:flex;align-items:center;gap:6px;margin:2px 0">'
125
+ f'<span style="width:80px;font-size:0.8em;color:#888">{mtype}</span>'
126
+ f'<div style="flex:1;background:#e5e7eb;border-radius:4px;height:16px">'
127
+ f'<div style="width:{pct:.1f}%;background:{color};height:100%;'
128
+ f'border-radius:4px;transition:width 0.3s"></div></div>'
129
+ f'<span style="width:50px;text-align:right;font-size:0.8em">{val:.4f}</span>'
130
+ f'</div>'
131
+ )
132
+
133
+ rows.append(
134
+ f'<div style="margin:6px 0;padding:6px;border-left:3px solid {color}">'
135
+ f'<strong style="font-size:0.9em">Node {node_id}</strong>'
136
+ + "".join(node_rows)
137
+ + "</div>"
138
+ )
139
+
140
+ return '<div style="padding:4px">' + "".join(rows) + "</div>"
141
+
142
+
143
+ def _snapshot_table(snapshot: dict[str, dict[str, float]]) -> str:
144
+ """Render snapshot as a markdown table."""
145
+ if not snapshot:
146
+ return "No data."
147
+
148
+ # Collect all morphogen types
149
+ all_types = sorted({mt for concs in snapshot.values() for mt in concs})
150
+ if not all_types:
151
+ all_types = ["(empty)"]
152
+
153
+ header = "| Node | " + " | ".join(all_types) + " |"
154
+ sep = "| :--- | " + " | ".join("---:" for _ in all_types) + " |"
155
+ rows = [header, sep]
156
+ for node_id in sorted(snapshot.keys()):
157
+ concs = snapshot[node_id]
158
+ vals = " | ".join(f"{concs.get(mt, 0.0):.4f}" for mt in all_types)
159
+ rows.append(f"| {node_id} | {vals} |")
160
+
161
+ return "\n".join(rows)
162
+
163
+
164
+ def _gradient_level_table(field: DiffusionField, nodes: list[str]) -> str:
165
+ """Show MorphogenGradient level per node via get_local_gradient()."""
166
+ rows = ["| Node | Morphogen | Concentration | Level |", "| :--- | :--- | ---: | :--- |"]
167
+ for nd in sorted(nodes):
168
+ gradient = field.get_local_gradient(nd)
169
+ for mt in MorphogenType:
170
+ val = gradient.get(mt)
171
+ if val > 0:
172
+ level = gradient.get_level(mt)
173
+ rows.append(f"| {nd} | {mt.value} | {val:.4f} | {level} |")
174
+ if len(rows) == 2:
175
+ return "No non-zero concentrations to display."
176
+ return "\n".join(rows)
177
+
178
+
179
+ # ── Tab 1: Linear Chain ─────────────────────────────────────────────────
180
+
181
+
182
+ def run_linear_chain(
183
+ num_nodes: int,
184
+ source_pos: str,
185
+ emission_rate: float,
186
+ diffusion_rate: float,
187
+ decay_rate: float,
188
+ num_steps: int,
189
+ ) -> tuple[str, str]:
190
+ """Run diffusion on a linear chain.
191
+
192
+ Returns (bars_html, step_snapshots_md).
193
+ """
194
+ n = int(num_nodes)
195
+ nodes = [chr(65 + i) for i in range(n)]
196
+ adj = _build_linear(n)
197
+
198
+ params = DiffusionParams(
199
+ diffusion_rate=float(diffusion_rate),
200
+ decay_rate=float(decay_rate),
201
+ )
202
+ field = DiffusionField.from_adjacency(adj, params=params)
203
+
204
+ source_node = source_pos if source_pos in nodes else nodes[0]
205
+ field.add_source(MorphogenSource(
206
+ node_id=source_node,
207
+ morphogen_type=MorphogenType.COMPLEXITY,
208
+ emission_rate=float(emission_rate),
209
+ ))
210
+
211
+ steps = int(num_steps)
212
+ step_rows = ["| Step | " + " | ".join(nodes) + " |"]
213
+ step_rows.append("| ---: | " + " | ".join("---:" for _ in nodes) + " |")
214
+
215
+ for s in range(1, steps + 1):
216
+ field.step()
217
+ snap = field.snapshot()
218
+ vals = " | ".join(
219
+ f"{snap.get(nd, {}).get('complexity', 0.0):.4f}" for nd in nodes
220
+ )
221
+ step_rows.append(f"| {s} | {vals} |")
222
+
223
+ bars_html = _concentration_bars(field.snapshot(), morphogen_filter="complexity")
224
+ steps_md = "\n".join(step_rows)
225
+
226
+ return bars_html, steps_md
227
+
228
+
229
+ # ── Tab 2: Topologies ───────────────────────────────────────────────────
230
+
231
+
232
+ def run_topology(
233
+ topo_name: str,
234
+ source_node: str,
235
+ emission_rate: float,
236
+ diffusion_rate: float,
237
+ decay_rate: float,
238
+ num_steps: int,
239
+ ) -> tuple[str, str, str]:
240
+ """Run diffusion on a preset topology.
241
+
242
+ Returns (bars_html, snapshot_md, gradient_md).
243
+ """
244
+ builder = TOPOLOGIES.get(topo_name, TOPOLOGIES["Linear"])
245
+ adj = builder()
246
+
247
+ params = DiffusionParams(
248
+ diffusion_rate=float(diffusion_rate),
249
+ decay_rate=float(decay_rate),
250
+ )
251
+ field = DiffusionField.from_adjacency(adj, params=params)
252
+
253
+ nodes = sorted(adj.keys())
254
+ src = source_node if source_node in nodes else nodes[0]
255
+ field.add_source(MorphogenSource(
256
+ node_id=src,
257
+ morphogen_type=MorphogenType.COMPLEXITY,
258
+ emission_rate=float(emission_rate),
259
+ ))
260
+
261
+ field.run(int(num_steps))
262
+
263
+ bars_html = _concentration_bars(field.snapshot(), morphogen_filter="complexity")
264
+ snap_md = "### Concentration Snapshot\n\n" + _snapshot_table(field.snapshot())
265
+ grad_md = "### Local Gradient Levels\n\n" + _gradient_level_table(field, nodes)
266
+
267
+ return bars_html, snap_md, grad_md
268
+
269
+
270
+ def _get_topology_nodes(topo_name: str) -> dict:
271
+ """Return dropdown update with nodes for the selected topology."""
272
+ builder = TOPOLOGIES.get(topo_name, TOPOLOGIES["Linear"])
273
+ adj = builder()
274
+ nodes = sorted(adj.keys())
275
+ return gr.update(choices=nodes, value=nodes[0])
276
+
277
+
278
+ # ── Tab 3: Competing Sources ────────────────────────────────────────────
279
+
280
+
281
+ def run_competing(
282
+ num_nodes: int,
283
+ src1_pos: str,
284
+ src1_type: str,
285
+ src1_rate: float,
286
+ src2_pos: str,
287
+ src2_type: str,
288
+ src2_rate: float,
289
+ diffusion_rate: float,
290
+ decay_rate: float,
291
+ num_steps: int,
292
+ ) -> tuple[str, str]:
293
+ """Two morphogens competing on a linear chain.
294
+
295
+ Returns (bars_html, table_md).
296
+ """
297
+ n = int(num_nodes)
298
+ nodes = [chr(65 + i) for i in range(n)]
299
+ adj = _build_linear(n)
300
+
301
+ params = DiffusionParams(
302
+ diffusion_rate=float(diffusion_rate),
303
+ decay_rate=float(decay_rate),
304
+ )
305
+ field = DiffusionField.from_adjacency(adj, params=params)
306
+
307
+ mt_map = {mt.value: mt for mt in MorphogenType}
308
+ mt1 = mt_map.get(src1_type, MorphogenType.COMPLEXITY)
309
+ mt2 = mt_map.get(src2_type, MorphogenType.CONFIDENCE)
310
+
311
+ s1 = src1_pos if src1_pos in nodes else nodes[0]
312
+ s2 = src2_pos if src2_pos in nodes else nodes[-1]
313
+
314
+ field.add_source(MorphogenSource(node_id=s1, morphogen_type=mt1, emission_rate=float(src1_rate)))
315
+ field.add_source(MorphogenSource(node_id=s2, morphogen_type=mt2, emission_rate=float(src2_rate)))
316
+
317
+ field.run(int(num_steps))
318
+
319
+ bars_html = _concentration_bars(field.snapshot())
320
+
321
+ # Build per-morphogen table
322
+ snap = field.snapshot()
323
+ types_present = sorted({mt for concs in snap.values() for mt in concs})
324
+ header = "| Node | " + " | ".join(types_present) + " |"
325
+ sep = "| :--- | " + " | ".join("---:" for _ in types_present) + " |"
326
+ rows = [header, sep]
327
+ for nd in nodes:
328
+ concs = snap.get(nd, {})
329
+ vals = " | ".join(f"{concs.get(t, 0.0):.4f}" for t in types_present)
330
+ rows.append(f"| {nd} | {vals} |")
331
+
332
+ table_md = "### Concentration per Morphogen\n\n" + "\n".join(rows)
333
+ return bars_html, table_md
334
+
335
+
336
+ # ── Gradio UI ────────────────────────────────────────────────────────────
337
+
338
+
339
+ def build_app() -> gr.Blocks:
340
+ with gr.Blocks(title="Diffusion Visualizer") as app:
341
+ gr.Markdown(
342
+ "# 🌊 Diffusion β€” Morphogen Gradient Visualizer\n"
343
+ "Simulate **morphogen diffusion** on graph topologies and watch "
344
+ "concentration gradients form step by step."
345
+ )
346
+
347
+ with gr.Tabs():
348
+ # ── Tab 1: Linear Chain ──────────────────────────────────
349
+ with gr.TabItem("Linear Chain"):
350
+ gr.Markdown(
351
+ "Emit morphogen from one node in a **linear chain** and "
352
+ "watch the gradient decay with distance."
353
+ )
354
+
355
+ with gr.Row():
356
+ lc_nodes = gr.Slider(3, 8, value=5, step=1, label="Number of Nodes")
357
+ lc_source = gr.Dropdown(
358
+ choices=[chr(65 + i) for i in range(8)],
359
+ value="A",
360
+ label="Source Node",
361
+ )
362
+
363
+ with gr.Row():
364
+ lc_emission = gr.Slider(0.05, 1.0, value=0.5, step=0.05, label="Emission Rate")
365
+ lc_diffusion = gr.Slider(0.01, 0.5, value=0.1, step=0.01, label="Diffusion Rate")
366
+ lc_decay = gr.Slider(0.0, 0.3, value=0.05, step=0.01, label="Decay Rate")
367
+
368
+ with gr.Row():
369
+ lc_steps = gr.Slider(1, 50, value=10, step=1, label="Number of Steps")
370
+ lc_btn = gr.Button("Run Diffusion", variant="primary")
371
+
372
+ lc_bars = gr.HTML(label="Final Concentrations")
373
+ lc_timeline = gr.Markdown(label="Step-by-Step Snapshot")
374
+
375
+ lc_btn.click(
376
+ fn=run_linear_chain,
377
+ inputs=[lc_nodes, lc_source, lc_emission, lc_diffusion, lc_decay, lc_steps],
378
+ outputs=[lc_bars, lc_timeline],
379
+ )
380
+
381
+ # ── Tab 2: Topologies ────────────────────────────────────
382
+ with gr.TabItem("Topologies"):
383
+ gr.Markdown(
384
+ "Choose a **graph topology** and see how shape affects "
385
+ "gradient formation. The local gradient level bridges to "
386
+ "the `MorphogenGradient` API."
387
+ )
388
+
389
+ with gr.Row():
390
+ tp_topo = gr.Dropdown(
391
+ choices=list(TOPOLOGIES.keys()),
392
+ value="Star",
393
+ label="Topology",
394
+ )
395
+ tp_source = gr.Dropdown(
396
+ choices=sorted(_build_star(6).keys()),
397
+ value="A",
398
+ label="Source Node",
399
+ )
400
+
401
+ with gr.Row():
402
+ tp_emission = gr.Slider(0.05, 1.0, value=0.5, step=0.05, label="Emission Rate")
403
+ tp_diffusion = gr.Slider(0.01, 0.5, value=0.1, step=0.01, label="Diffusion Rate")
404
+ tp_decay = gr.Slider(0.0, 0.3, value=0.05, step=0.01, label="Decay Rate")
405
+
406
+ with gr.Row():
407
+ tp_steps = gr.Slider(1, 50, value=15, step=1, label="Number of Steps")
408
+ tp_btn = gr.Button("Run Diffusion", variant="primary")
409
+
410
+ tp_bars = gr.HTML(label="Concentration Bars")
411
+ with gr.Row():
412
+ with gr.Column():
413
+ tp_snap = gr.Markdown(label="Snapshot Table")
414
+ with gr.Column():
415
+ tp_grad = gr.Markdown(label="Gradient Levels")
416
+
417
+ tp_topo.change(
418
+ fn=_get_topology_nodes,
419
+ inputs=[tp_topo],
420
+ outputs=[tp_source],
421
+ )
422
+ tp_btn.click(
423
+ fn=run_topology,
424
+ inputs=[tp_topo, tp_source, tp_emission, tp_diffusion, tp_decay, tp_steps],
425
+ outputs=[tp_bars, tp_snap, tp_grad],
426
+ )
427
+
428
+ # ── Tab 3: Competing Sources ─────────────────────────────
429
+ with gr.TabItem("Competing Sources"):
430
+ gr.Markdown(
431
+ "Place **two different morphogens** at different nodes "
432
+ "and observe overlapping gradients. Each morphogen type "
433
+ "diffuses independently."
434
+ )
435
+
436
+ with gr.Row():
437
+ cs_nodes = gr.Slider(3, 8, value=5, step=1, label="Chain Length")
438
+
439
+ morphogen_choices = [mt.value for mt in MorphogenType]
440
+
441
+ with gr.Row():
442
+ with gr.Column():
443
+ gr.Markdown("#### Source 1")
444
+ cs_src1 = gr.Dropdown(
445
+ choices=[chr(65 + i) for i in range(8)],
446
+ value="A",
447
+ label="Node",
448
+ )
449
+ cs_type1 = gr.Dropdown(
450
+ choices=morphogen_choices,
451
+ value="complexity",
452
+ label="Morphogen Type",
453
+ )
454
+ cs_rate1 = gr.Slider(0.05, 1.0, value=0.5, step=0.05, label="Emission Rate")
455
+
456
+ with gr.Column():
457
+ gr.Markdown("#### Source 2")
458
+ cs_src2 = gr.Dropdown(
459
+ choices=[chr(65 + i) for i in range(8)],
460
+ value="E",
461
+ label="Node",
462
+ )
463
+ cs_type2 = gr.Dropdown(
464
+ choices=morphogen_choices,
465
+ value="confidence",
466
+ label="Morphogen Type",
467
+ )
468
+ cs_rate2 = gr.Slider(0.05, 1.0, value=0.3, step=0.05, label="Emission Rate")
469
+
470
+ with gr.Row():
471
+ cs_diffusion = gr.Slider(0.01, 0.5, value=0.1, step=0.01, label="Diffusion Rate")
472
+ cs_decay = gr.Slider(0.0, 0.3, value=0.05, step=0.01, label="Decay Rate")
473
+ cs_steps = gr.Slider(1, 50, value=15, step=1, label="Steps")
474
+
475
+ cs_btn = gr.Button("Run Competing Diffusion", variant="primary")
476
+ cs_bars = gr.HTML(label="Concentration Bars")
477
+ cs_table = gr.Markdown(label="Per-Morphogen Table")
478
+
479
+ cs_btn.click(
480
+ fn=run_competing,
481
+ inputs=[
482
+ cs_nodes, cs_src1, cs_type1, cs_rate1,
483
+ cs_src2, cs_type2, cs_rate2,
484
+ cs_diffusion, cs_decay, cs_steps,
485
+ ],
486
+ outputs=[cs_bars, cs_table],
487
+ )
488
+
489
+ return app
490
+
491
+
492
+ if __name__ == "__main__":
493
+ app = build_app()
494
+ app.launch(theme=gr.themes.Soft())
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0
2
+ operon-ai