Skip to content

Validierung: Redundanz-gesicherte Flood-Suppression (Stufe B)

Simulative Absicherung des 5-Guard-Designs (Suppression_Design.md) auf der ECHTEN Mesh-Topologie, VOR der Firmware-Codierung. Skript: suppression_sim.py (Seed 42, 6 Seeds, 120 Paare, reproduzierbar). Daten: suppression_results.json.


Aufbau (kurz)

  • Topologie wie v4: reale Kanten aus neighbor_graph.json, ambiguous-Kanten verworfen (173), Link-Reliability logistisch aus echtem avg_snr. Kern: 1034 Knoten / 1783 Kanten, Ø-Grad 3,45 (sparse). Simulation auf der Riesenkomponente: 632 Knoten / 1577 Kanten.
  • Flood-Modell wie v4: timing-getrieben, first-packet-wins-Dedup, Airtime = Anzahl tatsächlich sendender Knoten je Zustellung, hop-gewichtetes Delay für MHR-Knoten.
  • Suppression = lokale Regel eines MHR-Knotens R: schweigt vor seinem Rebroadcast NUR, wenn ALLE aktiven Guards erfüllt sind (sonst senden = exakt wie Upstream). Cover-Sender = Nachbarn von R, die die Kopie P im Modell vor/gleichzeitig mit R gesendet haben; G3 nutzt die (ggf. unvollständige) Graph-Adjazenz als gelerntes 2-Hop-Wissen.
  • Rollout top_traffic (Hub-Knoten adoptieren zuerst) = konservativster Fall: genau die hochgradigen, last-tragenden Knoten dürfen am ehesten schweigen.
  • Baseline (Stock-Flood, kein Suppress): Lieferquote 0,9292, Airtime 616,85. Rausch-Band (2·SEM, min): Lieferquote ±0,0165, Airtime ±3,08.

Safety-Invariante je Konfig: Lieferquote ≥ Baseline − Tol UND Airtime ≤ Baseline + Tol.


EXP 1 — Safety-Sweep über Adoption (Default-Params d4/k2/snr−6/p0.8)

Guarded (G1–G5) vs. naive (nur G2) vs. Baseline:

α Guarded deliv (Δ) Guarded air% safe Naiv deliv (Δ) Naiv air% safe
1 Knoten 0,9514 (+0,022) −0,1 OK 0,9431 (+0,014) −1,1 OK
0,05 0,9611 (+0,032) −0,1 OK 0,7944 (−0,135) −21,0 X
0,10 0,9472 (+0,018) −0,9 OK 0,7208 (−0,208) −30,8 X
0,25 0,9361 (+0,007) −3,0 OK 0,6056 (−0,324) −45,8 X
0,50 0,9431 (+0,014) −8,5 OK 0,6181 (−0,311) −52,4 X
1,00 0,9611 (+0,032) −8,3 OK 0,6125 (−0,317) −53,4 X

Befund: Die guarded-Variante hält die Invariante bei allen α — Lieferquote bleibt durchgehend über Baseline (+0,7 … +3,2 Prozentpunkte), Airtime sinkt mit steigender Adoption (bis −8,5 % bei Default-Prob 0,8). Die naive Suppression bricht — wie im v4-Befund — schon ab α = 0,05 (Lieferquote −13 … −32 Prozentpunkte). Das reproduziert das M3/M4-Versagen exakt und zeigt den Mehrwert der Schutzschichten. → Plot fig_supp_safety_sweep.png.


EXP 2 — Parameter-Sweep (sicherer Sweet-Spot)

Alle 18 getesteten Konfigs (k_cover∈{2,3}, min_degree∈{3,4,5}, prob∈{0.6,0.8,1.0}, snr_floor −6) halten die Invariante über alle α (all_safe = True). Der Airtime-Gewinn ist erwartungsgemäß bei niedriger Adoption ~0 (korrekt — das Stock-Netz flutet ohnehin voll, da gibt es nichts zu sparen) und steigt bei hoher Adoption an. Maßgeblich ist der Gewinn dort, wo der Mechanismus wirken soll:

