Upload folder using huggingface_hub
Browse files- README.md +22 -6
- __pycache__/app.cpython-314.pyc +0 -0
- app.py +436 -0
- requirements.txt +2 -0
README.md
CHANGED
|
@@ -1,12 +1,28 @@
|
|
| 1 |
---
|
| 2 |
-
title: Operon
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 6.5.1
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Operon Biological Oscillators
|
| 3 |
+
emoji: π
|
| 4 |
+
colorFrom: purple
|
| 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: Biological oscillator patterns and waveform visualization
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# π Biological Oscillator Patterns
|
| 15 |
+
|
| 16 |
+
Compute and visualize oscillator waveforms (sine, square, sawtooth, triangle, pulse) with damping, plus explore biological oscillator phase structures.
|
| 17 |
+
|
| 18 |
+
## Features
|
| 19 |
+
|
| 20 |
+
- **Tab 1 β Waveform Explorer**: 5 waveform types with configurable frequency, amplitude, damping, and cycles
|
| 21 |
+
- **Tab 2 β Biological Oscillators**: Circadian (day/night), Heartbeat (BPM), and Cell Cycle (G1/S/G2/M) phase breakdowns
|
| 22 |
+
- **8 presets**: Sine wave, square pulse, damped sine, fast triangle, sawtooth, circadian, heartbeat, cell division
|
| 23 |
+
|
| 24 |
+
## How It Works
|
| 25 |
+
|
| 26 |
+
Waveforms are computed mathematically (matching `oscillator.py` internals) without starting background threads. Biological oscillators show phase structures that map to real-world patterns like circadian rhythms and cell division cycles.
|
| 27 |
+
|
| 28 |
+
[GitHub](https://github.com/coredipper/operon) | [PyPI](https://pypi.org/project/operon-ai/)
|
__pycache__/app.cpython-314.pyc
ADDED
|
Binary file (19.4 kB). View file
|
|
|
app.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Operon Biological Oscillator Patterns -- Interactive Gradio Demo
|
| 3 |
+
================================================================
|
| 4 |
+
|
| 5 |
+
Two-tab demo: compute and display waveform shapes (sine, square, sawtooth,
|
| 6 |
+
triangle, pulse) with damping, plus show biological oscillator phase
|
| 7 |
+
structures (Circadian, Heartbeat, CellCycle).
|
| 8 |
+
|
| 9 |
+
CRITICAL: Oscillators use background threads. We do NOT call start()/stop().
|
| 10 |
+
Waveform values are computed mathematically via _compute_waveform_value().
|
| 11 |
+
|
| 12 |
+
Run locally:
|
| 13 |
+
pip install gradio
|
| 14 |
+
python space-oscillator/app.py
|
| 15 |
+
|
| 16 |
+
Deploy to HuggingFace Spaces:
|
| 17 |
+
Copy this directory to a new HF Space with sdk=gradio.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import math
|
| 21 |
+
import sys
|
| 22 |
+
from pathlib import Path
|
| 23 |
+
|
| 24 |
+
import gradio as gr
|
| 25 |
+
|
| 26 |
+
# Allow importing operon_ai from the repo root when running locally
|
| 27 |
+
_repo_root = Path(__file__).resolve().parent.parent
|
| 28 |
+
if str(_repo_root) not in sys.path:
|
| 29 |
+
sys.path.insert(0, str(_repo_root))
|
| 30 |
+
|
| 31 |
+
from operon_ai import WaveformType
|
| 32 |
+
|
| 33 |
+
# ββ Waveform computation (matches oscillator.py lines 413-433) ββββββββββββ
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _compute_waveform_value(waveform: WaveformType, phase: float) -> float:
|
| 37 |
+
"""Compute waveform output for a given phase in [0, 1]."""
|
| 38 |
+
if waveform == WaveformType.SINE:
|
| 39 |
+
return math.sin(2 * math.pi * phase)
|
| 40 |
+
elif waveform == WaveformType.SQUARE:
|
| 41 |
+
return 1.0 if phase < 0.5 else -1.0
|
| 42 |
+
elif waveform == WaveformType.SAWTOOTH:
|
| 43 |
+
return 2.0 * phase - 1.0
|
| 44 |
+
elif waveform == WaveformType.TRIANGLE:
|
| 45 |
+
return 4.0 * phase - 1.0 if phase < 0.5 else 3.0 - 4.0 * phase
|
| 46 |
+
elif waveform == WaveformType.PULSE:
|
| 47 |
+
return 1.0 if phase < 0.1 else 0.0
|
| 48 |
+
return 0.0
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# ββ Tab 1: Waveform Presets βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 52 |
+
|
| 53 |
+
WAVEFORM_PRESETS: dict[str, dict] = {
|
| 54 |
+
"(custom)": {
|
| 55 |
+
"description": "Configure your own waveform parameters.",
|
| 56 |
+
"waveform": "sine",
|
| 57 |
+
"frequency": 1.0,
|
| 58 |
+
"amplitude": 1.0,
|
| 59 |
+
"damping": 0.0,
|
| 60 |
+
"cycles": 2,
|
| 61 |
+
},
|
| 62 |
+
"Sine wave": {
|
| 63 |
+
"description": "Classic sinusoidal oscillation β the fundamental waveform.",
|
| 64 |
+
"waveform": "sine",
|
| 65 |
+
"frequency": 1.0,
|
| 66 |
+
"amplitude": 1.0,
|
| 67 |
+
"damping": 0.0,
|
| 68 |
+
"cycles": 2,
|
| 69 |
+
},
|
| 70 |
+
"Square pulse": {
|
| 71 |
+
"description": "Binary on/off switching at 2 Hz β digital clock behavior.",
|
| 72 |
+
"waveform": "square",
|
| 73 |
+
"frequency": 2.0,
|
| 74 |
+
"amplitude": 1.0,
|
| 75 |
+
"damping": 0.0,
|
| 76 |
+
"cycles": 3,
|
| 77 |
+
},
|
| 78 |
+
"Damped sine": {
|
| 79 |
+
"description": "Sine wave with exponential decay β models energy dissipation.",
|
| 80 |
+
"waveform": "sine",
|
| 81 |
+
"frequency": 1.0,
|
| 82 |
+
"amplitude": 1.0,
|
| 83 |
+
"damping": 0.1,
|
| 84 |
+
"cycles": 5,
|
| 85 |
+
},
|
| 86 |
+
"Fast triangle": {
|
| 87 |
+
"description": "High-frequency triangle wave β linear ramps up and down.",
|
| 88 |
+
"waveform": "triangle",
|
| 89 |
+
"frequency": 5.0,
|
| 90 |
+
"amplitude": 1.0,
|
| 91 |
+
"damping": 0.0,
|
| 92 |
+
"cycles": 3,
|
| 93 |
+
},
|
| 94 |
+
"Sawtooth": {
|
| 95 |
+
"description": "Linear ramp from -1 to +1 β used in synthesis and scanning.",
|
| 96 |
+
"waveform": "sawtooth",
|
| 97 |
+
"frequency": 1.0,
|
| 98 |
+
"amplitude": 1.0,
|
| 99 |
+
"damping": 0.0,
|
| 100 |
+
"cycles": 3,
|
| 101 |
+
},
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _load_waveform_preset(name: str) -> tuple[str, float, float, float, int]:
|
| 106 |
+
p = WAVEFORM_PRESETS.get(name, WAVEFORM_PRESETS["(custom)"])
|
| 107 |
+
return p["waveform"], p["frequency"], p["amplitude"], p["damping"], p["cycles"]
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
# ββ Tab 2: Biological Oscillator Presets ββββββββββββββββββββββββββββββββββ
|
| 111 |
+
|
| 112 |
+
BIO_PRESETS: dict[str, dict] = {
|
| 113 |
+
"Standard circadian": {
|
| 114 |
+
"description": "16-hour day / 8-hour night cycle β governs sleep-wake patterns.",
|
| 115 |
+
"type": "circadian",
|
| 116 |
+
"day_hours": 16.0,
|
| 117 |
+
"night_hours": 8.0,
|
| 118 |
+
},
|
| 119 |
+
"Fast heartbeat": {
|
| 120 |
+
"description": "120 BPM heartbeat β elevated heart rate during exercise.",
|
| 121 |
+
"type": "heartbeat",
|
| 122 |
+
"bpm": 120.0,
|
| 123 |
+
},
|
| 124 |
+
"Cell division": {
|
| 125 |
+
"description": "24-hour cell cycle: G1 (40%), S (30%), G2 (20%), M (10%).",
|
| 126 |
+
"type": "cell_cycle",
|
| 127 |
+
"cycle_hours": 24.0,
|
| 128 |
+
"phases": {"G1": 0.4, "S": 0.3, "G2": 0.2, "M": 0.1},
|
| 129 |
+
},
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
# ββ Tab 1: Waveform computation ββββββββββββββββββββββββββββββββββββββββββ
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def run_waveform(
|
| 137 |
+
preset_name: str,
|
| 138 |
+
waveform_name: str,
|
| 139 |
+
frequency: float,
|
| 140 |
+
amplitude: float,
|
| 141 |
+
damping: float,
|
| 142 |
+
cycles: int,
|
| 143 |
+
) -> tuple[str, str, str]:
|
| 144 |
+
"""Compute waveform samples.
|
| 145 |
+
|
| 146 |
+
Returns (config_banner_html, sample_table_md, stats_md).
|
| 147 |
+
"""
|
| 148 |
+
waveform = WaveformType(waveform_name)
|
| 149 |
+
cycles = int(cycles)
|
| 150 |
+
|
| 151 |
+
# Config banner
|
| 152 |
+
banner = (
|
| 153 |
+
f'<div style="padding:10px 14px;border-radius:8px;background:#f0f9ff;'
|
| 154 |
+
f'border:1px solid #bae6fd;margin-bottom:8px">'
|
| 155 |
+
f'<span style="font-weight:700;font-size:1.1em">{waveform.value.upper()}</span> '
|
| 156 |
+
f'<span style="color:#666"> | freq={frequency} Hz | amp={amplitude} | '
|
| 157 |
+
f'damping={damping} | cycles={cycles}</span></div>'
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
# Compute samples (50 samples per cycle)
|
| 161 |
+
samples_per_cycle = 50
|
| 162 |
+
total_samples = samples_per_cycle * cycles
|
| 163 |
+
period = 1.0 / frequency if frequency > 0 else 1.0
|
| 164 |
+
|
| 165 |
+
rows = [
|
| 166 |
+
"| # | Time (s) | Phase | Cycle | Amplitude | Value |",
|
| 167 |
+
"| ---: | ---: | ---: | ---: | ---: | ---: |",
|
| 168 |
+
]
|
| 169 |
+
|
| 170 |
+
values = []
|
| 171 |
+
for i in range(total_samples + 1):
|
| 172 |
+
t = i * (cycles * period) / total_samples
|
| 173 |
+
# Phase within current cycle
|
| 174 |
+
cycle_time = t / period
|
| 175 |
+
current_cycle = int(cycle_time)
|
| 176 |
+
phase = cycle_time - current_cycle
|
| 177 |
+
if phase < 0:
|
| 178 |
+
phase = 0.0
|
| 179 |
+
if current_cycle >= cycles:
|
| 180 |
+
current_cycle = cycles - 1
|
| 181 |
+
phase = 1.0
|
| 182 |
+
|
| 183 |
+
# Apply damping: amplitude decays exponentially
|
| 184 |
+
damped_amp = amplitude * math.exp(-damping * t) if damping > 0 else amplitude
|
| 185 |
+
raw = _compute_waveform_value(waveform, phase)
|
| 186 |
+
value = raw * damped_amp
|
| 187 |
+
values.append(value)
|
| 188 |
+
|
| 189 |
+
# Show every 5th sample to keep table manageable
|
| 190 |
+
if i % 5 == 0 or i == total_samples:
|
| 191 |
+
rows.append(
|
| 192 |
+
f"| {i} | {t:.4f} | {phase:.3f} | {current_cycle + 1} "
|
| 193 |
+
f"| {damped_amp:.4f} | {value:.4f} |"
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
table_md = "\n".join(rows)
|
| 197 |
+
|
| 198 |
+
# Stats
|
| 199 |
+
min_val = min(values)
|
| 200 |
+
max_val = max(values)
|
| 201 |
+
mean_val = sum(values) / len(values)
|
| 202 |
+
zero_crossings = sum(
|
| 203 |
+
1 for i in range(1, len(values))
|
| 204 |
+
if (values[i] >= 0) != (values[i - 1] >= 0)
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
stats = f"""### Waveform Statistics
|
| 208 |
+
|
| 209 |
+
| Metric | Value |
|
| 210 |
+
| :--- | :--- |
|
| 211 |
+
| Samples computed | {len(values)} |
|
| 212 |
+
| Min value | {min_val:.4f} |
|
| 213 |
+
| Max value | {max_val:.4f} |
|
| 214 |
+
| Mean value | {mean_val:.4f} |
|
| 215 |
+
| Zero crossings | {zero_crossings} |
|
| 216 |
+
| Period | {period:.4f} s |
|
| 217 |
+
| Total duration | {cycles * period:.4f} s |
|
| 218 |
+
|
| 219 |
+
### Waveform Guide
|
| 220 |
+
|
| 221 |
+
- **SINE**: Smooth continuous oscillation. Most natural biological rhythm.
|
| 222 |
+
- **SQUARE**: Binary switching. Models on/off states (gene expression toggle).
|
| 223 |
+
- **SAWTOOTH**: Linear ramp with sharp reset. Models gradual buildup + sudden release.
|
| 224 |
+
- **TRIANGLE**: Symmetric linear ramp. Models gradual charge/discharge cycles.
|
| 225 |
+
- **PULSE**: Brief spike. Models action potentials and trigger signals.
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
return banner, table_md, stats
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
# ββ Tab 2: Biological oscillator display βββββββββββββββββββββββββββββββββ
|
| 232 |
+
|
| 233 |
+
PHASE_COLORS = {
|
| 234 |
+
"Day": "#fbbf24",
|
| 235 |
+
"Night": "#1e40af",
|
| 236 |
+
"Systole": "#ef4444",
|
| 237 |
+
"Diastole": "#3b82f6",
|
| 238 |
+
"G1": "#22c55e",
|
| 239 |
+
"S": "#3b82f6",
|
| 240 |
+
"G2": "#a855f7",
|
| 241 |
+
"M": "#ef4444",
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def run_bio_oscillator(preset_name: str) -> tuple[str, str, str]:
|
| 246 |
+
"""Display biological oscillator phase structure.
|
| 247 |
+
|
| 248 |
+
Returns (type_banner_html, phase_table_md, explanation_md).
|
| 249 |
+
"""
|
| 250 |
+
p = BIO_PRESETS.get(preset_name)
|
| 251 |
+
if not p:
|
| 252 |
+
return "<p>Select a biological oscillator preset.</p>", "", ""
|
| 253 |
+
|
| 254 |
+
osc_type = p["type"]
|
| 255 |
+
|
| 256 |
+
if osc_type == "circadian":
|
| 257 |
+
day_h = p["day_hours"]
|
| 258 |
+
night_h = p["night_hours"]
|
| 259 |
+
total = day_h + night_h
|
| 260 |
+
banner = (
|
| 261 |
+
f'<div style="padding:10px 14px;border-radius:8px;background:#fffbeb;'
|
| 262 |
+
f'border:1px solid #fde68a">'
|
| 263 |
+
f'<span style="font-weight:700;font-size:1.1em">CIRCADIAN OSCILLATOR</span> '
|
| 264 |
+
f'<span style="color:#666"> | {total}h cycle = {day_h}h day + {night_h}h night</span></div>'
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
phases = [
|
| 268 |
+
("Day", day_h, day_h / total),
|
| 269 |
+
("Night", night_h, night_h / total),
|
| 270 |
+
]
|
| 271 |
+
|
| 272 |
+
explanation = f"""### Circadian Rhythm
|
| 273 |
+
|
| 274 |
+
The circadian oscillator governs the sleep-wake cycle across a {total}-hour period.
|
| 275 |
+
|
| 276 |
+
- **Day phase** ({day_h}h, {day_h / total * 100:.0f}%): Active metabolism, high gene expression for repair enzymes, elevated cortisol
|
| 277 |
+
- **Night phase** ({night_h}h, {night_h / total * 100:.0f}%): Reduced metabolism, memory consolidation, growth hormone release
|
| 278 |
+
|
| 279 |
+
In agent systems, circadian oscillators can gate when expensive operations are allowed (day = active processing, night = background maintenance).
|
| 280 |
+
"""
|
| 281 |
+
|
| 282 |
+
elif osc_type == "heartbeat":
|
| 283 |
+
bpm = p["bpm"]
|
| 284 |
+
period_ms = 60000 / bpm
|
| 285 |
+
systole_ms = period_ms * 0.35
|
| 286 |
+
diastole_ms = period_ms * 0.65
|
| 287 |
+
banner = (
|
| 288 |
+
f'<div style="padding:10px 14px;border-radius:8px;background:#fef2f2;'
|
| 289 |
+
f'border:1px solid #fecaca">'
|
| 290 |
+
f'<span style="font-weight:700;font-size:1.1em">HEARTBEAT OSCILLATOR</span> '
|
| 291 |
+
f'<span style="color:#666"> | {bpm} BPM | period={period_ms:.0f}ms</span></div>'
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
phases = [
|
| 295 |
+
("Systole", systole_ms / 1000, 0.35),
|
| 296 |
+
("Diastole", diastole_ms / 1000, 0.65),
|
| 297 |
+
]
|
| 298 |
+
|
| 299 |
+
explanation = f"""### Heartbeat Rhythm
|
| 300 |
+
|
| 301 |
+
The heartbeat oscillator at {bpm} BPM has a period of {period_ms:.0f}ms.
|
| 302 |
+
|
| 303 |
+
- **Systole** ({systole_ms:.0f}ms, 35%): Contraction phase β pumping output. In agents: burst processing, sending responses.
|
| 304 |
+
- **Diastole** ({diastole_ms:.0f}ms, 65%): Relaxation phase β refilling. In agents: gathering input, buffering requests.
|
| 305 |
+
|
| 306 |
+
At {bpm} BPM, the system processes {bpm} pulse cycles per minute, each with a work burst followed by a collection period.
|
| 307 |
+
"""
|
| 308 |
+
|
| 309 |
+
else: # cell_cycle
|
| 310 |
+
cycle_h = p["cycle_hours"]
|
| 311 |
+
phase_pcts = p["phases"]
|
| 312 |
+
banner = (
|
| 313 |
+
f'<div style="padding:10px 14px;border-radius:8px;background:#f0fdf4;'
|
| 314 |
+
f'border:1px solid #bbf7d0">'
|
| 315 |
+
f'<span style="font-weight:700;font-size:1.1em">CELL CYCLE OSCILLATOR</span> '
|
| 316 |
+
f'<span style="color:#666"> | {cycle_h}h total cycle</span></div>'
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
phases = [
|
| 320 |
+
(name, cycle_h * pct, pct)
|
| 321 |
+
for name, pct in phase_pcts.items()
|
| 322 |
+
]
|
| 323 |
+
|
| 324 |
+
explanation = f"""### Cell Division Cycle
|
| 325 |
+
|
| 326 |
+
The cell cycle oscillator spans {cycle_h} hours with four distinct phases:
|
| 327 |
+
|
| 328 |
+
- **G1** ({cycle_h * 0.4:.1f}h, 40%): Gap 1 β cell growth, organelle duplication. In agents: planning and resource allocation.
|
| 329 |
+
- **S** ({cycle_h * 0.3:.1f}h, 30%): Synthesis β DNA replication. In agents: core work/computation phase.
|
| 330 |
+
- **G2** ({cycle_h * 0.2:.1f}h, 20%): Gap 2 β error checking, preparation for division. In agents: validation and testing.
|
| 331 |
+
- **M** ({cycle_h * 0.1:.1f}h, 10%): Mitosis β actual division. In agents: spawning sub-agents or forking work.
|
| 332 |
+
|
| 333 |
+
Checkpoints between phases ensure quality: G1/S checkpoint (commit to replication?), G2/M checkpoint (ready to divide?).
|
| 334 |
+
"""
|
| 335 |
+
|
| 336 |
+
# Phase breakdown table
|
| 337 |
+
table_lines = [
|
| 338 |
+
"| Phase | Duration | Fraction | Visual |",
|
| 339 |
+
"| :--- | ---: | ---: | :--- |",
|
| 340 |
+
]
|
| 341 |
+
for name, duration, fraction in phases:
|
| 342 |
+
color = PHASE_COLORS.get(name, "#888")
|
| 343 |
+
bar_width = max(5, int(fraction * 300))
|
| 344 |
+
duration_str = f"{duration:.2f}h" if duration >= 0.01 else f"{duration * 3600:.0f}s"
|
| 345 |
+
table_lines.append(
|
| 346 |
+
f"| **{name}** | {duration_str} | {fraction * 100:.0f}% | "
|
| 347 |
+
f'<span style="display:inline-block;width:{bar_width}px;height:16px;'
|
| 348 |
+
f'background:{color};border-radius:3px"></span> |'
|
| 349 |
+
)
|
| 350 |
+
table_md = "\n".join(table_lines)
|
| 351 |
+
|
| 352 |
+
return banner, table_md, explanation
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
# ββ Gradio UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
def build_app() -> gr.Blocks:
|
| 359 |
+
with gr.Blocks(title="Biological Oscillators") as app:
|
| 360 |
+
gr.Markdown(
|
| 361 |
+
"# π Biological Oscillator Patterns\n"
|
| 362 |
+
"Explore waveform shapes and biological oscillator phase "
|
| 363 |
+
"structures β computed mathematically, no threads needed."
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
with gr.Tabs():
|
| 367 |
+
# ββ Tab 1: Waveform Explorer ββββββββββββββββββββββββββββββ
|
| 368 |
+
with gr.TabItem("Waveform Explorer"):
|
| 369 |
+
with gr.Row():
|
| 370 |
+
wave_preset_dd = gr.Dropdown(
|
| 371 |
+
choices=list(WAVEFORM_PRESETS.keys()),
|
| 372 |
+
value="Sine wave",
|
| 373 |
+
label="Preset",
|
| 374 |
+
scale=2,
|
| 375 |
+
)
|
| 376 |
+
wave_btn = gr.Button("Compute Waveform", variant="primary", scale=1)
|
| 377 |
+
|
| 378 |
+
with gr.Row():
|
| 379 |
+
waveform_dd = gr.Dropdown(
|
| 380 |
+
choices=[w.value for w in WaveformType],
|
| 381 |
+
value="sine",
|
| 382 |
+
label="Waveform",
|
| 383 |
+
)
|
| 384 |
+
freq_sl = gr.Slider(0.1, 10.0, value=1.0, step=0.1, label="Frequency (Hz)")
|
| 385 |
+
amp_sl = gr.Slider(0.1, 2.0, value=1.0, step=0.1, label="Amplitude")
|
| 386 |
+
|
| 387 |
+
with gr.Row():
|
| 388 |
+
damp_sl = gr.Slider(0.0, 0.5, value=0.0, step=0.01, label="Damping factor")
|
| 389 |
+
cycles_sl = gr.Slider(1, 10, value=2, step=1, label="Cycles")
|
| 390 |
+
|
| 391 |
+
wave_banner = gr.HTML(label="Configuration")
|
| 392 |
+
with gr.Row():
|
| 393 |
+
with gr.Column(scale=2):
|
| 394 |
+
wave_table = gr.Markdown(label="Samples")
|
| 395 |
+
with gr.Column(scale=1):
|
| 396 |
+
wave_stats = gr.Markdown(label="Statistics")
|
| 397 |
+
|
| 398 |
+
wave_preset_dd.change(
|
| 399 |
+
fn=_load_waveform_preset,
|
| 400 |
+
inputs=[wave_preset_dd],
|
| 401 |
+
outputs=[waveform_dd, freq_sl, amp_sl, damp_sl, cycles_sl],
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
+
wave_btn.click(
|
| 405 |
+
fn=run_waveform,
|
| 406 |
+
inputs=[wave_preset_dd, waveform_dd, freq_sl, amp_sl, damp_sl, cycles_sl],
|
| 407 |
+
outputs=[wave_banner, wave_table, wave_stats],
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
# ββ Tab 2: Biological Oscillators βββββββββββββββββββββββββ
|
| 411 |
+
with gr.TabItem("Biological Oscillators"):
|
| 412 |
+
with gr.Row():
|
| 413 |
+
bio_preset_dd = gr.Dropdown(
|
| 414 |
+
choices=list(BIO_PRESETS.keys()),
|
| 415 |
+
value="Standard circadian",
|
| 416 |
+
label="Biological Oscillator",
|
| 417 |
+
scale=2,
|
| 418 |
+
)
|
| 419 |
+
bio_btn = gr.Button("Show Phases", variant="primary", scale=1)
|
| 420 |
+
|
| 421 |
+
bio_banner = gr.HTML(label="Oscillator Type")
|
| 422 |
+
bio_table = gr.Markdown(label="Phase Breakdown")
|
| 423 |
+
bio_explanation = gr.Markdown(label="Explanation")
|
| 424 |
+
|
| 425 |
+
bio_btn.click(
|
| 426 |
+
fn=run_bio_oscillator,
|
| 427 |
+
inputs=[bio_preset_dd],
|
| 428 |
+
outputs=[bio_banner, bio_table, bio_explanation],
|
| 429 |
+
)
|
| 430 |
+
|
| 431 |
+
return app
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
if __name__ == "__main__":
|
| 435 |
+
app = build_app()
|
| 436 |
+
app.launch(theme=gr.themes.Soft())
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.0
|
| 2 |
+
operon-ai
|