RFTSystems commited on
Commit
f77622a
·
verified ·
1 Parent(s): 5217dd4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -167
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import time
3
  import math
4
  import json
5
  import numpy as np
@@ -40,16 +39,18 @@ def df_to_csv_file(df: pd.DataFrame, name: str):
40
  # -----------------------------
41
  # RFT Core: τ_eff + gating
42
  # -----------------------------
43
- def tau_eff_adaptive(uncertainty: float,
44
- base: float = 1.0,
45
- slow_by: float = 1.0,
46
- gain: float = 1.2,
47
- cap: float = 4.0):
 
 
48
  """
49
- τ_eff here is implemented as a timing/decision delay modifier.
50
  - base: baseline τ_eff
51
- - slow_by: explicit "slow by 1.0" style term (user requested this behaviour)
52
- - gain: how strongly τ_eff reacts to uncertainty
53
  - cap: prevents absurd values
54
  """
55
  u = clamp(float(uncertainty), 0.0, 1.0)
@@ -57,35 +58,34 @@ def tau_eff_adaptive(uncertainty: float,
57
  return clamp(tau, base, cap)
58
 
59
  def rft_confidence(uncertainty: float):
60
- # Confidence is the complement of uncertainty, clipped.
61
  return clamp(1.0 - float(uncertainty), 0.0, 1.0)
62
 
63
  def rft_gate(conf: float, tau_eff: float, threshold: float):
64
  """
65
  Collapse gate:
66
- - higher τ_eff makes gate stricter (forces more decisive conditions)
67
  - threshold is the minimum confidence needed
68
  """
69
  conf = float(conf)
70
  tau_eff = float(tau_eff)
71
- # stricter with larger tau: raise the effective threshold
72
  effective = threshold + 0.08 * (tau_eff - 1.0)
73
  return conf >= clamp(effective, 0.0, 0.999)
74
 
75
  # -----------------------------
76
  # NEO Simulation
77
  # -----------------------------
78
- def simulate_neo(seed: int,
79
- steps: int,
80
- dt: float,
81
- alert_km: float,
82
- noise_km: float,
83
- rft_conf_threshold: float,
84
- tau_gain: float,
85
- show_debug: bool):
 
 
86
  set_seed(seed)
87
 
88
- # Start far-ish but inside a range that can produce alerts
89
  pos = np.array([9000.0, 2500.0, 1000.0], dtype=float) # km
90
  vel = np.array([-55.0, -8.0, -3.0], dtype=float) # km/step (scaled)
91
 
@@ -96,29 +96,22 @@ def simulate_neo(seed: int,
96
  ops_proxy = 0
97
 
98
  for t in range(int(steps)):
99
- # Truth propagation (simple linear + drift)
100
  drift = 0.05 * np.array([math.sin(0.03*t), math.cos(0.02*t), math.sin(0.015*t)])
101
  pos_true = pos + vel * dt + drift
102
 
103
- # Measurement noise
104
  meas = pos_true + np.random.normal(0.0, noise_km, size=3)
105
-
106
- # Distance to origin (proxy for Earth)
107
  dist = float(np.linalg.norm(meas))
108
 
109
- # Uncertainty proxy: higher noise and higher speed increase uncertainty
110
  speed = float(np.linalg.norm(vel))
111
  uncertainty = clamp((noise_km / max(alert_km, 1.0)) * 2.0 + (speed / 200.0) * 0.2, 0.0, 1.0)
112
 
113
- # Baseline alert: if within radius
114
  baseline_alert = dist <= alert_km
115
  if baseline_alert:
116
  alerts_baseline += 1
117
 
118
- # RFT: τ_eff + confidence + gate (collapse earlier / smarter)
119
  tau = tau_eff_adaptive(uncertainty=uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
120
  conf = rft_confidence(uncertainty)
121
- # "RFT alert candidate" uses same geometric condition but *requires* collapse gate
122
  rft_candidate = dist <= alert_km
123
  rft_alert = bool(rft_candidate and rft_gate(conf, tau, rft_conf_threshold))
124
 
@@ -127,7 +120,7 @@ def simulate_neo(seed: int,
127
  if rft_alert:
128
  alerts_rft_filtered += 1
129
 
130
- ops_proxy += 12 # a stable "compute proxy" per step
131
 
132
  rows.append({
133
  "t": t,
@@ -149,7 +142,6 @@ def simulate_neo(seed: int,
149
 
150
  df = pd.DataFrame(rows)
151
 
152
- # Plots
153
  fig1 = plt.figure(figsize=(10, 4))
154
  ax = fig1.add_subplot(111)
155
  ax.plot(df["t"], df["dist_km"])
@@ -194,30 +186,27 @@ def simulate_neo(seed: int,
194
 
195
  debug_lines = ""
196
  if show_debug:
197
- debug_lines = (
198
- "Debug view (first 12 rows):\n"
199
- + df.head(12).to_string(index=False)
200
- )
201
 
202
  return summary, debug_lines, [p_dist, p_conf, p_alerts], csv_path
203
 
204
  # -----------------------------
205
  # Satellite Jitter Simulation
206
  # -----------------------------
207
- def simulate_jitter(seed: int,
208
- steps: int,
209
- dt: float,
210
- noise: float,
211
- baseline_kp: float,
212
- rft_kp: float,
213
- gate_threshold: float,
214
- tau_gain: float):
 
 
215
  set_seed(seed)
216
 
217
  jitter = 0.0
218
  jitter_rate = 0.0
219
- act_baseline = 0.0
220
- act_rft = 0.0
221
 
222
  rows = []
223
  duty_baseline = 0
@@ -225,17 +214,14 @@ def simulate_jitter(seed: int,
225
  ops_proxy = 0
226
 
227
  for t in range(int(steps)):
228
- # Jitter dynamics (random walk + periodic micro-vibe)
229
  micro = 0.25 * math.sin(0.05 * t) + 0.12 * math.sin(0.13 * t)
230
  jitter_rate += np.random.normal(0.0, noise) * 0.08
231
  jitter += jitter_rate * dt + micro + np.random.normal(0.0, noise)
232
 
233
- # Baseline: continuous correction
234
  u_base = -baseline_kp * jitter
235
  jitter_base_next = jitter + u_base * 0.35
236
  duty_baseline += int(abs(u_base) > 0.01)
237
 
238
- # RFT: correct only when it’s worth collapsing an action
239
  uncertainty = clamp(noise * 3.0, 0.0, 1.0)
240
  tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
241
  conf = rft_confidence(uncertainty)
@@ -245,18 +231,15 @@ def simulate_jitter(seed: int,
245
  jitter_rft_next = jitter + u_rft * 0.35
246
  duty_rft += int(abs(u_rft) > 0.01)
247
 
248
- act_baseline = u_base
249
- act_rft = u_rft
250
-
251
  ops_proxy += 10
252
 
253
  rows.append({
254
  "t": t,
255
  "jitter": jitter,
256
- "u_baseline": act_baseline,
257
- "u_rft": act_rft,
258
- "baseline_active": int(abs(act_baseline) > 0.01),
259
- "rft_active": int(abs(act_rft) > 0.01),
260
  "tau_eff": tau,
261
  "confidence": conf,
262
  "noise": noise,
@@ -264,13 +247,15 @@ def simulate_jitter(seed: int,
264
  "jitter_rft_next": jitter_rft_next,
265
  })
266
 
267
- # Update jitter state (common plant)
268
- jitter = jitter_rft_next # choose RFT plant evolution to reflect "running RFT"
269
  jitter_rate *= 0.92
270
 
271
  df = pd.DataFrame(rows)
272
 
273
- rms = lambda x: float(np.sqrt(np.mean(np.square(x))))
 
 
274
  jitter_rms = rms(df["jitter"].values)
275
  duty_b = duty_baseline / max(steps, 1)
276
  duty_r = duty_rft / max(steps, 1)
@@ -317,87 +302,123 @@ def simulate_jitter(seed: int,
317
 
318
  # -----------------------------
319
  # Starship-style Landing Harness (2D)
 
 
 
 
 
320
  # -----------------------------
321
- def simulate_landing(seed: int,
322
- steps: int,
323
- dt: float,
324
- wind_max: float,
325
- thrust_noise: float,
326
- kp_baseline: float,
327
- kp_rft: float,
328
- gate_threshold: float,
329
- tau_gain: float,
330
- goal_m: float):
 
 
331
  set_seed(seed)
332
 
333
- # state: altitude, vertical velocity, lateral x offset, lateral velocity
334
  alt = 1000.0
335
  vv = -45.0
336
  x = 60.0
337
  xv = 0.0
338
 
 
 
 
339
  anomalies = 0
340
  actions = 0
341
  ops_proxy = 0
342
-
343
  rows = []
344
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  for t in range(int(steps)):
346
- # wind profile
347
- wind = wind_max * (0.55 + 0.45 * math.sin(0.08 * t)) + np.random.normal(0, 0.4)
348
- wind = clamp(wind, 0.0, wind_max)
 
349
 
350
- # thrust disturbance
351
  thrust_dev = np.random.normal(0.0, thrust_noise)
352
 
353
- # measurement noise (simple)
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 proxy
360
- uncertainty = clamp((abs(thrust_dev) / 5.0) * 0.15 + (wind / max(wind_max, 1e-9)) * 0.25, 0.0, 1.0)
361
  tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
362
  conf = rft_confidence(uncertainty)
363
 
364
- # anomaly definition (reduced spam): only count if materially bad
365
  anomaly_types = []
366
- if wind > (0.85 * wind_max):
367
  anomaly_types.append("High wind")
368
  if meas_alt < 200 and abs(meas_x) > 20:
369
  anomaly_types.append("High lateral error near ground")
370
  if meas_alt < 150 and abs(meas_vv) > 15:
371
  anomaly_types.append("High descent rate near ground")
372
-
373
  is_anomaly = len(anomaly_types) > 0
374
  if is_anomaly:
375
  anomalies += 1
376
 
377
- # Baseline control: continuous proportional (computed for reference / logging)
378
- u_base_x = -kp_baseline * meas_x - 0.25 * meas_xv
379
- u_base_v = -kp_baseline * (meas_vv + 5.0) # target ~ -5 m/s
380
-
381
- # RFT control: gated “collapse” actions
382
- do_action = rft_gate(conf, tau, gate_threshold)
383
 
384
- # lookahead scaling makes RFT more decisive as altitude drops
385
  phase = 1.0 - clamp(meas_alt / 1000.0, 0.0, 1.0) # 0 high up, 1 near ground
386
- lookahead = 1.0 + 1.2 * phase
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
  u_rft_x = 0.0
389
  u_rft_v = 0.0
390
  if do_action:
391
- u_rft_x = (-kp_rft * lookahead * meas_x) - (0.30 * meas_xv)
 
392
  u_rft_v = (-kp_rft * lookahead * (meas_vv + 5.0))
393
  actions += 1
394
 
395
- # apply dynamics
396
- g = -9.81
397
- vv = vv + (g + 0.18 * u_rft_v + 0.08 * thrust_dev) * dt
 
 
 
398
  alt = max(0.0, alt + vv * dt)
399
 
400
- xv = xv + (0.35 * wind - 0.30 * u_rft_x) * dt
 
401
  x = x + xv * dt
402
 
403
  ops_proxy += 16
@@ -420,13 +441,14 @@ def simulate_landing(seed: int,
420
  "u_baseline_v": u_base_v,
421
  "u_rft_x": u_rft_x,
422
  "u_rft_v": u_rft_v,
 
 
423
  })
424
 
425
  if alt <= 0.0:
426
  break
427
 
428
  df = pd.DataFrame(rows)
429
-
430
  landing_offset = float(abs(df["x_m"].iloc[-1])) if len(df) else 9999.0
431
 
432
  fig1 = plt.figure(figsize=(10, 4))
@@ -450,7 +472,7 @@ def simulate_landing(seed: int,
450
  fig3 = plt.figure(figsize=(10, 4))
451
  ax = fig3.add_subplot(111)
452
  ax.plot(df["t"], df["wind_m_s"])
453
- ax.set_title("Landing: wind profile")
454
  ax.set_xlabel("t (step)")
455
  ax.set_ylabel("wind (m/s)")
456
  p_w = save_plot(fig3, f"landing_wind_seed{seed}.png")
@@ -482,22 +504,16 @@ def simulate_landing(seed: int,
482
  # -----------------------------
483
  # Benchmarks
484
  # -----------------------------
485
- def run_benchmarks(seed: int,
486
- neo_steps: int, neo_dt: float, neo_alert_km: float, neo_noise_km: float,
487
- jit_steps: int, jit_dt: float, jit_noise: float,
488
- land_steps: int, land_dt: float, land_wind: float, land_thrust_noise: float,
489
- tau_gain: float):
490
- """
491
- Benchmarks are baseline vs RFT under the SAME seed.
492
- Baseline here means:
493
- - NEO: geometric threshold only
494
- - Jitter: continuous correction (no gating)
495
- - Landing: continuous proportional (no gating)
496
- RFT means τ_eff + confidence + gate.
497
- """
498
  seed = int(seed)
499
 
500
- # NEO benchmark
501
  s_rft, _, neo_imgs, neo_csv = simulate_neo(
502
  seed=seed,
503
  steps=neo_steps,
@@ -513,7 +529,7 @@ def run_benchmarks(seed: int,
513
  neo_rft = int(neo_df["rft_alert"].sum())
514
  neo_candidates = int(neo_df["rft_candidate"].sum())
515
 
516
- # Jitter benchmark
517
  j_sum, jit_imgs, jit_csv = simulate_jitter(
518
  seed=seed,
519
  steps=jit_steps,
@@ -525,7 +541,7 @@ def run_benchmarks(seed: int,
525
  tau_gain=tau_gain
526
  )
527
 
528
- # Landing benchmark
529
  l_sum, land_imgs, land_csv = simulate_landing(
530
  seed=seed,
531
  steps=land_steps,
@@ -575,23 +591,23 @@ def run_benchmarks(seed: int,
575
  f"- Landing: final offset={l_sum['final_landing_offset_m']:.2f} m (goal 10 m), anomalies={l_sum['total_anomalies_detected']}, actions={l_sum['total_control_actions']}\n"
576
  )
577
 
578
- all_imgs = neo_imgs + jit_imgs + land_imgs # 3 + 3 + 4 = 10 images
579
  return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
580
 
581
  # -----------------------------
582
- # UI text blocks (full openness)
583
  # -----------------------------
584
  HOME_MD = """
585
  # Rendered Frame Theory (RFT) — Agent Console
586
 
587
  I built this Space to be transparent, reproducible, and benchmarkable.
588
 
589
- I’m not asking anyone to “believe” in anything here.
590
- Run it. Change the parameters. Break it. Compare baseline vs RFT.
591
 
592
  What I’m demonstrating is a practical idea:
593
 
594
- **Decision timing matters.**
595
  RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
596
 
597
  This Space contains three working agent harnesses:
@@ -621,16 +637,15 @@ THEORY_PRACTICE_MD = """
621
  This Space uses RFT in a practical way:
622
 
623
  ## 1) Uncertainty (explicit)
624
- I compute an uncertainty proxy from noise + disturbance scale.
625
- This is not magic. It’s just honest modelling.
626
 
627
  ## 2) Confidence
628
- Confidence is the complement: **confidence = 1 − uncertainty** (clipped 0..1).
629
 
630
  ## 3) Adaptive τ_eff
631
  τ_eff is implemented as a timing/decision strictness modifier:
632
  - higher uncertainty → higher τ_eff
633
- - **and yes, I explicitly slow τ_eff by 1.0**, because this was the target behaviour I wanted to test.
634
 
635
  ## 4) Collapse gate
636
  I only apply “decisive actions” when the gate condition passes:
@@ -639,21 +654,19 @@ I only apply “decisive actions” when the gate condition passes:
639
 
640
  ## 5) Why this matters
641
  Baseline controllers often act constantly.
642
- RFT tries to act **less often**, but **more decisively**, so you waste less energy and trigger fewer junk corrections/alerts.
643
  """
644
 
645
  MATH_MD = r"""
646
  # Mathematics (minimal and implementation-linked)
647
 
648
- I’m keeping this readable and tied to actual behaviour in code.
649
-
650
  ## Variables (used in this Space)
651
- - **u ∈ [0,1]** : uncertainty proxy (dimensionless)
652
- - **C ∈ [0,1]** : confidence proxy (dimensionless)
653
- - **τ_eff ≥ 1** : effective render/decision timing factor (dimensionless)
654
- - **Gate(C, τ_eff)** : action/alert collapse condition
655
 
656
  ## Definitions
 
657
  ### Confidence
658
  \[
659
  C = \text{clip}(1 - u, 0, 1)
@@ -663,16 +676,14 @@ C = \text{clip}(1 - u, 0, 1)
663
  \[
664
  \tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
665
  \]
666
- where \( g \) is a gain.
667
 
668
  ### Collapse gate (concept)
669
- A higher τ_eff makes the decision stricter:
670
  \[
671
  \text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
672
  \]
673
- where \( \theta \) is the base confidence threshold and \( k \) increases strictness with τ_eff.
674
 
675
- That’s exactly what I implement here: more uncertainty → higher τ_eff → harder gate → fewer low-confidence actions.
676
  """
677
 
678
  INVESTOR_MD = """
@@ -684,27 +695,22 @@ I’m demonstrating a decision-timing framework that can be applied to:
684
  - stabilisation (jitter reduction)
685
  - anomaly-aware control loops (landing harness)
686
 
687
- This is not a “pitch deck”. It’s a runnable harness:
688
- - you can reproduce the results with seeds
689
  - you can export logs
690
  - you can compare baseline vs RFT
691
  - you can change thresholds and see behaviour shift
692
 
693
- ## What I’m NOT claiming
694
  - I’m not claiming flight certification
695
- - I’m not claiming SpaceX is using this
696
  - I’m not claiming this replaces aerospace validation pipelines
697
 
698
- ## What would make this production-grade
699
  - real sensor ingestion + timing constraints
700
  - hardware-in-loop testing
701
  - systematic dataset validation
702
  - integration targets (embedded, REST, batch)
703
-
704
- If you want the “serious build”, I can package these modules as:
705
- - Python module
706
- - REST endpoint
707
- - edge builds (ARM)
708
  """
709
 
710
  REPRO_MD = """
@@ -724,7 +730,7 @@ CSV schema is explicit in the exports:
724
  """
725
 
726
  # -----------------------------
727
- # Gradio UI
728
  # -----------------------------
729
  def ui_run_neo(seed, steps, dt, alert_km, noise_km, rft_conf_th, tau_gain, show_debug):
730
  summary, debug_lines, imgs, csv_path = simulate_neo(
@@ -770,10 +776,7 @@ def ui_run_landing(seed, steps, dt, wind_max, thrust_noise, kp_base, kp_rft, gat
770
  summary_txt = json.dumps(summary, indent=2)
771
  return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
772
 
773
- def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km,
774
- jit_steps, jit_dt, jit_noise,
775
- land_steps, land_dt, land_wind, land_thrust_noise,
776
- tau_gain):
777
  txt, score_df, score_csv, imgs, logs = run_benchmarks(
778
  seed=int(seed),
779
  neo_steps=int(neo_steps), neo_dt=float(neo_dt), neo_alert_km=float(neo_alert_km), neo_noise_km=float(neo_noise_km),
@@ -781,24 +784,16 @@ def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km,
781
  land_steps=int(land_steps), land_dt=float(land_dt), land_wind=float(land_wind), land_thrust_noise=float(land_thrust_noise),
782
  tau_gain=float(tau_gain)
783
  )
784
-
785
- # IMPORTANT:
786
- # The Live Console Benchmarks tab has 16 outputs:
787
- # - 1 textbox, 1 dataframe, 1 file
788
- # - 10 images
789
- # - 3 log files
790
- # run_benchmarks returns 10 images (3 NEO + 3 jitter + 4 landing)
791
- # so we MUST return imgs[0]..imgs[9] here.
792
- while len(imgs) < 10:
793
- imgs.append(None)
794
-
795
  return (
796
  txt, score_df, score_csv,
797
- imgs[0], imgs[1], imgs[2], imgs[3], imgs[4],
798
- imgs[5], imgs[6], imgs[7], imgs[8], imgs[9],
799
  logs[0], logs[1], logs[2]
800
  )
801
 
 
 
 
802
  with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
803
  gr.Markdown(HOME_MD)
804
 
@@ -856,14 +851,21 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
856
  run_b.click(
857
  ui_run_bench,
858
  inputs=[seed_live, neo_steps, neo_dt, neo_alert, neo_noise, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain_live],
859
- outputs=[bench_txt, bench_table, bench_score_csv,
860
- img1, img2, img3, img4, img5, img6, img7, img8, img9, img10,
861
- neo_log, jit_log, land_log]
 
 
862
  )
863
 
864
  # ----------------------------------------------------------
865
  with gr.Tab("NEO Agent"):
866
- gr.Markdown("# Near-Earth Object (NEO) Alerting Agent\nThis is a test harness for filtering close-approach alerts under noise.\nBaseline: distance threshold only.\nRFT: distance threshold + confidence + τ_eff collapse gate.\n")
 
 
 
 
 
867
  with gr.Row():
868
  seed_neo = gr.Number(value=42, precision=0, label="Seed")
869
  steps_neo = gr.Slider(50, 400, value=120, step=1, label="Steps")
@@ -892,7 +894,12 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
892
 
893
  # ----------------------------------------------------------
894
  with gr.Tab("Satellite Jitter Agent"):
895
- gr.Markdown("# Satellite Jitter Reduction\nBaseline: continuous correction.\nRFT: gated correction using confidence + τ_eff.\nThis is a simple but honest test of duty-cycle reduction.\n")
 
 
 
 
 
896
  with gr.Row():
897
  seed_j = gr.Number(value=42, precision=0, label="Seed")
898
  steps_j = gr.Slider(100, 1200, value=500, step=1, label="Steps")
@@ -920,7 +927,11 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
920
 
921
  # ----------------------------------------------------------
922
  with gr.Tab("Starship Landing Harness"):
923
- gr.Markdown("# Starship-style Landing Harness (Simplified)\nThis is not a SpaceX flight model. It’s a timing-control harness.\nBaseline vs RFT shows whether gated decision timing can reduce waste and still hit the landing goal.\n")
 
 
 
 
924
  with gr.Row():
925
  seed_l = gr.Number(value=42, precision=0, label="Seed")
926
  steps_l = gr.Slider(40, 400, value=120, step=1, label="Steps")
@@ -952,9 +963,12 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
952
 
953
  # ----------------------------------------------------------
954
  with gr.Tab("Benchmarks"):
955
- gr.Markdown("# Benchmarks\nBaseline vs RFT runs, same seed, exported logs.\nUse Live Console to run full benchmark packs.\n")
 
 
 
 
956
 
957
- # ----------------------------------------------------------
958
  with gr.Tab("Theory → Practice"):
959
  gr.Markdown(THEORY_PRACTICE_MD)
960
 
 
1
  import os
 
2
  import math
3
  import json
4
  import numpy as np
 
39
  # -----------------------------
40
  # RFT Core: τ_eff + gating
41
  # -----------------------------
42
+ def tau_eff_adaptive(
43
+ uncertainty: float,
44
+ base: float = 1.0,
45
+ slow_by: float = 1.0,
46
+ gain: float = 1.2,
47
+ cap: float = 4.0
48
+ ):
49
  """
50
+ τ_eff is implemented here as a timing/decision delay modifier.
51
  - base: baseline τ_eff
52
+ - slow_by: explicit slow-down term (I wanted this behaviour: slow by 1.0)
53
+ - gain: reaction strength to uncertainty
54
  - cap: prevents absurd values
55
  """
56
  u = clamp(float(uncertainty), 0.0, 1.0)
 
58
  return clamp(tau, base, cap)
59
 
60
  def rft_confidence(uncertainty: float):
 
61
  return clamp(1.0 - float(uncertainty), 0.0, 1.0)
62
 
63
  def rft_gate(conf: float, tau_eff: float, threshold: float):
64
  """
65
  Collapse gate:
66
+ - higher τ_eff makes the gate stricter
67
  - threshold is the minimum confidence needed
68
  """
69
  conf = float(conf)
70
  tau_eff = float(tau_eff)
 
71
  effective = threshold + 0.08 * (tau_eff - 1.0)
72
  return conf >= clamp(effective, 0.0, 0.999)
73
 
74
  # -----------------------------
75
  # NEO Simulation
76
  # -----------------------------
77
+ def simulate_neo(
78
+ seed: int,
79
+ steps: int,
80
+ dt: float,
81
+ alert_km: float,
82
+ noise_km: float,
83
+ rft_conf_threshold: float,
84
+ tau_gain: float,
85
+ show_debug: bool
86
+ ):
87
  set_seed(seed)
88
 
 
89
  pos = np.array([9000.0, 2500.0, 1000.0], dtype=float) # km
90
  vel = np.array([-55.0, -8.0, -3.0], dtype=float) # km/step (scaled)
91
 
 
96
  ops_proxy = 0
97
 
98
  for t in range(int(steps)):
 
99
  drift = 0.05 * np.array([math.sin(0.03*t), math.cos(0.02*t), math.sin(0.015*t)])
100
  pos_true = pos + vel * dt + drift
101
 
 
102
  meas = pos_true + np.random.normal(0.0, noise_km, size=3)
 
 
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
109
  if baseline_alert:
110
  alerts_baseline += 1
111
 
 
112
  tau = tau_eff_adaptive(uncertainty=uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
113
  conf = rft_confidence(uncertainty)
114
+
115
  rft_candidate = dist <= alert_km
116
  rft_alert = bool(rft_candidate and rft_gate(conf, tau, rft_conf_threshold))
117
 
 
120
  if rft_alert:
121
  alerts_rft_filtered += 1
122
 
123
+ ops_proxy += 12
124
 
125
  rows.append({
126
  "t": t,
 
142
 
143
  df = pd.DataFrame(rows)
144
 
 
145
  fig1 = plt.figure(figsize=(10, 4))
146
  ax = fig1.add_subplot(111)
147
  ax.plot(df["t"], df["dist_km"])
 
186
 
187
  debug_lines = ""
188
  if show_debug:
189
+ debug_lines = "Debug view (first 12 rows):\n" + df.head(12).to_string(index=False)
 
 
 
190
 
191
  return summary, debug_lines, [p_dist, p_conf, p_alerts], csv_path
192
 
193
  # -----------------------------
194
  # Satellite Jitter Simulation
195
  # -----------------------------
196
+ def simulate_jitter(
197
+ seed: int,
198
+ steps: int,
199
+ dt: float,
200
+ noise: float,
201
+ baseline_kp: float,
202
+ rft_kp: float,
203
+ gate_threshold: float,
204
+ tau_gain: float
205
+ ):
206
  set_seed(seed)
207
 
208
  jitter = 0.0
209
  jitter_rate = 0.0
 
 
210
 
211
  rows = []
212
  duty_baseline = 0
 
214
  ops_proxy = 0
215
 
216
  for t in range(int(steps)):
 
217
  micro = 0.25 * math.sin(0.05 * t) + 0.12 * math.sin(0.13 * t)
218
  jitter_rate += np.random.normal(0.0, noise) * 0.08
219
  jitter += jitter_rate * dt + micro + np.random.normal(0.0, noise)
220
 
 
221
  u_base = -baseline_kp * jitter
222
  jitter_base_next = jitter + u_base * 0.35
223
  duty_baseline += int(abs(u_base) > 0.01)
224
 
 
225
  uncertainty = clamp(noise * 3.0, 0.0, 1.0)
226
  tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
227
  conf = rft_confidence(uncertainty)
 
231
  jitter_rft_next = jitter + u_rft * 0.35
232
  duty_rft += int(abs(u_rft) > 0.01)
233
 
 
 
 
234
  ops_proxy += 10
235
 
236
  rows.append({
237
  "t": t,
238
  "jitter": jitter,
239
+ "u_baseline": u_base,
240
+ "u_rft": u_rft,
241
+ "baseline_active": int(abs(u_base) > 0.01),
242
+ "rft_active": int(abs(u_rft) > 0.01),
243
  "tau_eff": tau,
244
  "confidence": conf,
245
  "noise": noise,
 
247
  "jitter_rft_next": jitter_rft_next,
248
  })
249
 
250
+ # Run the plant under RFT to reflect "using the RFT agent"
251
+ jitter = jitter_rft_next
252
  jitter_rate *= 0.92
253
 
254
  df = pd.DataFrame(rows)
255
 
256
+ def rms(x):
257
+ return float(np.sqrt(np.mean(np.square(np.asarray(x)))))
258
+
259
  jitter_rms = rms(df["jitter"].values)
260
  duty_b = duty_baseline / max(steps, 1)
261
  duty_r = duty_rft / max(steps, 1)
 
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,
313
+ steps: int,
314
+ dt: float,
315
+ wind_max: float,
316
+ thrust_noise: float,
317
+ kp_baseline: float,
318
+ kp_rft: float,
319
+ gate_threshold: float,
320
+ tau_gain: float,
321
+ goal_m: float
322
+ ):
323
  set_seed(seed)
324
 
325
+ # State
326
  alt = 1000.0
327
  vv = -45.0
328
  x = 60.0
329
  xv = 0.0
330
 
331
+ # A tiny integral term helps remove persistent bias
332
+ ix = 0.0
333
+
334
  anomalies = 0
335
  actions = 0
336
  ops_proxy = 0
 
337
  rows = []
338
 
339
+ # Tuned plant constants (simple, but consistent)
340
+ g = -9.81
341
+
342
+ # Control authority (this is what makes 10m achievable)
343
+ LAT_CTRL = 0.95 # lateral accel per control unit
344
+ WIND_PUSH = 0.28 # lateral accel per m/s wind
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")
375
  if meas_alt < 200 and abs(meas_x) > 20:
376
  anomaly_types.append("High lateral error near ground")
377
  if meas_alt < 150 and abs(meas_vv) > 15:
378
  anomaly_types.append("High descent rate near ground")
 
379
  is_anomaly = len(anomaly_types) > 0
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
+ # RFT control (gated + override)
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)
403
 
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
 
424
  ops_proxy += 16
 
441
  "u_baseline_v": u_base_v,
442
  "u_rft_x": u_rft_x,
443
  "u_rft_v": u_rft_v,
444
+ "ix": ix,
445
+ "wind_ff": wind_ff,
446
  })
447
 
448
  if alt <= 0.0:
449
  break
450
 
451
  df = pd.DataFrame(rows)
 
452
  landing_offset = float(abs(df["x_m"].iloc[-1])) if len(df) else 9999.0
453
 
454
  fig1 = plt.figure(figsize=(10, 4))
 
472
  fig3 = plt.figure(figsize=(10, 4))
473
  ax = fig3.add_subplot(111)
474
  ax.plot(df["t"], df["wind_m_s"])
475
+ ax.set_title("Landing: wind profile (signed gusts)")
476
  ax.set_xlabel("t (step)")
477
  ax.set_ylabel("wind (m/s)")
478
  p_w = save_plot(fig3, f"landing_wind_seed{seed}.png")
 
504
  # -----------------------------
505
  # Benchmarks
506
  # -----------------------------
507
+ def run_benchmarks(
508
+ seed: int,
509
+ neo_steps: int, neo_dt: float, neo_alert_km: float, neo_noise_km: float,
510
+ jit_steps: int, jit_dt: float, jit_noise: float,
511
+ land_steps: int, land_dt: float, land_wind: float, land_thrust_noise: float,
512
+ tau_gain: float
513
+ ):
 
 
 
 
 
 
514
  seed = int(seed)
515
 
516
+ # NEO
517
  s_rft, _, neo_imgs, neo_csv = simulate_neo(
518
  seed=seed,
519
  steps=neo_steps,
 
529
  neo_rft = int(neo_df["rft_alert"].sum())
530
  neo_candidates = int(neo_df["rft_candidate"].sum())
531
 
532
+ # Jitter
533
  j_sum, jit_imgs, jit_csv = simulate_jitter(
534
  seed=seed,
535
  steps=jit_steps,
 
541
  tau_gain=tau_gain
542
  )
543
 
544
+ # Landing
545
  l_sum, land_imgs, land_csv = simulate_landing(
546
  seed=seed,
547
  steps=land_steps,
 
591
  f"- Landing: final offset={l_sum['final_landing_offset_m']:.2f} m (goal 10 m), anomalies={l_sum['total_anomalies_detected']}, actions={l_sum['total_control_actions']}\n"
592
  )
593
 
594
+ all_imgs = neo_imgs + jit_imgs + land_imgs # 3 + 3 + 4 = 10
595
  return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
596
 
597
  # -----------------------------
598
+ # UI text blocks (my voice, full openness)
599
  # -----------------------------
600
  HOME_MD = """
601
  # Rendered Frame Theory (RFT) — Agent Console
602
 
603
  I built this Space to be transparent, reproducible, and benchmarkable.
604
 
605
+ I’m not asking anyone to “believe” in anything here.
606
+ Run it. Change the parameters. Break it. Compare baseline vs RFT.
607
 
608
  What I’m demonstrating is a practical idea:
609
 
610
+ **Decision timing matters.**
611
  RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
612
 
613
  This Space contains three working agent harnesses:
 
637
  This Space uses RFT in a practical way:
638
 
639
  ## 1) Uncertainty (explicit)
640
+ I compute an uncertainty proxy from noise + disturbance scale.
 
641
 
642
  ## 2) Confidence
643
+ Confidence is the complement: confidence = 1 − uncertainty (clipped 0..1).
644
 
645
  ## 3) Adaptive τ_eff
646
  τ_eff is implemented as a timing/decision strictness modifier:
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
  I only apply “decisive actions” when the gate condition passes:
 
654
 
655
  ## 5) Why this matters
656
  Baseline controllers often act constantly.
657
+ RFT tries to act less often, but more decisively, so you waste less energy and trigger fewer junk corrections/alerts.
658
  """
659
 
660
  MATH_MD = r"""
661
  # Mathematics (minimal and implementation-linked)
662
 
 
 
663
  ## Variables (used in this Space)
664
+ - u ∈ [0,1] : uncertainty proxy (dimensionless)
665
+ - C ∈ [0,1] : confidence proxy (dimensionless)
666
+ - τ_eff ≥ 1 : effective render/decision timing factor (dimensionless)
 
667
 
668
  ## Definitions
669
+
670
  ### Confidence
671
  \[
672
  C = \text{clip}(1 - u, 0, 1)
 
676
  \[
677
  \tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
678
  \]
 
679
 
680
  ### Collapse gate (concept)
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 = """
 
695
  - stabilisation (jitter reduction)
696
  - anomaly-aware control loops (landing harness)
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
+ ## What I’m not claiming
705
  - I’m not claiming flight certification
706
+ - I’m not claiming any company is using this
707
  - I’m not claiming this replaces aerospace validation pipelines
708
 
709
+ ## What would make it production-grade
710
  - real sensor ingestion + timing constraints
711
  - hardware-in-loop testing
712
  - systematic dataset validation
713
  - integration targets (embedded, REST, batch)
 
 
 
 
 
714
  """
715
 
716
  REPRO_MD = """
 
730
  """
731
 
732
  # -----------------------------
733
+ # Gradio UI helpers
734
  # -----------------------------
735
  def ui_run_neo(seed, steps, dt, alert_km, noise_km, rft_conf_th, tau_gain, show_debug):
736
  summary, debug_lines, imgs, csv_path = simulate_neo(
 
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),
782
  neo_steps=int(neo_steps), neo_dt=float(neo_dt), neo_alert_km=float(neo_alert_km), neo_noise_km=float(neo_noise_km),
 
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],
 
791
  logs[0], logs[1], logs[2]
792
  )
793
 
794
+ # -----------------------------
795
+ # Gradio UI
796
+ # -----------------------------
797
  with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
798
  gr.Markdown(HOME_MD)
799
 
 
851
  run_b.click(
852
  ui_run_bench,
853
  inputs=[seed_live, neo_steps, neo_dt, neo_alert, neo_noise, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain_live],
854
+ outputs=[
855
+ bench_txt, bench_table, bench_score_csv,
856
+ img1, img2, img3, img4, img5, img6, img7, img8, img9, img10,
857
+ neo_log, jit_log, land_log
858
+ ]
859
  )
860
 
861
  # ----------------------------------------------------------
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
+ )
869
  with gr.Row():
870
  seed_neo = gr.Number(value=42, precision=0, label="Seed")
871
  steps_neo = gr.Slider(50, 400, value=120, step=1, label="Steps")
 
894
 
895
  # ----------------------------------------------------------
896
  with gr.Tab("Satellite Jitter Agent"):
897
+ gr.Markdown(
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")
905
  steps_j = gr.Slider(100, 1200, value=500, step=1, label="Steps")
 
927
 
928
  # ----------------------------------------------------------
929
  with gr.Tab("Starship Landing Harness"):
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")
937
  steps_l = gr.Slider(40, 400, value=120, step=1, label="Steps")
 
963
 
964
  # ----------------------------------------------------------
965
  with gr.Tab("Benchmarks"):
966
+ gr.Markdown(
967
+ "# Benchmarks\n"
968
+ "Run full packs from the Live Console tab.\n"
969
+ "Everything is seeded, logged, and exportable.\n"
970
+ )
971
 
 
972
  with gr.Tab("Theory → Practice"):
973
  gr.Markdown(THEORY_PRACTICE_MD)
974