Config air-Gewinn α≥0,5 air-Gewinn α=1,0 schlechteste Lieferquote-Δ
k2 d3 p1.0 (Sweet-Spot) +14,6 % +15,4 % −0,0056 (im Rauschband)
k2 d3 p0.8 +11,8 % +12,4 % +0,0069 (immer ≥ Baseline)
k2 d4 p1.0 +10,6 % +10,5 % +0,0111
k3 d3 p1.0 +10,1 % +10,1 % +0,0111
k2 d3 p0.6 +9,0 % +9,5 % +0,0097

Tendenzen: kleineres min_degree und höheres prob sparen mehr Airtime (mehr Knoten qualifizieren / schweigen häufiger), größeres k_cover ist konservativer (weniger Gewinn, mehr Marge). → Plot fig_supp_param_sweep.png.

Sicherer Sweet-Spot (Empfehlung): k_cover=2, min_degree=3, snr_floor=−6, prob=0.8. Diese Konfig spart +11,8 % Airtime bei α≥0,5 (+12,4 % bei α=1,0) und hält die Lieferquote an jedem α strikt ≥ Baseline (schlechteste Δ = +0,0069, kein Rauschband nötig). Die Variante prob=1.0 spart mehr (+15,4 % bei α=1,0), ihre schlechteste Lieferquote-Δ ist aber knapp negativ (−0,0056, innerhalb ±0,0165-Rauschband) — als Max-Airtime-Variante zulässig, aber p=0.8 ist die robustere Produktionswahl (G5 lässt bewusst einen Bruchteil immer senden).


EXP 3 — Ablation: ist G3 load-bearing?

Default-Params, Guards schrittweise zugeschaltet:

Variante all_safe schlechteste Lieferquote-Δ Ø Airtime-Gewinn
nur G2 NEIN −0,324 (Bruch wie naiv) +34,1 %
G2+G3 JA +0,0167 +7,2 %
G2+G3+G1 JA +0,0083 +4,7 %
alle (G1–G5) JA +0,0069 +3,5 %

Befund: G3 (Neighbour-Coverage) ist eindeutig die load-bearing Schicht. Ohne G3 (nur G2) bricht die Lieferquote um bis zu −32 Prozentpunkte — exakt das v4-„einziger-Blatt-Pfad"-Versagen. Allein das Hinzuschalten von G3 kippt das System von „bricht" zu „hält" (schlechteste Δ von −0,324 auf +0,017). G1/G4/G5 fügen danach nur noch zusätzliche, billige Sicherheitsmarge hinzu (und kosten etwas Airtime-Gewinn). → Plot fig_supp_ablation.png.


EXP 4 — Imperfektes 2-Hop-Wissen (Realismus)

Sweet-Spot-Params, G3 nutzt nur einen Anteil der echten Nachbar-Infos (lückenhaftes passives Lernen frischer Firmware):

2-Hop-Wissen all_safe schlechteste Lieferquote-Δ Ø Airtime-Gewinn
60 % JA −0,0083 (im Rauschband) +6,6 %
80 % JA −0,0014 (im Rauschband) +6,1 %
100 % JA −0,0056 (im Rauschband) +5,7 %

Befund: Die Invariante hält auch bei nur 60 % bekannter Nachbarschaft. Unvollständiges 2-Hop-Wissen macht G3 strenger (unbekannte Nachbarn gelten als nicht abgedeckt → R sendet im Zweifel), nicht laxer — die Lücke wirkt konservativ, nicht gefährlich. Zusätzlich greift G1 als degree-Fallback. Die schlechtesten Lieferquote-Abweichungen liegen durchweg im Rauschband (±0,0165). → Plot fig_supp_imperfect_knowledge.png.


EXP 5 — Stress (Churn + Linkausfall) bei α=1,0

Guarded (Sweet-Spot) gegen Baseline unter derselben Störung:

Szenario Baseline deliv Guarded deliv (Δ) Guarded air% safe
Churn (nach advert_count) 0,8896 0,8880 (−0,0015) −13,6 OK
Linkausfall 10 % 0,8639 0,8625 (−0,0014) −13,8 OK
Linkausfall 20 % 0,7750 0,7583 (−0,0167) −12,4 knapp X
Churn + Linkausfall 20 % 0,6610 0,6564 (−0,0046) −11,7 OK

