Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import os
|
| 2 |
import math
|
| 3 |
import json
|
|
|
|
| 4 |
import numpy as np
|
| 5 |
import pandas as pd
|
| 6 |
import matplotlib.pyplot as plt
|
|
@@ -20,7 +21,9 @@ os.makedirs(OUTDIR, exist_ok=True)
|
|
| 20 |
# Shared utilities
|
| 21 |
# -----------------------------
|
| 22 |
def set_seed(seed: int):
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
|
| 25 |
def clamp(x, lo, hi):
|
| 26 |
return max(lo, min(hi, x))
|
|
@@ -49,7 +52,7 @@ def tau_eff_adaptive(
|
|
| 49 |
"""
|
| 50 |
τ_eff is implemented here as a timing/decision delay modifier.
|
| 51 |
- base: baseline τ_eff
|
| 52 |
-
- slow_by: explicit slow-down term
|
| 53 |
- gain: reaction strength to uncertainty
|
| 54 |
- cap: prevents absurd values
|
| 55 |
"""
|
|
@@ -103,6 +106,7 @@ def simulate_neo(
|
|
| 103 |
dist = float(np.linalg.norm(meas))
|
| 104 |
|
| 105 |
speed = float(np.linalg.norm(vel))
|
|
|
|
| 106 |
uncertainty = clamp((noise_km / max(alert_km, 1.0)) * 2.0 + (speed / 200.0) * 0.2, 0.0, 1.0)
|
| 107 |
|
| 108 |
baseline_alert = dist <= alert_km
|
|
@@ -302,11 +306,6 @@ def simulate_jitter(
|
|
| 302 |
|
| 303 |
# -----------------------------
|
| 304 |
# Starship-style Landing Harness (2D)
|
| 305 |
-
# FIXES:
|
| 306 |
-
# - wind is SIGNED (gusts left/right), not always positive drift
|
| 307 |
-
# - control authority increased so the goal is actually reachable
|
| 308 |
-
# - gate cannot veto "must-correct" moments (override when error is big / low altitude)
|
| 309 |
-
# - includes simple wind feed-forward cancellation term
|
| 310 |
# -----------------------------
|
| 311 |
def simulate_landing(
|
| 312 |
seed: int,
|
|
@@ -328,7 +327,7 @@ def simulate_landing(
|
|
| 328 |
x = 60.0
|
| 329 |
xv = 0.0
|
| 330 |
|
| 331 |
-
#
|
| 332 |
ix = 0.0
|
| 333 |
|
| 334 |
anomalies = 0
|
|
@@ -336,39 +335,31 @@ def simulate_landing(
|
|
| 336 |
ops_proxy = 0
|
| 337 |
rows = []
|
| 338 |
|
| 339 |
-
# Tuned plant constants (simple, but consistent)
|
| 340 |
g = -9.81
|
| 341 |
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
VERT_CTRL = 0.22 # vertical accel per control unit
|
| 346 |
|
| 347 |
-
# Override thresholds (safety style)
|
| 348 |
OVERRIDE_X = 18.0
|
| 349 |
OVERRIDE_ALT = 260.0
|
| 350 |
|
| 351 |
for t in range(int(steps)):
|
| 352 |
-
# Signed wind with gusty behaviour (not always pushing one way)
|
| 353 |
gust = math.sin(0.08 * t) + 0.55 * math.sin(0.21 * t + 0.7)
|
| 354 |
wind = (wind_max * 0.75) * gust + np.random.normal(0.0, 0.65)
|
| 355 |
wind = clamp(wind, -wind_max, wind_max)
|
| 356 |
|
| 357 |
-
# Thrust disturbance
|
| 358 |
thrust_dev = np.random.normal(0.0, thrust_noise)
|
| 359 |
|
| 360 |
-
# Measurement noise
|
| 361 |
meas_alt = alt + np.random.normal(0, 0.6)
|
| 362 |
meas_vv = vv + np.random.normal(0, 0.35)
|
| 363 |
meas_x = x + np.random.normal(0, 0.8)
|
| 364 |
meas_xv = xv + np.random.normal(0, 0.25)
|
| 365 |
|
| 366 |
-
# Uncertainty proxy
|
| 367 |
uncertainty = clamp((abs(thrust_dev) / 5.0) * 0.18 + (abs(wind) / max(wind_max, 1e-9)) * 0.30, 0.0, 1.0)
|
| 368 |
tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
|
| 369 |
conf = rft_confidence(uncertainty)
|
| 370 |
|
| 371 |
-
# Anomaly definition (count only meaningful events)
|
| 372 |
anomaly_types = []
|
| 373 |
if abs(wind) > (0.85 * wind_max):
|
| 374 |
anomaly_types.append("High wind")
|
|
@@ -380,23 +371,17 @@ def simulate_landing(
|
|
| 380 |
if is_anomaly:
|
| 381 |
anomalies += 1
|
| 382 |
|
| 383 |
-
# Baseline control (continuous)
|
| 384 |
u_base_x = -kp_baseline * meas_x - 0.30 * meas_xv
|
| 385 |
u_base_v = -kp_baseline * (meas_vv + 5.0)
|
| 386 |
|
| 387 |
-
|
| 388 |
-
phase = 1.0 - clamp(meas_alt / 1000.0, 0.0, 1.0) # 0 high up, 1 near ground
|
| 389 |
lookahead = 1.0 + 1.6 * phase
|
| 390 |
|
| 391 |
-
# Wind feed-forward: cancel expected push
|
| 392 |
-
# If wind is pushing +, we apply opposite control; vice versa.
|
| 393 |
wind_ff = (WIND_PUSH * wind) / max(LAT_CTRL, 1e-9)
|
| 394 |
|
| 395 |
-
# Integral accumulates only when closer (so it doesn't explode up high)
|
| 396 |
if meas_alt < 600:
|
| 397 |
ix = clamp(ix + (meas_x * dt) * 0.0025, -40.0, 40.0)
|
| 398 |
|
| 399 |
-
# Gate + override logic
|
| 400 |
do_action = rft_gate(conf, tau, gate_threshold)
|
| 401 |
must_act = (abs(meas_x) > OVERRIDE_X) or (meas_alt < OVERRIDE_ALT)
|
| 402 |
do_action = bool(do_action or must_act)
|
|
@@ -404,20 +389,16 @@ def simulate_landing(
|
|
| 404 |
u_rft_x = 0.0
|
| 405 |
u_rft_v = 0.0
|
| 406 |
if do_action:
|
| 407 |
-
# PD + small I + wind FF
|
| 408 |
u_rft_x = (-kp_rft * lookahead * meas_x) - (0.42 * meas_xv) - (0.20 * ix) - wind_ff
|
| 409 |
u_rft_v = (-kp_rft * lookahead * (meas_vv + 5.0))
|
| 410 |
actions += 1
|
| 411 |
|
| 412 |
-
# Saturate control (realistic bounded actuation)
|
| 413 |
u_rft_x = clamp(u_rft_x, -20.0, 20.0)
|
| 414 |
u_rft_v = clamp(u_rft_v, -18.0, 18.0)
|
| 415 |
|
| 416 |
-
# Apply dynamics
|
| 417 |
vv = vv + (g + VERT_CTRL * u_rft_v + 0.09 * thrust_dev) * dt
|
| 418 |
alt = max(0.0, alt + vv * dt)
|
| 419 |
|
| 420 |
-
# Lateral acceleration from wind and control
|
| 421 |
xv = xv + (WIND_PUSH * wind - LAT_CTRL * u_rft_x) * dt
|
| 422 |
x = x + xv * dt
|
| 423 |
|
|
@@ -501,6 +482,387 @@ def simulate_landing(
|
|
| 501 |
|
| 502 |
return summary, [p_alt, p_x, p_w, p_a], csv_path
|
| 503 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
# -----------------------------
|
| 505 |
# Benchmarks
|
| 506 |
# -----------------------------
|
|
@@ -595,35 +957,33 @@ def run_benchmarks(
|
|
| 595 |
return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
|
| 596 |
|
| 597 |
# -----------------------------
|
| 598 |
-
# UI text blocks
|
| 599 |
# -----------------------------
|
| 600 |
HOME_MD = """
|
| 601 |
# Rendered Frame Theory (RFT) — Agent Console
|
| 602 |
|
| 603 |
-
|
| 604 |
|
| 605 |
-
|
| 606 |
-
Run it. Change the parameters. Break it. Compare baseline vs RFT.
|
| 607 |
|
| 608 |
-
|
| 609 |
|
| 610 |
**Decision timing matters.**
|
| 611 |
RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
|
| 612 |
|
| 613 |
-
This Space contains
|
| 614 |
-
- **NEO alerting**
|
| 615 |
-
- **Satellite jitter reduction**
|
| 616 |
-
- **Starship-style landing harness**
|
| 617 |
-
|
| 618 |
-
Every tab shows what it’s doing, why, and where it wins or loses.
|
| 619 |
|
| 620 |
-
No SciPy. No hidden dependencies. No model weights.
|
| 621 |
"""
|
| 622 |
|
| 623 |
LIVE_MD = """
|
| 624 |
# Live Console
|
| 625 |
|
| 626 |
-
|
| 627 |
|
| 628 |
- deterministic runs (seeded)
|
| 629 |
- plots saved
|
|
@@ -634,99 +994,78 @@ This tab is a single place to run everything quickly and export logs.
|
|
| 634 |
THEORY_PRACTICE_MD = """
|
| 635 |
# Theory → Practice (how I implement RFT here)
|
| 636 |
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
## 1) Uncertainty (explicit)
|
| 640 |
-
I compute an uncertainty proxy from noise + disturbance scale.
|
| 641 |
|
| 642 |
## 2) Confidence
|
| 643 |
-
|
| 644 |
|
| 645 |
## 3) Adaptive τ_eff
|
| 646 |
-
|
| 647 |
-
- higher uncertainty → higher τ_eff
|
| 648 |
-
- and yes, I explicitly slow τ_eff by 1.0, because this was the behaviour I wanted to test.
|
| 649 |
|
| 650 |
## 4) Collapse gate
|
| 651 |
-
|
| 652 |
-
- confidence
|
| 653 |
-
- τ_eff increases strictness
|
| 654 |
|
| 655 |
-
## 5) Why
|
| 656 |
Baseline controllers often act constantly.
|
| 657 |
-
RFT tries to act less often, but more decisively
|
| 658 |
"""
|
| 659 |
|
| 660 |
MATH_MD = r"""
|
| 661 |
# Mathematics (minimal and implementation-linked)
|
| 662 |
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
- τ_eff ≥ 1 : effective render/decision timing factor (dimensionless)
|
| 667 |
-
|
| 668 |
-
## Definitions
|
| 669 |
|
| 670 |
-
|
| 671 |
\[
|
| 672 |
C = \text{clip}(1 - u, 0, 1)
|
| 673 |
\]
|
| 674 |
|
| 675 |
-
|
| 676 |
\[
|
| 677 |
\tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
|
| 678 |
\]
|
| 679 |
|
| 680 |
-
|
| 681 |
-
Higher τ_eff makes decisions stricter:
|
| 682 |
\[
|
| 683 |
\text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
|
| 684 |
\]
|
| 685 |
-
|
| 686 |
-
That is exactly what I implement here: more uncertainty → higher τ_eff → harder gate → fewer low-confidence actions.
|
| 687 |
"""
|
| 688 |
|
| 689 |
INVESTOR_MD = """
|
| 690 |
-
# Investor / Agency Walkthrough
|
| 691 |
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
- alert filtering (NEO / tracking)
|
| 695 |
- stabilisation (jitter reduction)
|
| 696 |
-
- anomaly-aware control
|
| 697 |
-
|
| 698 |
-
This is a runnable harness:
|
| 699 |
-
- you can reproduce results with seeds
|
| 700 |
-
- you can export logs
|
| 701 |
-
- you can compare baseline vs RFT
|
| 702 |
-
- you can change thresholds and see behaviour shift
|
| 703 |
|
| 704 |
-
|
| 705 |
-
-
|
| 706 |
-
-
|
| 707 |
-
-
|
| 708 |
|
| 709 |
-
|
| 710 |
- real sensor ingestion + timing constraints
|
| 711 |
- hardware-in-loop testing
|
| 712 |
-
-
|
| 713 |
-
- integration targets (embedded, REST, batch)
|
| 714 |
"""
|
| 715 |
|
| 716 |
REPRO_MD = """
|
| 717 |
# Reproducibility & Logs
|
| 718 |
|
| 719 |
-
Everything
|
| 720 |
-
- set
|
| 721 |
-
- run
|
| 722 |
-
- export
|
| 723 |
-
- verify plots
|
| 724 |
-
|
| 725 |
-
CSV schema is explicit in the exports
|
| 726 |
-
- time index
|
| 727 |
-
- state values
|
| 728 |
-
- uncertainty, confidence, τ_eff
|
| 729 |
-
- alerts/actions flags
|
| 730 |
"""
|
| 731 |
|
| 732 |
# -----------------------------
|
|
@@ -776,6 +1115,40 @@ def ui_run_landing(seed, steps, dt, wind_max, thrust_noise, kp_base, kp_rft, gat
|
|
| 776 |
summary_txt = json.dumps(summary, indent=2)
|
| 777 |
return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
|
| 778 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 779 |
def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain):
|
| 780 |
txt, score_df, score_csv, imgs, logs = run_benchmarks(
|
| 781 |
seed=int(seed),
|
|
@@ -784,7 +1157,6 @@ def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps,
|
|
| 784 |
land_steps=int(land_steps), land_dt=float(land_dt), land_wind=float(land_wind), land_thrust_noise=float(land_thrust_noise),
|
| 785 |
tau_gain=float(tau_gain)
|
| 786 |
)
|
| 787 |
-
# IMPORTANT: 16 outputs expected. We return 10 images + 3 files + 3 objects = 16.
|
| 788 |
return (
|
| 789 |
txt, score_df, score_csv,
|
| 790 |
imgs[0], imgs[1], imgs[2], imgs[3], imgs[4], imgs[5], imgs[6], imgs[7], imgs[8], imgs[9],
|
|
@@ -794,7 +1166,7 @@ def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps,
|
|
| 794 |
# -----------------------------
|
| 795 |
# Gradio UI
|
| 796 |
# -----------------------------
|
| 797 |
-
with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
|
| 798 |
gr.Markdown(HOME_MD)
|
| 799 |
|
| 800 |
with gr.Tabs():
|
|
@@ -862,7 +1234,6 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
|
|
| 862 |
with gr.Tab("NEO Agent"):
|
| 863 |
gr.Markdown(
|
| 864 |
"# Near-Earth Object (NEO) Alerting Agent\n"
|
| 865 |
-
"This is a test harness for filtering close-approach alerts under noise.\n"
|
| 866 |
"Baseline: distance threshold only.\n"
|
| 867 |
"RFT: distance threshold + confidence + τ_eff collapse gate.\n"
|
| 868 |
)
|
|
@@ -898,7 +1269,6 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
|
|
| 898 |
"# Satellite Jitter Reduction\n"
|
| 899 |
"Baseline: continuous correction.\n"
|
| 900 |
"RFT: gated correction using confidence + τ_eff.\n"
|
| 901 |
-
"This is a simple but honest test of duty-cycle reduction.\n"
|
| 902 |
)
|
| 903 |
with gr.Row():
|
| 904 |
seed_j = gr.Number(value=42, precision=0, label="Seed")
|
|
@@ -930,7 +1300,6 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
|
|
| 930 |
gr.Markdown(
|
| 931 |
"# Starship-style Landing Harness (Simplified)\n"
|
| 932 |
"This is not a flight model. It’s a timing-control harness.\n"
|
| 933 |
-
"Baseline vs RFT shows whether gated decision timing can reduce waste and still hit the landing goal.\n"
|
| 934 |
)
|
| 935 |
with gr.Row():
|
| 936 |
seed_l = gr.Number(value=42, precision=0, label="Seed")
|
|
@@ -962,13 +1331,68 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
|
|
| 962 |
)
|
| 963 |
|
| 964 |
# ----------------------------------------------------------
|
| 965 |
-
with gr.Tab("
|
| 966 |
gr.Markdown(
|
| 967 |
-
"#
|
| 968 |
-
"
|
| 969 |
-
"
|
|
|
|
| 970 |
)
|
| 971 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 972 |
with gr.Tab("Theory → Practice"):
|
| 973 |
gr.Markdown(THEORY_PRACTICE_MD)
|
| 974 |
|
|
|
|
| 1 |
import os
|
| 2 |
import math
|
| 3 |
import json
|
| 4 |
+
import random
|
| 5 |
import numpy as np
|
| 6 |
import pandas as pd
|
| 7 |
import matplotlib.pyplot as plt
|
|
|
|
| 21 |
# Shared utilities
|
| 22 |
# -----------------------------
|
| 23 |
def set_seed(seed: int):
|
| 24 |
+
seed = int(seed) % (2**32 - 1)
|
| 25 |
+
np.random.seed(seed)
|
| 26 |
+
random.seed(seed)
|
| 27 |
|
| 28 |
def clamp(x, lo, hi):
|
| 29 |
return max(lo, min(hi, x))
|
|
|
|
| 52 |
"""
|
| 53 |
τ_eff is implemented here as a timing/decision delay modifier.
|
| 54 |
- base: baseline τ_eff
|
| 55 |
+
- slow_by: explicit slow-down term
|
| 56 |
- gain: reaction strength to uncertainty
|
| 57 |
- cap: prevents absurd values
|
| 58 |
"""
|
|
|
|
| 106 |
dist = float(np.linalg.norm(meas))
|
| 107 |
|
| 108 |
speed = float(np.linalg.norm(vel))
|
| 109 |
+
# uncertainty proxy: noise relative to alert radius + a mild speed term
|
| 110 |
uncertainty = clamp((noise_km / max(alert_km, 1.0)) * 2.0 + (speed / 200.0) * 0.2, 0.0, 1.0)
|
| 111 |
|
| 112 |
baseline_alert = dist <= alert_km
|
|
|
|
| 306 |
|
| 307 |
# -----------------------------
|
| 308 |
# Starship-style Landing Harness (2D)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
# -----------------------------
|
| 310 |
def simulate_landing(
|
| 311 |
seed: int,
|
|
|
|
| 327 |
x = 60.0
|
| 328 |
xv = 0.0
|
| 329 |
|
| 330 |
+
# Integral term helps remove persistent bias
|
| 331 |
ix = 0.0
|
| 332 |
|
| 333 |
anomalies = 0
|
|
|
|
| 335 |
ops_proxy = 0
|
| 336 |
rows = []
|
| 337 |
|
|
|
|
| 338 |
g = -9.81
|
| 339 |
|
| 340 |
+
LAT_CTRL = 0.95
|
| 341 |
+
WIND_PUSH = 0.28
|
| 342 |
+
VERT_CTRL = 0.22
|
|
|
|
| 343 |
|
|
|
|
| 344 |
OVERRIDE_X = 18.0
|
| 345 |
OVERRIDE_ALT = 260.0
|
| 346 |
|
| 347 |
for t in range(int(steps)):
|
|
|
|
| 348 |
gust = math.sin(0.08 * t) + 0.55 * math.sin(0.21 * t + 0.7)
|
| 349 |
wind = (wind_max * 0.75) * gust + np.random.normal(0.0, 0.65)
|
| 350 |
wind = clamp(wind, -wind_max, wind_max)
|
| 351 |
|
|
|
|
| 352 |
thrust_dev = np.random.normal(0.0, thrust_noise)
|
| 353 |
|
|
|
|
| 354 |
meas_alt = alt + np.random.normal(0, 0.6)
|
| 355 |
meas_vv = vv + np.random.normal(0, 0.35)
|
| 356 |
meas_x = x + np.random.normal(0, 0.8)
|
| 357 |
meas_xv = xv + np.random.normal(0, 0.25)
|
| 358 |
|
|
|
|
| 359 |
uncertainty = clamp((abs(thrust_dev) / 5.0) * 0.18 + (abs(wind) / max(wind_max, 1e-9)) * 0.30, 0.0, 1.0)
|
| 360 |
tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
|
| 361 |
conf = rft_confidence(uncertainty)
|
| 362 |
|
|
|
|
| 363 |
anomaly_types = []
|
| 364 |
if abs(wind) > (0.85 * wind_max):
|
| 365 |
anomaly_types.append("High wind")
|
|
|
|
| 371 |
if is_anomaly:
|
| 372 |
anomalies += 1
|
| 373 |
|
|
|
|
| 374 |
u_base_x = -kp_baseline * meas_x - 0.30 * meas_xv
|
| 375 |
u_base_v = -kp_baseline * (meas_vv + 5.0)
|
| 376 |
|
| 377 |
+
phase = 1.0 - clamp(meas_alt / 1000.0, 0.0, 1.0)
|
|
|
|
| 378 |
lookahead = 1.0 + 1.6 * phase
|
| 379 |
|
|
|
|
|
|
|
| 380 |
wind_ff = (WIND_PUSH * wind) / max(LAT_CTRL, 1e-9)
|
| 381 |
|
|
|
|
| 382 |
if meas_alt < 600:
|
| 383 |
ix = clamp(ix + (meas_x * dt) * 0.0025, -40.0, 40.0)
|
| 384 |
|
|
|
|
| 385 |
do_action = rft_gate(conf, tau, gate_threshold)
|
| 386 |
must_act = (abs(meas_x) > OVERRIDE_X) or (meas_alt < OVERRIDE_ALT)
|
| 387 |
do_action = bool(do_action or must_act)
|
|
|
|
| 389 |
u_rft_x = 0.0
|
| 390 |
u_rft_v = 0.0
|
| 391 |
if do_action:
|
|
|
|
| 392 |
u_rft_x = (-kp_rft * lookahead * meas_x) - (0.42 * meas_xv) - (0.20 * ix) - wind_ff
|
| 393 |
u_rft_v = (-kp_rft * lookahead * (meas_vv + 5.0))
|
| 394 |
actions += 1
|
| 395 |
|
|
|
|
| 396 |
u_rft_x = clamp(u_rft_x, -20.0, 20.0)
|
| 397 |
u_rft_v = clamp(u_rft_v, -18.0, 18.0)
|
| 398 |
|
|
|
|
| 399 |
vv = vv + (g + VERT_CTRL * u_rft_v + 0.09 * thrust_dev) * dt
|
| 400 |
alt = max(0.0, alt + vv * dt)
|
| 401 |
|
|
|
|
| 402 |
xv = xv + (WIND_PUSH * wind - LAT_CTRL * u_rft_x) * dt
|
| 403 |
x = x + xv * dt
|
| 404 |
|
|
|
|
| 482 |
|
| 483 |
return summary, [p_alt, p_x, p_w, p_a], csv_path
|
| 484 |
|
| 485 |
+
# ===============================================================
|
| 486 |
+
# Predator Avoidance (Reflex vs QuantumConscious "RFT-style")
|
| 487 |
+
# ===============================================================
|
| 488 |
+
|
| 489 |
+
def numpy_convolve2d_toroidal(array: np.ndarray, kernel: np.ndarray) -> np.ndarray:
|
| 490 |
+
out = np.zeros_like(array, dtype=float)
|
| 491 |
+
kcx = kernel.shape[0] // 2
|
| 492 |
+
kcy = kernel.shape[1] // 2
|
| 493 |
+
rows, cols = array.shape
|
| 494 |
+
for i in range(rows):
|
| 495 |
+
for j in range(cols):
|
| 496 |
+
val = 0.0
|
| 497 |
+
for m in range(kernel.shape[0]):
|
| 498 |
+
for n in range(kernel.shape[1]):
|
| 499 |
+
x = (i + m - kcx) % rows
|
| 500 |
+
y = (j + n - kcy) % cols
|
| 501 |
+
val += array[x, y] * kernel[m, n]
|
| 502 |
+
out[i, j] = val
|
| 503 |
+
return out
|
| 504 |
+
|
| 505 |
+
class Predator:
|
| 506 |
+
def __init__(self, grid_size: int):
|
| 507 |
+
self.grid_size = grid_size
|
| 508 |
+
self.x = random.randint(0, grid_size - 1)
|
| 509 |
+
self.y = random.randint(0, grid_size - 1)
|
| 510 |
+
|
| 511 |
+
def move(self):
|
| 512 |
+
dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
|
| 513 |
+
self.x = (self.x + dx) % self.grid_size
|
| 514 |
+
self.y = (self.y + dy) % self.grid_size
|
| 515 |
+
|
| 516 |
+
class ReflexAgent:
|
| 517 |
+
def __init__(self, grid_size: int):
|
| 518 |
+
self.grid_size = grid_size
|
| 519 |
+
self.x = random.randint(0, grid_size - 1)
|
| 520 |
+
self.y = random.randint(0, grid_size - 1)
|
| 521 |
+
self.collisions = 0
|
| 522 |
+
|
| 523 |
+
def move(self):
|
| 524 |
+
dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
|
| 525 |
+
self.x = (self.x + dx) % self.grid_size
|
| 526 |
+
self.y = (self.y + dy) % self.grid_size
|
| 527 |
+
|
| 528 |
+
class QuantumConsciousAgent:
|
| 529 |
+
def __init__(
|
| 530 |
+
self,
|
| 531 |
+
grid_size: int,
|
| 532 |
+
move_kernel: np.ndarray,
|
| 533 |
+
energy_max: float,
|
| 534 |
+
energy_regen: float,
|
| 535 |
+
base_override_cost: float,
|
| 536 |
+
quantum_boost_prob: float,
|
| 537 |
+
quantum_boost_amount: float,
|
| 538 |
+
sense_noise_prob: float,
|
| 539 |
+
alpha: float,
|
| 540 |
+
beta: float,
|
| 541 |
+
dt_internal: float,
|
| 542 |
+
override_threshold: float
|
| 543 |
+
):
|
| 544 |
+
self.grid_size = grid_size
|
| 545 |
+
self.move_kernel = move_kernel.astype(float)
|
| 546 |
+
|
| 547 |
+
# "probability field" + initial collapse
|
| 548 |
+
self.pos_prob = np.zeros((grid_size, grid_size), dtype=float)
|
| 549 |
+
x, y = np.random.randint(grid_size), np.random.randint(grid_size)
|
| 550 |
+
self.pos_prob[x, y] = 1.0
|
| 551 |
+
self.x, self.y = int(x), int(y)
|
| 552 |
+
|
| 553 |
+
# energy + override state
|
| 554 |
+
self.energy_max = float(energy_max)
|
| 555 |
+
self.energy = float(energy_max)
|
| 556 |
+
self.energy_regen = float(energy_regen)
|
| 557 |
+
self.base_override_cost = float(base_override_cost)
|
| 558 |
+
self.quantum_boost_prob = float(quantum_boost_prob)
|
| 559 |
+
self.quantum_boost_amount = float(quantum_boost_amount)
|
| 560 |
+
self.sense_noise_prob = float(sense_noise_prob)
|
| 561 |
+
|
| 562 |
+
self.alpha = float(alpha)
|
| 563 |
+
self.beta = float(beta)
|
| 564 |
+
self.dt_internal = float(dt_internal)
|
| 565 |
+
self.override_threshold = float(override_threshold)
|
| 566 |
+
|
| 567 |
+
self.psi_override = (0.1 + 0j)
|
| 568 |
+
self.overrides = 0
|
| 569 |
+
self.collisions = 0
|
| 570 |
+
|
| 571 |
+
def move(self):
|
| 572 |
+
dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
|
| 573 |
+
self.x = (self.x + dx) % self.grid_size
|
| 574 |
+
self.y = (self.y + dy) % self.grid_size
|
| 575 |
+
|
| 576 |
+
def sense_predators(self, predators):
|
| 577 |
+
perceived = []
|
| 578 |
+
for p in predators:
|
| 579 |
+
if random.random() < self.sense_noise_prob:
|
| 580 |
+
# drop this predator (sensor miss)
|
| 581 |
+
continue
|
| 582 |
+
perceived.append((p.x, p.y))
|
| 583 |
+
return perceived
|
| 584 |
+
|
| 585 |
+
def compute_threat(self, perceived):
|
| 586 |
+
# threat = probability mass near predators (radius window)
|
| 587 |
+
threat = 0.0
|
| 588 |
+
radius = 2
|
| 589 |
+
for (px, py) in perceived:
|
| 590 |
+
xs = [(px + dx) % self.grid_size for dx in range(-radius, radius + 1)]
|
| 591 |
+
ys = [(py + dy) % self.grid_size for dy in range(-radius, radius + 1)]
|
| 592 |
+
sub = self.pos_prob[np.ix_(xs, ys)]
|
| 593 |
+
threat += float(sub.sum())
|
| 594 |
+
return threat
|
| 595 |
+
|
| 596 |
+
def update_override_state(self, perceived):
|
| 597 |
+
T = self.compute_threat(perceived)
|
| 598 |
+
E = self.energy / max(self.energy_max, 1e-9)
|
| 599 |
+
# same structural shape as your PDF: H = alpha*T - beta*E + small recursion term
|
| 600 |
+
H = self.alpha * T - self.beta * E + 0.01 * (abs(self.psi_override) ** 2)
|
| 601 |
+
self.psi_override *= np.exp(-1j * H * self.dt_internal)
|
| 602 |
+
|
| 603 |
+
def get_override_probability(self):
|
| 604 |
+
return float(abs(self.psi_override) ** 2)
|
| 605 |
+
|
| 606 |
+
def apply_override(self, perceived):
|
| 607 |
+
# diffuse probability a little (kernel), then suppress regions near predators
|
| 608 |
+
field = numpy_convolve2d_toroidal(self.pos_prob, self.move_kernel)
|
| 609 |
+
field = np.maximum(field, 0.0)
|
| 610 |
+
|
| 611 |
+
# suppress predator neighborhoods
|
| 612 |
+
for (px, py) in perceived:
|
| 613 |
+
for dx in range(-2, 3):
|
| 614 |
+
for dy in range(-2, 3):
|
| 615 |
+
nx = (px + dx) % self.grid_size
|
| 616 |
+
ny = (py + dy) % self.grid_size
|
| 617 |
+
dist = abs(dx) + abs(dy)
|
| 618 |
+
field[nx, ny] *= (1.0 - 0.30 / (dist + 1.0))
|
| 619 |
+
|
| 620 |
+
s = float(field.sum())
|
| 621 |
+
if s <= 0:
|
| 622 |
+
# fallback to uniform
|
| 623 |
+
field[:] = 1.0 / (self.grid_size * self.grid_size)
|
| 624 |
+
else:
|
| 625 |
+
field /= s
|
| 626 |
+
|
| 627 |
+
self.pos_prob = field
|
| 628 |
+
|
| 629 |
+
# collapse away from predator cells explicitly
|
| 630 |
+
flat = self.pos_prob.flatten().copy()
|
| 631 |
+
for (px, py) in perceived:
|
| 632 |
+
flat[px * self.grid_size + py] = 0.0
|
| 633 |
+
|
| 634 |
+
tot = float(flat.sum())
|
| 635 |
+
if tot <= 0:
|
| 636 |
+
# if all got zeroed (rare), just random move
|
| 637 |
+
self.move()
|
| 638 |
+
return
|
| 639 |
+
|
| 640 |
+
flat /= tot
|
| 641 |
+
idx = np.random.choice(self.grid_size * self.grid_size, p=flat)
|
| 642 |
+
self.x, self.y = divmod(int(idx), self.grid_size)
|
| 643 |
+
|
| 644 |
+
def quantum_energy_boost(self):
|
| 645 |
+
if random.random() < self.quantum_boost_prob:
|
| 646 |
+
return float(self.quantum_boost_amount)
|
| 647 |
+
return 0.0
|
| 648 |
+
|
| 649 |
+
def regen_energy(self):
|
| 650 |
+
boost = self.quantum_energy_boost()
|
| 651 |
+
self.energy = clamp(self.energy + self.energy_regen + boost, 0.0, self.energy_max)
|
| 652 |
+
# occasional full regen (as in your runs)
|
| 653 |
+
if self.energy < self.energy_max and random.random() < 0.05:
|
| 654 |
+
self.energy = self.energy_max
|
| 655 |
+
|
| 656 |
+
def move_consciously(self, predators, group_coherence: float):
|
| 657 |
+
if self.energy <= 0:
|
| 658 |
+
self.move()
|
| 659 |
+
return 0, 0.0, 0.0 # acted?, P_ov, threat
|
| 660 |
+
|
| 661 |
+
perceived = self.sense_predators(predators)
|
| 662 |
+
self.update_override_state(perceived)
|
| 663 |
+
P_ov = self.get_override_probability()
|
| 664 |
+
threat = self.compute_threat(perceived)
|
| 665 |
+
|
| 666 |
+
acted = 0
|
| 667 |
+
if (P_ov > self.override_threshold) and (self.energy > 0):
|
| 668 |
+
effective_cost = self.base_override_cost * (1.0 - float(group_coherence))
|
| 669 |
+
if self.energy >= effective_cost:
|
| 670 |
+
self.overrides += 1
|
| 671 |
+
self.energy -= effective_cost
|
| 672 |
+
self.apply_override(perceived)
|
| 673 |
+
self.psi_override = (0.1 + 0j)
|
| 674 |
+
acted = 1
|
| 675 |
+
else:
|
| 676 |
+
self.move()
|
| 677 |
+
else:
|
| 678 |
+
self.move()
|
| 679 |
+
|
| 680 |
+
return acted, P_ov, threat
|
| 681 |
+
|
| 682 |
+
def simulate_predator(
|
| 683 |
+
seed: int,
|
| 684 |
+
grid_size: int,
|
| 685 |
+
steps: int,
|
| 686 |
+
num_reflex: int,
|
| 687 |
+
num_conscious: int,
|
| 688 |
+
num_predators: int,
|
| 689 |
+
group_coherence: float,
|
| 690 |
+
sense_noise_prob: float,
|
| 691 |
+
override_threshold: float,
|
| 692 |
+
alpha: float,
|
| 693 |
+
beta: float,
|
| 694 |
+
dt_internal: float,
|
| 695 |
+
energy_max: float,
|
| 696 |
+
base_override_cost: float,
|
| 697 |
+
energy_regen: float,
|
| 698 |
+
quantum_boost_prob: float,
|
| 699 |
+
quantum_boost_amount: float,
|
| 700 |
+
show_heatmap: bool
|
| 701 |
+
):
|
| 702 |
+
set_seed(seed)
|
| 703 |
+
|
| 704 |
+
move_kernel = np.array([[0, 0.2, 0],
|
| 705 |
+
[0.2, 0.2, 0.2],
|
| 706 |
+
[0, 0.2, 0]], dtype=float)
|
| 707 |
+
|
| 708 |
+
reflex_agents = [ReflexAgent(grid_size) for _ in range(int(num_reflex))]
|
| 709 |
+
conscious_agents = [
|
| 710 |
+
QuantumConsciousAgent(
|
| 711 |
+
grid_size=grid_size,
|
| 712 |
+
move_kernel=move_kernel,
|
| 713 |
+
energy_max=energy_max,
|
| 714 |
+
energy_regen=energy_regen,
|
| 715 |
+
base_override_cost=base_override_cost,
|
| 716 |
+
quantum_boost_prob=quantum_boost_prob,
|
| 717 |
+
quantum_boost_amount=quantum_boost_amount,
|
| 718 |
+
sense_noise_prob=sense_noise_prob,
|
| 719 |
+
alpha=alpha,
|
| 720 |
+
beta=beta,
|
| 721 |
+
dt_internal=dt_internal,
|
| 722 |
+
override_threshold=override_threshold
|
| 723 |
+
)
|
| 724 |
+
for _ in range(int(num_conscious))
|
| 725 |
+
]
|
| 726 |
+
predators = [Predator(grid_size) for _ in range(int(num_predators))]
|
| 727 |
+
|
| 728 |
+
reflex_collisions_cum = []
|
| 729 |
+
conscious_collisions_cum = []
|
| 730 |
+
conscious_overrides_avg = []
|
| 731 |
+
conscious_energy_avg = []
|
| 732 |
+
conscious_threat_avg = []
|
| 733 |
+
conscious_pov_avg = []
|
| 734 |
+
conscious_actions_avg = []
|
| 735 |
+
|
| 736 |
+
rows = []
|
| 737 |
+
ops_proxy = 0
|
| 738 |
+
|
| 739 |
+
for t in range(int(steps)):
|
| 740 |
+
# predators move first
|
| 741 |
+
for p in predators:
|
| 742 |
+
p.move()
|
| 743 |
+
|
| 744 |
+
# reflex move + collision check
|
| 745 |
+
for a in reflex_agents:
|
| 746 |
+
a.move()
|
| 747 |
+
for p in predators:
|
| 748 |
+
if a.x == p.x and a.y == p.y:
|
| 749 |
+
a.collisions += 1
|
| 750 |
+
|
| 751 |
+
# conscious move + collision check
|
| 752 |
+
actions = []
|
| 753 |
+
povs = []
|
| 754 |
+
threats = []
|
| 755 |
+
for a in conscious_agents:
|
| 756 |
+
acted, P_ov, threat = a.move_consciously(predators, group_coherence)
|
| 757 |
+
a.regen_energy()
|
| 758 |
+
actions.append(acted)
|
| 759 |
+
povs.append(P_ov)
|
| 760 |
+
threats.append(threat)
|
| 761 |
+
for p in predators:
|
| 762 |
+
if a.x == p.x and a.y == p.y:
|
| 763 |
+
a.collisions += 1
|
| 764 |
+
|
| 765 |
+
ops_proxy += 18
|
| 766 |
+
|
| 767 |
+
reflex_collisions = int(sum(a.collisions for a in reflex_agents))
|
| 768 |
+
conscious_collisions = int(sum(a.collisions for a in conscious_agents))
|
| 769 |
+
avg_overrides = float(np.mean([a.overrides for a in conscious_agents])) if conscious_agents else 0.0
|
| 770 |
+
avg_energy = float(np.mean([a.energy for a in conscious_agents])) if conscious_agents else 0.0
|
| 771 |
+
avg_threat = float(np.mean(threats)) if threats else 0.0
|
| 772 |
+
avg_pov = float(np.mean(povs)) if povs else 0.0
|
| 773 |
+
avg_act = float(np.mean(actions)) if actions else 0.0
|
| 774 |
+
|
| 775 |
+
reflex_collisions_cum.append(reflex_collisions)
|
| 776 |
+
conscious_collisions_cum.append(conscious_collisions)
|
| 777 |
+
conscious_overrides_avg.append(avg_overrides)
|
| 778 |
+
conscious_energy_avg.append(avg_energy)
|
| 779 |
+
conscious_threat_avg.append(avg_threat)
|
| 780 |
+
conscious_pov_avg.append(avg_pov)
|
| 781 |
+
conscious_actions_avg.append(avg_act)
|
| 782 |
+
|
| 783 |
+
# log row (one line per step)
|
| 784 |
+
rows.append({
|
| 785 |
+
"t": t,
|
| 786 |
+
"reflex_collisions_cum": reflex_collisions,
|
| 787 |
+
"conscious_collisions_cum": conscious_collisions,
|
| 788 |
+
"avg_conscious_overrides": avg_overrides,
|
| 789 |
+
"avg_conscious_energy": avg_energy,
|
| 790 |
+
"avg_conscious_threat": avg_threat,
|
| 791 |
+
"avg_conscious_P_override": avg_pov,
|
| 792 |
+
"avg_conscious_action": avg_act,
|
| 793 |
+
"predators_positions": "|".join([f"{p.x},{p.y}" for p in predators]),
|
| 794 |
+
})
|
| 795 |
+
|
| 796 |
+
df = pd.DataFrame(rows)
|
| 797 |
+
csv_path = df_to_csv_file(df, f"predator_log_seed{seed}.csv")
|
| 798 |
+
|
| 799 |
+
# Plot 1: collisions (cumulative)
|
| 800 |
+
fig1 = plt.figure(figsize=(10, 4))
|
| 801 |
+
ax = fig1.add_subplot(111)
|
| 802 |
+
ax.plot(df["t"], df["reflex_collisions_cum"], label="Reflex collisions (cum)")
|
| 803 |
+
ax.plot(df["t"], df["conscious_collisions_cum"], label="Conscious collisions (cum)")
|
| 804 |
+
ax.set_title("Predator Avoidance: Collisions (Reflex vs RFT)")
|
| 805 |
+
ax.set_xlabel("t (step)")
|
| 806 |
+
ax.set_ylabel("collisions (cum)")
|
| 807 |
+
ax.legend()
|
| 808 |
+
p_col = save_plot(fig1, f"predator_collisions_seed{seed}.png")
|
| 809 |
+
|
| 810 |
+
# Plot 2: overrides + energy
|
| 811 |
+
fig2 = plt.figure(figsize=(10, 4))
|
| 812 |
+
ax = fig2.add_subplot(111)
|
| 813 |
+
ax.plot(df["t"], df["avg_conscious_overrides"], label="Avg overrides (conscious)")
|
| 814 |
+
ax.plot(df["t"], df["avg_conscious_energy"], label="Avg energy (conscious)")
|
| 815 |
+
ax.set_title("Predator Avoidance: Overrides + Energy (Conscious)")
|
| 816 |
+
ax.set_xlabel("t (step)")
|
| 817 |
+
ax.set_ylabel("value")
|
| 818 |
+
ax.legend()
|
| 819 |
+
p_ov = save_plot(fig2, f"predator_overrides_energy_seed{seed}.png")
|
| 820 |
+
|
| 821 |
+
# Plot 3: threat + P_override + actions
|
| 822 |
+
fig3 = plt.figure(figsize=(10, 4))
|
| 823 |
+
ax = fig3.add_subplot(111)
|
| 824 |
+
ax.plot(df["t"], df["avg_conscious_threat"], label="Avg threat")
|
| 825 |
+
ax.plot(df["t"], df["avg_conscious_P_override"], label="Avg P_override")
|
| 826 |
+
ax.plot(df["t"], df["avg_conscious_action"], label="Avg action rate")
|
| 827 |
+
ax.set_title("Predator Avoidance: Threat vs Override Probability vs Action Rate")
|
| 828 |
+
ax.set_xlabel("t (step)")
|
| 829 |
+
ax.set_ylabel("value")
|
| 830 |
+
ax.legend()
|
| 831 |
+
p_thr = save_plot(fig3, f"predator_threat_seed{seed}.png")
|
| 832 |
+
|
| 833 |
+
heatmap_path = None
|
| 834 |
+
if show_heatmap and len(conscious_agents) > 0:
|
| 835 |
+
# show the final probability field of agent 0
|
| 836 |
+
field = conscious_agents[0].pos_prob
|
| 837 |
+
fig4 = plt.figure(figsize=(6, 5))
|
| 838 |
+
ax = fig4.add_subplot(111)
|
| 839 |
+
im = ax.imshow(field, aspect="auto")
|
| 840 |
+
ax.set_title("Conscious Agent[0]: Final probability field (pos_prob)")
|
| 841 |
+
ax.set_xlabel("y")
|
| 842 |
+
ax.set_ylabel("x")
|
| 843 |
+
fig4.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
|
| 844 |
+
heatmap_path = save_plot(fig4, f"predator_probfield_seed{seed}.png")
|
| 845 |
+
|
| 846 |
+
summary = {
|
| 847 |
+
"seed": int(seed),
|
| 848 |
+
"grid_size": int(grid_size),
|
| 849 |
+
"steps": int(steps),
|
| 850 |
+
"num_reflex": int(num_reflex),
|
| 851 |
+
"num_conscious": int(num_conscious),
|
| 852 |
+
"num_predators": int(num_predators),
|
| 853 |
+
"final_reflex_collisions": int(df["reflex_collisions_cum"].iloc[-1]) if len(df) else 0,
|
| 854 |
+
"final_conscious_collisions": int(df["conscious_collisions_cum"].iloc[-1]) if len(df) else 0,
|
| 855 |
+
"final_avg_conscious_overrides": float(df["avg_conscious_overrides"].iloc[-1]) if len(df) else 0.0,
|
| 856 |
+
"final_avg_conscious_energy": float(df["avg_conscious_energy"].iloc[-1]) if len(df) else 0.0,
|
| 857 |
+
"ops_proxy": int(ops_proxy),
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
imgs = [p_col, p_ov, p_thr]
|
| 861 |
+
if heatmap_path is not None:
|
| 862 |
+
imgs.append(heatmap_path)
|
| 863 |
+
|
| 864 |
+
return summary, imgs, csv_path
|
| 865 |
+
|
| 866 |
# -----------------------------
|
| 867 |
# Benchmarks
|
| 868 |
# -----------------------------
|
|
|
|
| 957 |
return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
|
| 958 |
|
| 959 |
# -----------------------------
|
| 960 |
+
# UI text blocks
|
| 961 |
# -----------------------------
|
| 962 |
HOME_MD = """
|
| 963 |
# Rendered Frame Theory (RFT) — Agent Console
|
| 964 |
|
| 965 |
+
This Space is meant to be transparent, reproducible, and benchmarkable.
|
| 966 |
|
| 967 |
+
Run it. Change parameters. Break it. Compare baseline vs RFT.
|
|
|
|
| 968 |
|
| 969 |
+
Core idea:
|
| 970 |
|
| 971 |
**Decision timing matters.**
|
| 972 |
RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
|
| 973 |
|
| 974 |
+
This Space contains:
|
| 975 |
+
- **NEO alerting**
|
| 976 |
+
- **Satellite jitter reduction**
|
| 977 |
+
- **Starship-style landing harness**
|
| 978 |
+
- **Predator avoidance** (Reflex vs RFT-style "QuantumConscious" agents)
|
|
|
|
| 979 |
|
| 980 |
+
No SciPy. No hidden dependencies. No model weights.
|
| 981 |
"""
|
| 982 |
|
| 983 |
LIVE_MD = """
|
| 984 |
# Live Console
|
| 985 |
|
| 986 |
+
Run everything quickly and export logs.
|
| 987 |
|
| 988 |
- deterministic runs (seeded)
|
| 989 |
- plots saved
|
|
|
|
| 994 |
THEORY_PRACTICE_MD = """
|
| 995 |
# Theory → Practice (how I implement RFT here)
|
| 996 |
|
| 997 |
+
## 1) Uncertainty
|
| 998 |
+
Explicit uncertainty proxy from noise + disturbance scale.
|
|
|
|
|
|
|
| 999 |
|
| 1000 |
## 2) Confidence
|
| 1001 |
+
confidence = 1 − uncertainty (clipped 0..1).
|
| 1002 |
|
| 1003 |
## 3) Adaptive τ_eff
|
| 1004 |
+
Higher uncertainty → higher τ_eff.
|
|
|
|
|
|
|
| 1005 |
|
| 1006 |
## 4) Collapse gate
|
| 1007 |
+
Act only when the gate passes:
|
| 1008 |
+
- confidence exceeds a threshold
|
| 1009 |
+
- τ_eff increases strictness under uncertainty
|
| 1010 |
|
| 1011 |
+
## 5) Why it matters
|
| 1012 |
Baseline controllers often act constantly.
|
| 1013 |
+
RFT tries to act less often, but more decisively.
|
| 1014 |
"""
|
| 1015 |
|
| 1016 |
MATH_MD = r"""
|
| 1017 |
# Mathematics (minimal and implementation-linked)
|
| 1018 |
|
| 1019 |
+
u ∈ [0,1] : uncertainty proxy
|
| 1020 |
+
C ∈ [0,1] : confidence proxy
|
| 1021 |
+
τ_eff ≥ 1 : effective decision timing factor
|
|
|
|
|
|
|
|
|
|
| 1022 |
|
| 1023 |
+
Confidence:
|
| 1024 |
\[
|
| 1025 |
C = \text{clip}(1 - u, 0, 1)
|
| 1026 |
\]
|
| 1027 |
|
| 1028 |
+
Adaptive τ_eff:
|
| 1029 |
\[
|
| 1030 |
\tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
|
| 1031 |
\]
|
| 1032 |
|
| 1033 |
+
Collapse gate (concept):
|
|
|
|
| 1034 |
\[
|
| 1035 |
\text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
|
| 1036 |
\]
|
|
|
|
|
|
|
| 1037 |
"""
|
| 1038 |
|
| 1039 |
INVESTOR_MD = """
|
| 1040 |
+
# Investor / Agency Walkthrough
|
| 1041 |
|
| 1042 |
+
What this Space demonstrates:
|
| 1043 |
+
- alert filtering (NEO)
|
|
|
|
| 1044 |
- stabilisation (jitter reduction)
|
| 1045 |
+
- anomaly-aware control (landing harness)
|
| 1046 |
+
- threat-aware avoidance (predator demo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1047 |
|
| 1048 |
+
What it is not:
|
| 1049 |
+
- not flight-certified
|
| 1050 |
+
- not a production pipeline
|
| 1051 |
+
- not a claim that anyone is using it
|
| 1052 |
|
| 1053 |
+
What makes it production-grade:
|
| 1054 |
- real sensor ingestion + timing constraints
|
| 1055 |
- hardware-in-loop testing
|
| 1056 |
+
- dataset validation
|
|
|
|
| 1057 |
"""
|
| 1058 |
|
| 1059 |
REPRO_MD = """
|
| 1060 |
# Reproducibility & Logs
|
| 1061 |
|
| 1062 |
+
Everything is reproducible:
|
| 1063 |
+
- set seed
|
| 1064 |
+
- run
|
| 1065 |
+
- export CSV
|
| 1066 |
+
- verify plots + metrics
|
| 1067 |
+
|
| 1068 |
+
CSV schema is explicit in the exports.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1069 |
"""
|
| 1070 |
|
| 1071 |
# -----------------------------
|
|
|
|
| 1115 |
summary_txt = json.dumps(summary, indent=2)
|
| 1116 |
return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
|
| 1117 |
|
| 1118 |
+
def ui_run_predator(seed, grid_size, steps, num_reflex, num_conscious, num_predators,
|
| 1119 |
+
group_coherence, sense_noise_prob, override_threshold,
|
| 1120 |
+
alpha, beta, dt_internal,
|
| 1121 |
+
energy_max, base_override_cost, energy_regen,
|
| 1122 |
+
quantum_boost_prob, quantum_boost_amount,
|
| 1123 |
+
show_heatmap):
|
| 1124 |
+
summary, imgs, csv_path = simulate_predator(
|
| 1125 |
+
seed=int(seed),
|
| 1126 |
+
grid_size=int(grid_size),
|
| 1127 |
+
steps=int(steps),
|
| 1128 |
+
num_reflex=int(num_reflex),
|
| 1129 |
+
num_conscious=int(num_conscious),
|
| 1130 |
+
num_predators=int(num_predators),
|
| 1131 |
+
group_coherence=float(group_coherence),
|
| 1132 |
+
sense_noise_prob=float(sense_noise_prob),
|
| 1133 |
+
override_threshold=float(override_threshold),
|
| 1134 |
+
alpha=float(alpha),
|
| 1135 |
+
beta=float(beta),
|
| 1136 |
+
dt_internal=float(dt_internal),
|
| 1137 |
+
energy_max=float(energy_max),
|
| 1138 |
+
base_override_cost=float(base_override_cost),
|
| 1139 |
+
energy_regen=float(energy_regen),
|
| 1140 |
+
quantum_boost_prob=float(quantum_boost_prob),
|
| 1141 |
+
quantum_boost_amount=float(quantum_boost_amount),
|
| 1142 |
+
show_heatmap=bool(show_heatmap)
|
| 1143 |
+
)
|
| 1144 |
+
summary_txt = json.dumps(summary, indent=2)
|
| 1145 |
+
# imgs: 3 or 4
|
| 1146 |
+
img1 = imgs[0] if len(imgs) > 0 else None
|
| 1147 |
+
img2 = imgs[1] if len(imgs) > 1 else None
|
| 1148 |
+
img3 = imgs[2] if len(imgs) > 2 else None
|
| 1149 |
+
img4 = imgs[3] if len(imgs) > 3 else None
|
| 1150 |
+
return summary_txt, img1, img2, img3, img4, csv_path
|
| 1151 |
+
|
| 1152 |
def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain):
|
| 1153 |
txt, score_df, score_csv, imgs, logs = run_benchmarks(
|
| 1154 |
seed=int(seed),
|
|
|
|
| 1157 |
land_steps=int(land_steps), land_dt=float(land_dt), land_wind=float(land_wind), land_thrust_noise=float(land_thrust_noise),
|
| 1158 |
tau_gain=float(tau_gain)
|
| 1159 |
)
|
|
|
|
| 1160 |
return (
|
| 1161 |
txt, score_df, score_csv,
|
| 1162 |
imgs[0], imgs[1], imgs[2], imgs[3], imgs[4], imgs[5], imgs[6], imgs[7], imgs[8], imgs[9],
|
|
|
|
| 1166 |
# -----------------------------
|
| 1167 |
# Gradio UI
|
| 1168 |
# -----------------------------
|
| 1169 |
+
with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)") as demo:
|
| 1170 |
gr.Markdown(HOME_MD)
|
| 1171 |
|
| 1172 |
with gr.Tabs():
|
|
|
|
| 1234 |
with gr.Tab("NEO Agent"):
|
| 1235 |
gr.Markdown(
|
| 1236 |
"# Near-Earth Object (NEO) Alerting Agent\n"
|
|
|
|
| 1237 |
"Baseline: distance threshold only.\n"
|
| 1238 |
"RFT: distance threshold + confidence + τ_eff collapse gate.\n"
|
| 1239 |
)
|
|
|
|
| 1269 |
"# Satellite Jitter Reduction\n"
|
| 1270 |
"Baseline: continuous correction.\n"
|
| 1271 |
"RFT: gated correction using confidence + τ_eff.\n"
|
|
|
|
| 1272 |
)
|
| 1273 |
with gr.Row():
|
| 1274 |
seed_j = gr.Number(value=42, precision=0, label="Seed")
|
|
|
|
| 1300 |
gr.Markdown(
|
| 1301 |
"# Starship-style Landing Harness (Simplified)\n"
|
| 1302 |
"This is not a flight model. It’s a timing-control harness.\n"
|
|
|
|
| 1303 |
)
|
| 1304 |
with gr.Row():
|
| 1305 |
seed_l = gr.Number(value=42, precision=0, label="Seed")
|
|
|
|
| 1331 |
)
|
| 1332 |
|
| 1333 |
# ----------------------------------------------------------
|
| 1334 |
+
with gr.Tab("Predator Avoidance"):
|
| 1335 |
gr.Markdown(
|
| 1336 |
+
"# Predator Avoidance (Reflex vs RFT)\n"
|
| 1337 |
+
"Grid world with roaming predators.\n"
|
| 1338 |
+
"Reflex agents: random walk.\n"
|
| 1339 |
+
"Conscious agents: probability field + threat-weighted override.\n"
|
| 1340 |
)
|
| 1341 |
|
| 1342 |
+
with gr.Row():
|
| 1343 |
+
seed_p = gr.Number(value=42, precision=0, label="Seed")
|
| 1344 |
+
grid_size = gr.Slider(10, 60, value=20, step=1, label="Grid size")
|
| 1345 |
+
steps_p = gr.Slider(50, 1500, value=200, step=1, label="Steps")
|
| 1346 |
+
|
| 1347 |
+
with gr.Row():
|
| 1348 |
+
num_reflex = gr.Slider(0, 50, value=10, step=1, label="Reflex agents")
|
| 1349 |
+
num_conscious = gr.Slider(0, 20, value=3, step=1, label="Conscious agents")
|
| 1350 |
+
num_predators = gr.Slider(1, 20, value=3, step=1, label="Predators")
|
| 1351 |
+
|
| 1352 |
+
with gr.Accordion("RFT / Agent parameters", open=True):
|
| 1353 |
+
with gr.Row():
|
| 1354 |
+
group_coherence = gr.Slider(0.0, 0.95, value=0.30, step=0.01, label="Group coherence")
|
| 1355 |
+
sense_noise_prob = gr.Slider(0.0, 0.9, value=0.10, step=0.01, label="Sense noise probability")
|
| 1356 |
+
override_threshold = gr.Slider(0.0, 1.0, value=0.01, step=0.005, label="Override threshold (P_ov)")
|
| 1357 |
+
|
| 1358 |
+
with gr.Row():
|
| 1359 |
+
alpha = gr.Slider(0.0, 50.0, value=15.0, step=0.5, label="alpha (threat gain)")
|
| 1360 |
+
beta = gr.Slider(0.0, 10.0, value=0.5, step=0.05, label="beta (energy term)")
|
| 1361 |
+
dt_internal = gr.Slider(0.01, 1.0, value=0.2, step=0.01, label="override dt")
|
| 1362 |
+
|
| 1363 |
+
with gr.Row():
|
| 1364 |
+
energy_max = gr.Slider(1.0, 300.0, value=100.0, step=1.0, label="Energy max")
|
| 1365 |
+
base_override_cost = gr.Slider(0.0, 10.0, value=1.0, step=0.1, label="Base override cost")
|
| 1366 |
+
energy_regen = gr.Slider(0.0, 1.0, value=0.05, step=0.01, label="Energy regen")
|
| 1367 |
+
|
| 1368 |
+
with gr.Row():
|
| 1369 |
+
quantum_boost_prob = gr.Slider(0.0, 1.0, value=0.10, step=0.01, label="Quantum boost probability")
|
| 1370 |
+
quantum_boost_amount = gr.Slider(0.0, 50.0, value=5.0, step=0.5, label="Quantum boost amount")
|
| 1371 |
+
show_heatmap = gr.Checkbox(value=True, label="Show probability field heatmap (agent[0])")
|
| 1372 |
+
|
| 1373 |
+
run_p = gr.Button("Run Predator Simulation")
|
| 1374 |
+
|
| 1375 |
+
out_p_summary = gr.Textbox(label="Summary JSON", lines=12)
|
| 1376 |
+
with gr.Row():
|
| 1377 |
+
out_p_img1 = gr.Image(label="Collisions (cumulative)")
|
| 1378 |
+
out_p_img2 = gr.Image(label="Overrides + Energy")
|
| 1379 |
+
with gr.Row():
|
| 1380 |
+
out_p_img3 = gr.Image(label="Threat / P_override / Action rate")
|
| 1381 |
+
out_p_img4 = gr.Image(label="Final probability field (optional)")
|
| 1382 |
+
out_p_csv = gr.File(label="Download Predator CSV log")
|
| 1383 |
+
|
| 1384 |
+
run_p.click(
|
| 1385 |
+
ui_run_predator,
|
| 1386 |
+
inputs=[seed_p, grid_size, steps_p, num_reflex, num_conscious, num_predators,
|
| 1387 |
+
group_coherence, sense_noise_prob, override_threshold,
|
| 1388 |
+
alpha, beta, dt_internal,
|
| 1389 |
+
energy_max, base_override_cost, energy_regen,
|
| 1390 |
+
quantum_boost_prob, quantum_boost_amount,
|
| 1391 |
+
show_heatmap],
|
| 1392 |
+
outputs=[out_p_summary, out_p_img1, out_p_img2, out_p_img3, out_p_img4, out_p_csv]
|
| 1393 |
+
)
|
| 1394 |
+
|
| 1395 |
+
# ----------------------------------------------------------
|
| 1396 |
with gr.Tab("Theory → Practice"):
|
| 1397 |
gr.Markdown(THEORY_PRACTICE_MD)
|
| 1398 |
|