waveguard-demo / app.py
emergentphysicslab's picture
Upload app.py with huggingface_hub
0401adb verified
"""
WaveGuard Demo Space -- Hugging Face
=====================================
Showcases WaveGuard anomaly detection results.
NO engine code included. All results pre-computed.
For live detection, use the API: https://rapidapi.com/gpartin/api/waveguard
"""
import json
import os
import gradio as gr
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
from matplotlib.patches import Patch
# ---------------------------------------------------------------------------
# Load pre-computed demo results (generated locally with full engine)
# ---------------------------------------------------------------------------
_HERE = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(_HERE, "demo_results.json")) as _f:
DEMO_DATA = json.load(_f)
# ---------------------------------------------------------------------------
# Color palette
# ---------------------------------------------------------------------------
C_NORMAL = "#43a047"
C_ANOMALY = "#e53935"
C_ACCENT = "#1565c0"
C_BG = "#fafafa"
C_GRID = "#e0e0e0"
C_WG = "#1565c0"
C_IF = "#ff9800"
C_LOF = "#7b1fa2"
C_SVM = "#546e7a"
def _score_color(is_anomaly):
return C_ANOMALY if is_anomaly else C_NORMAL
# ---------------------------------------------------------------------------
# Benchmark comparison data (from validated benchmark suite)
# ---------------------------------------------------------------------------
BENCHMARKS = [
("Credit Card Fraud*", 0.653, 0.607, 0.601, 0.472),
("Crypto Fraud", 1.000, 0.933, 0.946, 0.897),
("Network Security", 0.990, 0.962, 0.980, 0.952),
("Ad Click Fraud", 0.988, 0.952, 0.930, 0.889),
("Insurance Claims", 0.972, 0.921, 0.959, 0.833),
("API Monitoring", 0.959, 0.909, 0.933, 0.814),
]
def build_benchmark_chart():
"""Grouped bar chart: WaveGuard vs baselines on selected benchmarks."""
names = [b[0] for b in BENCHMARKS]
wg = [b[1] for b in BENCHMARKS]
iso_f = [b[2] for b in BENCHMARKS]
lof = [b[3] for b in BENCHMARKS]
svm = [b[4] for b in BENCHMARKS]
fig, ax = plt.subplots(figsize=(12, 6))
fig.patch.set_facecolor(C_BG)
ax.set_facecolor(C_BG)
x = np.arange(len(names))
w = 0.19
ax.bar(x - 1.5 * w, wg, w, label="WaveGuard", color=C_WG,
edgecolor="#333", linewidth=0.5)
ax.bar(x - 0.5 * w, iso_f, w, label="IsolationForest", color=C_IF,
edgecolor="#333", linewidth=0.5)
ax.bar(x + 0.5 * w, lof, w, label="LOF", color=C_LOF,
edgecolor="#333", linewidth=0.5)
ax.bar(x + 1.5 * w, svm, w, label="OneClassSVM", color=C_SVM,
edgecolor="#333", linewidth=0.5)
for i, val in enumerate(wg):
ax.text(i - 1.5 * w, val + 0.015, f"{val:.2f}", ha="center",
va="bottom", fontsize=8, fontweight="bold", color=C_WG)
ax.set_ylabel("F1 Score", fontsize=12)
ax.set_title("WaveGuard vs Traditional Anomaly Detectors",
fontsize=14, fontweight="bold", pad=15)
ax.set_xticks(x)
ax.set_xticklabels(names, rotation=25, ha="right", fontsize=10)
ax.set_ylim(0, 1.15)
ax.legend(loc="upper right", fontsize=9, framealpha=0.9)
ax.grid(axis="y", alpha=0.3, color=C_GRID)
plt.tight_layout()
return fig
# ---------------------------------------------------------------------------
# Visualization builders
# ---------------------------------------------------------------------------
def build_score_chart(demo_key):
"""Bar chart of anomaly scores with ground truth annotations."""
d = DEMO_DATA[demo_key]
labels = d["test_labels"]
results = d["results"]
expected = d["expected_anomaly"]
n = len(labels)
fig, ax = plt.subplots(figsize=(max(10, n * 1.2), 5))
fig.patch.set_facecolor(C_BG)
ax.set_facecolor(C_BG)
scores = [r["score"] for r in results]
detected = [r["is_anomaly"] for r in results]
colors = [_score_color(det) for det in detected]
bars = ax.bar(range(n), scores, color=colors, edgecolor="#333",
linewidth=0.6, alpha=0.85, width=0.7)
for i, (bar, sc, det, exp) in enumerate(zip(bars, scores, detected, expected)):
# Top label
if det and exp:
label = "CAUGHT"
color = C_ANOMALY
elif det and not exp:
label = "FALSE POS"
color = "#ff9800"
elif not det and exp:
label = "MISSED"
color = "#9e9e9e"
else:
label = "OK"
color = C_NORMAL
y_pos = bar.get_height() + max(scores) * 0.03
ax.text(bar.get_x() + bar.get_width() / 2., y_pos, label,
ha="center", va="bottom", fontsize=8, fontweight="bold",
color=color)
# Ground truth marker
if exp:
ax.plot(i, -max(scores) * 0.04, marker="^", color=C_ANOMALY,
markersize=8, zorder=5)
ax.set_xticks(range(n))
ax.set_xticklabels(labels, rotation=35, ha="right", fontsize=9)
ax.set_ylabel("Anomaly Score", fontsize=11)
ax.set_title(d["scenario"], fontsize=13, fontweight="bold", pad=15)
ax.grid(axis="y", alpha=0.3, color=C_GRID)
ax.legend(handles=[
Patch(color=C_NORMAL, label="Normal"),
Patch(color=C_ANOMALY, label="Anomaly detected"),
plt.Line2D([0], [0], marker="^", color=C_ANOMALY, linestyle="None",
markersize=8, label="True anomaly"),
], loc="upper left", fontsize=8, framealpha=0.9)
plt.tight_layout()
return fig
def build_timeseries_chart():
"""Specialized time series visualization with signal preview."""
d = DEMO_DATA["timeseries"]
results = d["results"]
labels = d["test_labels"]
expected = d["expected_anomaly"]
test_data = d.get("test_preview", [])
train_data = d.get("train_preview", [])
fig = plt.figure(figsize=(14, 8))
fig.patch.set_facecolor(C_BG)
gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1], hspace=0.35)
# Top: Signal traces
ax1 = fig.add_subplot(gs[0])
ax1.set_facecolor(C_BG)
if train_data:
for i, win in enumerate(train_data[:3]):
ax1.plot(win, color="#90caf9", alpha=0.4, linewidth=0.8,
label="Training (normal)" if i == 0 else None)
if test_data:
for i, (win, r, lbl) in enumerate(zip(test_data, results, labels)):
color = C_ANOMALY if r["is_anomaly"] else C_NORMAL
style = "--" if expected[i] else "-"
ax1.plot(win, color=color, linewidth=1.5, alpha=0.8, linestyle=style,
label=f'{lbl} ({"ANOMALY" if r["is_anomaly"] else "ok"})')
ax1.set_xlabel("Time Step", fontsize=10)
ax1.set_ylabel("Value", fontsize=10)
ax1.set_title("Time Series: Training vs Test Windows", fontsize=12, fontweight="bold")
ax1.legend(fontsize=7, loc="upper right", ncol=2, framealpha=0.9)
ax1.grid(True, alpha=0.3, color=C_GRID)
# Bottom: Score bars
ax2 = fig.add_subplot(gs[1])
ax2.set_facecolor(C_BG)
scores = [r["score"] for r in results]
detected = [r["is_anomaly"] for r in results]
colors = [_score_color(det) for det in detected]
ax2.bar(range(len(scores)), scores, color=colors, edgecolor="#333",
linewidth=0.5, alpha=0.85, width=0.7)
ax2.set_xticks(range(len(scores)))
ax2.set_xticklabels(labels, rotation=30, ha="right", fontsize=8)
ax2.set_ylabel("Anomaly Score", fontsize=10)
ax2.set_title("Detection Results", fontsize=12, fontweight="bold")
ax2.grid(axis="y", alpha=0.3, color=C_GRID)
for i, exp_flag in enumerate(expected):
if exp_flag:
ax2.plot(i, -max(scores) * 0.04, marker="^", color=C_ANOMALY,
markersize=8, zorder=5)
plt.tight_layout()
return fig
def build_summary_text(demo_key):
"""Build a clean text summary for the demo results."""
d = DEMO_DATA[demo_key]
results = d["results"]
labels = d["test_labels"]
expected = d["expected_anomaly"]
n_true = sum(1 for e in expected if e)
n_norm = sum(1 for e in expected if not e)
n_caught = sum(1 for i, r in enumerate(results) if expected[i] and r["is_anomaly"])
n_fp = sum(1 for i, r in enumerate(results) if not expected[i] and r["is_anomaly"])
lines = [
d["scenario"].upper(),
"=" * 55,
f"Training samples: {d['training_count']}",
f"Grid: {d['grid_size']}^3 ({d['grid_size']**3:,} cells)",
f"Compute time: {d['elapsed_seconds']}s (pre-computed)",
"",
"PERFORMANCE:",
f" Anomalies caught: {n_caught}/{n_true} ({n_caught/max(n_true,1):.0%})",
f" False positives: {n_fp}/{n_norm} ({n_fp/max(n_norm,1):.0%})",
"",
"DETAIL:",
]
for i, (r, lbl, exp) in enumerate(zip(results, labels, expected)):
tag = ""
if exp and r["is_anomaly"]:
tag = " >> CAUGHT <<"
elif exp and not r["is_anomaly"]:
tag = " ** MISSED **"
elif not exp and r["is_anomaly"]:
tag = " [false positive]"
lines.append(
f" {lbl}: score={r['score']:.3f} "
f"conf={r['confidence']:.3f}{tag}"
)
if "note" in d:
lines.extend(["", f"NOTE: {d['note']}"])
lines.extend([
"",
"-" * 55,
"Want to run on YOUR data? Use the WaveGuard API:",
" https://rapidapi.com/gpartin/api/waveguard",
" pip install WaveGuardClient",
])
return "\n".join(lines)
# ---------------------------------------------------------------------------
# Demo handler functions
# ---------------------------------------------------------------------------
def run_timeseries_demo():
fig = build_timeseries_chart()
summary = build_summary_text("timeseries")
return summary, fig
def run_financial_demo():
fig = build_score_chart("financial")
summary = build_summary_text("financial")
return summary, fig
def run_process_demo():
fig = build_score_chart("process_health")
summary = build_summary_text("process_health")
return summary, fig
def run_network_demo():
fig = build_score_chart("network")
summary = build_summary_text("network")
return summary, fig
# ---------------------------------------------------------------------------
# Gradio UI -- Markdown content
# ---------------------------------------------------------------------------
HEADER_MD = """
# WaveGuard: Physics-Based Anomaly Detection
**Zero-training anomaly detection powered by wave equation physics.**
No neural networks. No gradient descent. No hyperparameter tuning. **Deterministic.**
Your data enters a physics simulation where normal behavior creates smooth wave
propagation. Anomalies disrupt the simulation in measurable ways -- catching threats
that statistical methods miss.
| | WaveGuard | Traditional ML |
|---|---|---|
| **Training data** | 2-15 samples | Hundreds to thousands |
| **Hyperparameters** | Zero | Many |
| **Deterministic** | Always | Rarely |
| **Explainable** | Per-feature breakdown | Often opaque |
**Ranked #1 in F1 score on all 12 benchmark datasets** vs. IsolationForest, LOF,
and OneClassSVM. **See the Benchmarks tab below.**
"""
BENCHMARK_MD = """
### WaveGuard vs Traditional Anomaly Detectors
Head-to-head comparison using identical train/test splits.
WaveGuard uses 2-15 training samples; baselines use the same data
with default scikit-learn hyperparameters.
**Result: Ranked #1 on all 12 benchmark datasets by F1 score.**
| Dataset | WaveGuard | IsolationForest | LOF | OneClassSVM | Winner |
|---------|:---------:|:---------------:|:---:|:-----------:|:------:|
| Credit Card Fraud* | **0.653** | 0.607 | 0.601 | 0.472 | WaveGuard |
| Crypto Fraud | **1.000** | 0.933 | 0.946 | 0.897 | WaveGuard |
| Network Security | **0.990** | 0.962 | 0.980 | 0.952 | WaveGuard |
| Ad Click Fraud | **0.988** | 0.952 | 0.930 | 0.889 | WaveGuard |
| Insurance Claims | **0.972** | 0.921 | 0.959 | 0.833 | WaveGuard |
| API Monitoring | **0.959** | 0.909 | 0.933 | 0.814 | WaveGuard |
*\\*Real-world dataset. Others use domain-specific test suites.
Full results: [waveguard-benchmarks](https://huggingface.co/datasets/emergentphysicslab/waveguard-benchmarks)*
"""
HOW_IT_WORKS_MD = """
## How It Works
WaveGuard replaces machine learning with **physics simulation**.
### Detection in 5 Steps
1. **Encode** -- Your data is mapped into a simulation environment
2. **Evolve** -- Wave equations run forward, adapting to your normal patterns
3. **Lock** -- The evolved state is frozen as your reference model
4. **Test** -- New data enters the simulation. Normal data propagates smoothly.
Anomalies create measurable disruptions.
5. **Score** -- Disruption magnitude becomes the anomaly score.
Per-feature breakdowns show exactly what triggered detection.
### Why Physics Instead of Statistics?
Statistical methods draw boundaries around "normal" in feature space.
This breaks down with small training sets, concept drift, or correlated
multi-feature shifts.
Wave equations model how information propagates through a system.
Normal patterns create resonant, low-energy propagation. Anomalies
break the resonance -- regardless of direction in feature space.
**Result:** Robust detection from a handful of examples, zero tuning.
### Best For
- **Multi-feature anomalies** -- several metrics shift simultaneously
- **Pattern breaks** -- frequency shifts, level changes, structural discontinuities
- **Extreme events** -- crashes, spikes, DDoS attacks, sensor failures
- **Cold start** -- works with as few as 2 training samples
### Known Limitations
- Subtle single-feature drift may not trigger detection
- High-variance data can produce elevated false positive rates
- Not designed for image classification, NLP, or structured prediction
"""
FOOTER_MD = """
---
**WaveGuard v3.3.0** by [Emergent Physics Lab](https://huggingface.co/emergentphysicslab) |
[RapidAPI](https://rapidapi.com/gpartin/api/waveguard) |
[GitHub](https://github.com/gpartin/WaveGuardClient) |
[PyPI](https://pypi.org/project/WaveGuardClient/) |
[MCP Registry](https://glama.ai/mcp/connectors/com.emergentphysicslab/waveguard) |
MIT License
"""
# ---------------------------------------------------------------------------
# Gradio app layout
# ---------------------------------------------------------------------------
with gr.Blocks(
title="WaveGuard - Physics-Based Anomaly Detection",
theme=gr.themes.Soft(),
css="""
.demo-btn { min-height: 80px !important; font-size: 14px !important; }
.results-text { font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; }
"""
) as demo:
gr.Markdown(HEADER_MD)
with gr.Tabs():
# ==============================================================
# Tab 1: Interactive Demos
# ==============================================================
with gr.TabItem("Live Demos", id="demos"):
gr.Markdown("Click any scenario to see pre-computed detection results.")
with gr.Row():
btn_ts = gr.Button(
"Time Series\nSpikes, shifts, flatlines",
variant="primary", elem_classes=["demo-btn"])
btn_fin = gr.Button(
"Financial Fraud\nCard testing, structuring",
variant="primary", elem_classes=["demo-btn"])
btn_proc = gr.Button(
"Process Health\nMemory leaks, crashes",
variant="secondary", elem_classes=["demo-btn"])
btn_net = gr.Button(
"Network Intrusion\nSYN floods, port scans",
variant="secondary", elem_classes=["demo-btn"])
with gr.Row():
with gr.Column(scale=1):
demo_summary = gr.Textbox(
label="Results Summary", lines=22, max_lines=30,
elem_classes=["results-text"],
value="Click a demo button above to see results.")
with gr.Column(scale=2):
demo_plot = gr.Plot(label="Visualization")
gr.Markdown("""
---
**Ready to analyze your own data?**
[Subscribe on RapidAPI](https://rapidapi.com/gpartin/api/waveguard) (free tier available)
| `pip install WaveGuardClient` | [MCP Server](https://glama.ai/mcp/connectors/com.emergentphysicslab/waveguard)
""")
btn_ts.click(fn=run_timeseries_demo, outputs=[demo_summary, demo_plot])
btn_fin.click(fn=run_financial_demo, outputs=[demo_summary, demo_plot])
btn_proc.click(fn=run_process_demo, outputs=[demo_summary, demo_plot])
btn_net.click(fn=run_network_demo, outputs=[demo_summary, demo_plot])
# ==============================================================
# Tab 2: Benchmarks
# ==============================================================
with gr.TabItem("Benchmarks", id="benchmarks"):
gr.Markdown(BENCHMARK_MD)
benchmark_plot = gr.Plot(label="F1 Score Comparison")
demo.load(fn=build_benchmark_chart, outputs=benchmark_plot)
# ==============================================================
# Tab 3: How It Works
# ==============================================================
with gr.TabItem("How It Works", id="howto"):
gr.Markdown(HOW_IT_WORKS_MD)
# ==============================================================
# Tab 4: Get Started
# ==============================================================
with gr.TabItem("Get Started", id="api"):
gr.Markdown("""
## Start Using WaveGuard
### Option 1: RapidAPI -- Hosted, No Setup
Subscribe and start making API calls immediately.
| Plan | Price | Rate Limit | Best For |
|------|-------|-----------|----------|
| **Free** | $0 | 10 req/month | Try it out |
| **Pro** | $0.005/req | 60/min | Production apps |
| **Ultra** | $0.003/req | 300/min | High volume |
| **Mega** | $0.001/req | 1000/min | Enterprise scale |
**[Subscribe on RapidAPI ->](https://rapidapi.com/gpartin/api/waveguard)**
---
### Option 2: MCP Server -- AI Agent Integration
Connect WaveGuard to Claude, Cursor, or any MCP-compatible AI agent.
| Directory | Status |
|-----------|--------|
| [Glama](https://glama.ai/mcp/connectors/com.emergentphysicslab/waveguard) | Live |
| [Smithery](https://smithery.ai/servers/emergentphysicslab/waveguard) | Live -- 100/100 quality score |
| [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) | Listed |
---
### Option 3: Python SDK -- Full Control
```bash
pip install WaveGuardClient
```
```python
from waveguard import WaveGuard
wg = WaveGuard(api_key="YOUR_RAPIDAPI_KEY")
# Detect a compromised server
result = wg.scan(
training=[
{"cpu": 45, "memory": 62, "disk_io": 120, "errors": 0},
{"cpu": 48, "memory": 63, "disk_io": 115, "errors": 0},
{"cpu": 42, "memory": 61, "disk_io": 125, "errors": 1},
],
test=[
{"cpu": 46, "memory": 62, "disk_io": 119, "errors": 0},
{"cpu": 99, "memory": 95, "disk_io": 800, "errors": 150},
],
)
for r in result.results:
status = "ANOMALY" if r.is_anomaly else "normal"
print(f"{status} score={r.score:.1f} confidence={r.confidence:.0%}")
```
Get your free API key at [RapidAPI](https://rapidapi.com/gpartin/api/waveguard).
### Time Series Mode
```python
result = wg.scan(
training=[
[1.0, 1.1, 0.9, 1.0, 1.05, 0.95],
[0.95, 1.0, 1.1, 0.98, 1.02, 1.0],
],
test=[
[1.0, 1.0, 1.0, 8.0, 1.0, 1.0], # spike
],
encoder_type="timeseries",
)
```
---
| Resource | URL |
|----------|-----|
| **RapidAPI** | [rapidapi.com/gpartin/api/waveguard](https://rapidapi.com/gpartin/api/waveguard) |
| **PyPI** | [pypi.org/project/WaveGuardClient](https://pypi.org/project/WaveGuardClient/) |
| **GitHub** | [github.com/gpartin/WaveGuardClient](https://github.com/gpartin/WaveGuardClient) |
| **MCP** | [glama.ai/mcp/connectors](https://glama.ai/mcp/connectors/com.emergentphysicslab/waveguard) |
| **Model Card** | [huggingface.co/emergentphysicslab/waveguard-anomaly-detector](https://huggingface.co/emergentphysicslab/waveguard-anomaly-detector) |
| **Benchmarks** | [huggingface.co/datasets/emergentphysicslab/waveguard-benchmarks](https://huggingface.co/datasets/emergentphysicslab/waveguard-benchmarks) |
""")
gr.Markdown(FOOTER_MD)
if __name__ == "__main__":
demo.launch()