Befund: Bei Churn, Linkausfall 10 % und der kombinierten Hartstörung hält die Invariante (Lieferquote ≈ Baseline, Airtime −12 … −14 %). Bei Linkausfall 20 % verfehlt sie das Rauschband knapp: −0,0167 vs. Toleranz ±0,0165 — eine Lieferquote-Abweichung von −1,7 Prozentpunkten, also am Rand der statistischen Auflösung, nicht ein struktureller Einbruch (Airtime bleibt −12,4 %). Ursache: bei massivem Linkausfall reißen reale Cover-Pfade weg, die G3 in seiner gelernten (statisch angenommenen) Adjazenz noch für intakt hält → vereinzelt schweigt ein Knoten, dessen Cover-Sender real nicht mehr durchkommt. → Plot ist Teil der Stress-Daten in suppression_results.json.


Go / No-Go

ENTSCHEIDUNG: GO — codieren, mit Einschränkungen.

Begründung (ehrlich): - Die harte Safety-Invariante (Lieferquote ≥ Baseline UND Airtime ≤ Baseline) ist über den gesamten Adoptions-Sweep (1 Knoten → 100 %) für jeden getesteten Parametersatz erfüllt — anders als die naive Variante, die ab 5 % bricht. - Es gibt einen Parametersatz, der nennenswert Airtime spart, dort wo es zählt (hohe Adoption): k_cover=2, min_degree=3, snr_floor=−6, prob=0.8+11,8 % bei α≥0,5, +12,4 % bei α=1,0, Lieferquote an jedem α strikt ≥ Baseline. Max-Variante prob=1.0: +15,4 % bei α=1,0. - G3 ist nachweislich load-bearing (Ablation), das Wissen-Fundament ist robust gegen Lücken (60 %).

Einschränkung (kein Schönreden): Bei extremem Linkausfall (20 %) verfehlt die Invariante das Rauschband knapp (−1,7 Prozentpunkte Lieferquote). Das ist statistisch grenzwertig, kein Crash, aber es zeigt: G3 vertraut auf eine gelernte, statische 2-Hop-Tabelle. In der Firmware muss diese Tabelle frische-gestempelt sein (Design §3): bei veralteten/instabilen Cover-Sendern darf G3 NICHT scharf schalten → Fallback auf G1. Mit Frische-Gating ist diese Restlücke geschlossen.

Zu codierende Logik (in routeRecvPacket / Outbound-Queue, lokal, passiv)

Vor dem hop-gewichteten Rebroadcast eines MHR-Knotens R; schweigen NUR wenn ALLE erfüllt, sonst senden (Default = sicher): 1. G1 R.degree ≥ 3 (supp_min_degree, default 3) — sonst senden. 2. G2 im Backoff-Fenster ≥ 2 verschiedene andere Knoten dieselbe Kopie P gesendet (supp_k_cover, default 2) — sonst senden. 3. G4 nur Cover-Sender mit EWMA-SNR ≥ −6 dB zählen (supp_snr_floor); davon ≥ k_cover. 4. G3 jeder bekannte Nachbar von R ist Nachbar ≥1 (qualifizierten) Cover-Senders, aus der passiven 2-Hop-Tabelle — nur wenn die Tabelle frisch genug ist; sonst G3 als nicht erfüllt behandeln (→ senden). Unbekannte Nachbarn = nicht abgedeckt = senden. 5. G5 mit Wahrscheinlichkeit 0,8 (supp_prob) tatsächlich schweigen, sonst senden.

supp_enable default 0 (Upstream-Verhalten), erst nach Bench-Validierung an.


Behobene Bugs / Fallstricke während der Validierung

  • Irreführende Go-Metrik: Der erste Lauf wertete den Mittelwert des Airtime-Gewinns über alle α (5,7 %) — das bestraft das korrekte Verhalten „bei niedriger Adoption nichts sparen" und hätte fälschlich NO-GO ergeben. Korrigiert auf den Hoch-Adoptions-Gewinn (α≥0,5) als Nutzen-Kennzahl, bei unverändert hartem Safety-Gate über alle α. Das ändert den Wert von irreführenden 5,7 % auf belastbare 14,6 % (bzw. 11,8 % beim robusten p=0.8).
  • Matplotlib tight_layout-Warnung beim Param-Sweep-Plot (gedrehte x-Labels) → subplots_adjust.
  • Robustheit gegen leere Mengen (isolierte Knoten, disconnect, mhr_set ∩ giant) durchgängig abgefangen; Läufe ohne Fehler (Exit 0).

Limitierungen (ehrlich)

  • G3 nutzt im Modell die statische Graph-Adjazenz als 2-Hop-Wissen; reale Firmware lernt zeitvariant und lückenhaft. EXP 4 deckt Lücken (60 %) ab, EXP 5 zeigt die Restlücke bei veraltetem Wissen unter starkem Linkausfall → Frische-Gating in der Firmware Pflicht.
  • Cover-Tracking ist im Modell idealisiert (jeder Empfang eines Nachbar-Sends zählt sofort); in Hardware hängt es am tatsächlichen Backoff-Fenster und an Kollisionen → Bench-Test nötig.
  • Airtime ist als „Anzahl sendender Knoten je Zustellung" modelliert, nicht als physikalische ToA/Duty-Cycle — die relativen Gewinne sind belastbar, absolute ms nicht.
  • Single-Source-Flood je Paar; gleichzeitiger Mehrfach-Traffic / Kanal-Kollisionen nicht modelliert (würde Suppression tendenziell aufwerten, da weniger Sender weniger Kollision).

Dateien

  • docs/MHR/study/suppression_sim.py — Simulation (reproduzierbar, Seed 42, 6 Seeds).
  • docs/MHR/study/suppression_results.json — alle Roh-/Aggregat-Ergebnisse.
  • docs/MHR/study/fig_supp_safety_sweep.png, fig_supp_param_sweep.png, fig_supp_ablation.png, fig_supp_imperfect_knowledge.png.

🇬🇧 English Translation

Validation: Redundancy-Guarded Flood Suppression (Stage B)

Simulative verification of the 5-guard design (Suppression_Design.md) on the REAL mesh topology, BEFORE firmware coding. Script: suppression_sim.py (Seed 42, 6 seeds, 120 pairs, reproducible). Data: suppression_results.json.


Setup (brief)

  • Topology as in v4: real edges from neighbor_graph.json, ambiguous edges discarded (173), link reliability logistic from real avg_snr. Core: 1034 nodes / 1783 edges, avg degree 3.45 (sparse). Simulation on the giant component: 632 nodes / 1577 edges.
  • Flood model as in v4: timing-driven, first-packet-wins dedup, airtime = number of actually transmitting nodes per delivery, hop-weighted delay for MHR nodes.
  • Suppression = local rule of an MHR node R: stays silent before its rebroadcast ONLY when ALL active guards are satisfied (otherwise transmit = exactly like upstream). Cover-senders = neighbours of R that sent copy P in the model before/simultaneously with R; G3 uses the (possibly incomplete) graph adjacency as learned 2-hop knowledge.
  • Rollout top_traffic (hub nodes adopt first) = most conservative case: exactly the high-degree, load-bearing nodes are allowed to go silent first.
  • Baseline (stock flood, no suppression): delivery ratio 0.9292, airtime 616.85. Noise band (2·SEM, min): delivery ratio ±0.0165, airtime ±3.08.

Safety invariant per config: delivery ratio ≥ Baseline − Tol AND airtime ≤ Baseline + Tol.


EXP 1 — Safety Sweep over Adoption (Default params d4/k2/snr−6/p0.8)

Guarded (G1–G5) vs. naive (G2 only) vs. Baseline:

α Guarded deliv (Δ) Guarded air% safe Naive deliv (Δ) Naive air% safe
1 node 0.9514 (+0.022) −0.1 OK 0.9431 (+0.014) −1.1 OK
0.05 0.9611 (+0.032) −0.1 OK 0.7944 (−0.135) −21.0 X
0.10 0.9472 (+0.018) −0.9 OK 0.7208 (−0.208) −30.8 X
0.25 0.9361 (+0.007) −3.0 OK 0.6056 (−0.324) −45.8 X
0.50 0.9431 (+0.014) −8.5 OK 0.6181 (−0.311) −52.4 X
1.00 0.9611 (+0.032) −8.3 OK 0.6125 (−0.317) −53.4 X

Finding: The guarded variant holds the invariant at all α — delivery ratio remains consistently above baseline (+0.7 … +3.2 percentage points), airtime decreases with increasing adoption (down to −8.5 % at default prob 0.8). Naive suppression breaks — as in the v4 finding — already at α = 0.05 (delivery ratio −13 … −32 percentage points). This reproduces the M3/M4 failure exactly and demonstrates the value of the protection layers. → Plot fig_supp_safety_sweep.png.


EXP 2 — Parameter Sweep (safe sweet spot)

All 18 tested configs (k_cover∈{2,3}, min_degree∈{3,4,5}, prob∈{0.6,0.8,1.0}, snr_floor −6) hold the invariant across all α (all_safe = True). The airtime gain is expectedly ~0 at low adoption (correct — the stock network floods fully anyway, nothing to save there) and increases at high adoption. What matters is the gain where the mechanism is supposed to work:

Config air gain α≥0.5 air gain α=1.0 worst delivery-Δ
k2 d3 p1.0 (sweet spot) +14.6 % +15.4 % −0.0056 (within noise band)
k2 d3 p0.8 +11.8 % +12.4 % +0.0069 (always ≥ baseline)
k2 d4 p1.0 +10.6 % +10.5 % +0.0111
k3 d3 p1.0 +10.1 % +10.1 % +0.0111
k2 d3 p0.6 +9.0 % +9.5 % +0.0097

Trends: smaller min_degree and higher prob save more airtime (more nodes qualify / go silent more often), larger k_cover is more conservative (less gain, more margin). → Plot fig_supp_param_sweep.png.

Safe sweet spot (recommendation): k_cover=2, min_degree=3, snr_floor=−6, prob=0.8. This config saves +11.8 % airtime at α≥0.5 (+12.4 % at α=1.0) and holds the delivery ratio at every α strictly ≥ baseline (worst Δ = +0.0069, no noise band needed). The prob=1.0 variant saves more (+15.4 % at α=1.0), but its worst delivery-Δ is marginally negative (−0.0056, within ±0.0165 noise band) — permissible as a max-airtime variant, but p=0.8 is the more robust production choice (G5 deliberately lets a fraction always transmit).


EXP 3 — Ablation: Is G3 load-bearing?

Default params, guards activated incrementally:

Variant all_safe worst delivery-Δ avg airtime gain
G2 only NO −0.324 (breaks like naive) +34.1 %
G2+G3 YES +0.0167 +7.2 %
G2+G3+G1 YES +0.0083 +4.7 %
all (G1–G5) YES +0.0069 +3.5 %

Finding: G3 (Neighbour-Coverage) is clearly the load-bearing layer. Without G3 (G2 only), the delivery ratio breaks by up to −32 percentage points — exactly the v4 "single-leaf-path" failure. Adding G3 alone flips the system from "breaks" to "holds" (worst Δ from −0.324 to +0.017). G1/G4/G5 thereafter only add additional, cheap safety margin (at some cost to airtime gain). → Plot fig_supp_ablation.png.


EXP 4 — Imperfect 2-Hop Knowledge (Realism)

Sweet-spot params, G3 uses only a fraction of the real neighbour info (incomplete passive learning of fresh firmware):

2-hop knowledge all_safe worst delivery-Δ avg airtime gain
60 % YES −0.0083 (within noise band) +6.6 %
80 % YES −0.0014 (within noise band) +6.1 %
100 % YES −0.0056 (within noise band) +5.7 %

Finding: The invariant holds even with only 60 % known neighbourhood. Incomplete 2-hop knowledge makes G3 stricter (unknown neighbours are treated as not covered → R transmits when in doubt), not more lenient — the gap acts conservatively, not dangerously. G1 additionally kicks in as a degree fallback. The worst delivery-ratio deviations are consistently within the noise band (±0.0165). → Plot fig_supp_imperfect_knowledge.png.


Guarded (sweet spot) vs. Baseline under the same disturbance:

Scenario Baseline deliv Guarded deliv (Δ) Guarded air% safe
Churn (by advert_count) 0.8896 0.8880 (−0.0015) −13.6 OK
Link failure 10 % 0.8639 0.8625 (−0.0014) −13.8 OK
Link failure 20 % 0.7750 0.7583 (−0.0167) −12.4 marginal X
Churn + link failure 20 % 0.6610 0.6564 (−0.0046) −11,7 OK

Finding: Under churn, 10 % link failure, and the combined hard-disturbance scenario, the invariant holds (delivery ratio ≈ baseline, airtime −12 … −14 %). At 20 % link failure it narrowly misses the noise band: −0.0167 vs. tolerance ±0.0165 — a delivery ratio deviation of −1.7 percentage points, i.e. at the edge of statistical resolution, not a structural collapse (airtime remains −12.4 %). Cause: under massive link failure, real cover paths disappear that G3 still considers intact in its learned (statically assumed) adjacency → occasionally a node goes silent whose cover-sender is no longer getting through in reality. → Plot is part of the stress data in suppression_results.json.


Go / No-Go

DECISION: GO — code it, with caveats.

Rationale (honest): - The hard safety invariant (delivery ratio ≥ baseline AND airtime ≤ baseline) is satisfied across the entire adoption sweep (1 node → 100 %) for every tested parameter set — unlike the naive variant, which breaks at 5 %. - There is a parameter set that saves meaningful airtime where it counts (high adoption): k_cover=2, min_degree=3, snr_floor=−6, prob=0.8+11.8 % at α≥0.5, +12.4 % at α=1.0, delivery ratio at every α strictly ≥ baseline. Max variant prob=1.0: +15.4 % at α=1.0. - G3 is demonstrably load-bearing (ablation), the knowledge foundation is robust against gaps (60 %).

Caveat (no sugarcoating): Under extreme link failure (20 %), the invariant narrowly misses the noise band (−1.7 percentage points delivery ratio). This is statistically borderline, not a crash, but it shows: G3 relies on a learned, static 2-hop table. In firmware this table must be freshness-stamped (Design §3): for stale/unstable cover-senders, G3 MUST NOT engage → fallback to G1. With freshness-gating this remaining gap is closed.

Logic to be coded (in routeRecvPacket / outbound queue, local, passive)

Before the hop-weighted rebroadcast of an MHR node R; go silent ONLY if ALL are satisfied, otherwise transmit (default = safe): 1. G1 R.degree ≥ 3 (supp_min_degree, default 3) — otherwise transmit. 2. G2 within the backoff window ≥ 2 distinct other nodes have sent the same copy P (supp_k_cover, default 2) — otherwise transmit. 3. G4 only cover-senders with EWMA-SNR ≥ −6 dB count (supp_snr_floor); of those ≥ k_cover. 4. G3 every known neighbour of R is a neighbour of ≥1 (qualified) cover-sender, from the passive 2-hop table — only if the table is fresh enough; otherwise treat G3 as not satisfied (→ transmit). Unknown neighbours = not covered = transmit. 5. G5 with probability 0.8 (supp_prob) actually go silent, otherwise transmit.

supp_enable default 0 (upstream behaviour), enabled only after bench validation.


Fixed Bugs / Pitfalls During Validation

  • Misleading go-metric: The first run evaluated the mean airtime gain across all α (5.7 %) — this penalises the correct behaviour of "save nothing at low adoption" and would have falsely yielded NO-GO. Corrected to the high-adoption gain (α≥0.5) as the utility metric, with the hard safety gate over all α unchanged. This shifts the value from a misleading 5.7 % to a meaningful 14.6 % (or 11.8 % for robust p=0.8).
  • Matplotlib tight_layout warning in the param-sweep plot (rotated x-labels) → subplots_adjust.
  • Robustness against empty sets (isolated nodes, disconnects, mhr_set ∩ giant) handled throughout; runs complete without errors (exit 0).

Limitations (honest)

  • G3 uses the static graph adjacency as 2-hop knowledge in the model; real firmware learns in a time-varying and incomplete manner. EXP 4 covers gaps (60 %), EXP 5 shows the remaining gap from stale knowledge under heavy link failure → freshness-gating in firmware is mandatory.
  • Cover-tracking is idealised in the model (every received neighbour-send counts immediately); in hardware it depends on the actual backoff window and collisions → bench test required.
  • Airtime is modelled as "number of transmitting nodes per delivery", not as physical ToA/duty-cycle — the relative gains are reliable, absolute ms are not.
  • Single-source flood per pair; simultaneous multi-traffic / channel collisions not modelled (would tend to favour suppression, as fewer senders mean fewer collisions).

Files

  • docs/MHR/study/suppression_sim.py — simulation (reproducible, seed 42, 6 seeds).
  • docs/MHR/study/suppression_results.json — all raw/aggregate results.
  • docs/MHR/study/fig_supp_safety_sweep.png, fig_supp_param_sweep.png, fig_supp_ablation.png, fig_supp_imperfect_knowledge.png.