Commit Β·
c2e4262
1
Parent(s): f52daca
Add README.md
Browse files
README.md
CHANGED
|
@@ -8,46 +8,208 @@ app_port: 7860
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
-
# Cascade Containment
|
| 12 |
|
| 13 |
-
An RL benchmark for
|
| 14 |
-
A city health authority must allocate limited resources across districts
|
| 15 |
-
to contain a spreading outbreak β with delayed data, resource scarcity,
|
| 16 |
-
and cascading hospital stress.
|
| 17 |
|
| 18 |
-
|
| 19 |
|
| 20 |
-
|
| 21 |
|
| 22 |
-
-
|
| 23 |
-
- **Action space:** `action_type` (test/restrict/allocate) + `district_id`
|
| 24 |
-
- **Learning:** GRPO-style episodic memory with advantage gating
|
| 25 |
|
| 26 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
from client import CascadeContainmentEnv
|
| 30 |
from models import ContainmentAction
|
| 31 |
|
| 32 |
-
with CascadeContainmentEnv(
|
|
|
|
|
|
|
|
|
|
| 33 |
obs = env.reset(task_name="easy")
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
| medium | 4 | 15 | 8 | None |
|
| 43 |
-
| hard | 6 | 20 | 7 | 3 days |
|
| 44 |
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
| Infection penalty | -0.50 | Per district above 0.4 threshold |
|
| 50 |
-
| Hospital breach | -1.00 | Per breached hospital |
|
| 51 |
-
| Early containment | +0.50 | Scaled by time remaining |
|
| 52 |
-
| Unnecessary restriction | -0.20 | Restricting below 0.2 threshold |
|
| 53 |
-
| Correct prioritisation | +0.30 | Allocating to highest-infected district |
|
|
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# π¦ Cascade Containment
|
| 12 |
|
| 13 |
+
> An RL benchmark for sequential resource allocation under spreading cascade dynamics.
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
A city health authority must allocate limited resources across districts to contain a spreading outbreak β with delayed data, resource scarcity, and cascading hospital stress. Designed as a **generalizable benchmark**: the same environment mechanics model wildfire resource deployment, cyberattack isolation, and misinformation containment.
|
| 16 |
|
| 17 |
+
Built for the **Meta PyTorch OpenEnv Hackathon x SST 2026**.
|
| 18 |
|
| 19 |
+
---
|
|
|
|
|
|
|
| 20 |
|
| 21 |
+
## The Problem
|
| 22 |
+
|
| 23 |
+
Sequential resource allocation under uncertainty is one of the most common and consequential decision problems in the real world. Whether containing an epidemic, deploying firefighting crews, or isolating a cyberattack β the agent faces the same fundamental challenge:
|
| 24 |
+
|
| 25 |
+
- Resources are scarce and cannot cover every district simultaneously
|
| 26 |
+
- Data is delayed β by the time a crisis is visible, it has already grown
|
| 27 |
+
- Interventions have cascading effects across adjacent areas
|
| 28 |
+
- Acting too late is catastrophic; acting too early wastes resources
|
| 29 |
+
|
| 30 |
+
No existing OpenEnv benchmark formalizes this problem class. Cascade Containment does.
|
| 31 |
+
|
| 32 |
+
---
|
| 33 |
+
|
| 34 |
+
## Environment Design
|
| 35 |
+
|
| 36 |
+
### Action Space
|
| 37 |
+
One decision per step β kept deliberately simple to maximize strategic depth:
|
| 38 |
+
|
| 39 |
+
| Field | Type | Values |
|
| 40 |
+
|-------|------|--------|
|
| 41 |
+
| `action_type` | string | `"test"` Β· `"restrict"` Β· `"allocate"` |
|
| 42 |
+
| `district_id` | int | 0-indexed district target |
|
| 43 |
+
|
| 44 |
+
- **test** β spend 1 resource to get accurate infection data for a district
|
| 45 |
+
- **restrict** β impose movement restriction (free, but penalised if infection is low)
|
| 46 |
+
- **allocate** β deploy 1 resource unit to reduce spread rate this step
|
| 47 |
+
|
| 48 |
+
### Observation Space
|
| 49 |
+
The agent receives a filtered, potentially lagged view of the world β never the full ground truth:
|
| 50 |
+
```python
|
| 51 |
+
CityObservation:
|
| 52 |
+
districts: List[DistrictObservation] # per-district visible state
|
| 53 |
+
available_resources: int # budget remaining this turn
|
| 54 |
+
current_step: int
|
| 55 |
+
max_steps: int
|
| 56 |
+
done: bool
|
| 57 |
+
reward: float | None
|
| 58 |
+
message: str | None # human-readable feedback
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
Each `DistrictObservation` contains:
|
| 62 |
+
- `reported_infection_rate` β real-time (easy/medium) or **3 days lagged** (hard)
|
| 63 |
+
- `growth_rate_hint` β noisy signal of true spread rate
|
| 64 |
+
- `hospital_capacity_remaining` β always accurate (hospitals report in real time)
|
| 65 |
+
- `tested_recently`, `restriction_active`
|
| 66 |
+
|
| 67 |
+
### The Key Design Decision: Partial Observability
|
| 68 |
+
The hard task exposes infection rates from **3 days ago**. The agent must learn to act on noisy forward signals (`growth_rate_hint`) rather than react to confirmed data β exactly the challenge real public health officials face. This single mechanic is what separates a thoughtful agent from a reactive one.
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## Three Tasks
|
| 73 |
+
|
| 74 |
+
| Task | Districts | Steps | Resources | Data Lag | Challenge |
|
| 75 |
+
|------|-----------|-------|-----------|----------|-----------|
|
| 76 |
+
| `easy` | 2 | 10 | 10 | None | Single outbreak, clear signal |
|
| 77 |
+
| `medium` | 4 | 15 | 8 | None | Two simultaneous outbreaks, forced triage |
|
| 78 |
+
| `hard` | 6 | 20 | 7 | 3 days | Scarce resources, invisible acceleration |
|
| 79 |
+
|
| 80 |
+
---
|
| 81 |
+
|
| 82 |
+
## Reward Function
|
| 83 |
+
|
| 84 |
+
Five shaped reward terms fire independently each step, providing dense feedback throughout the episode:
|
| 85 |
+
|
| 86 |
+
| Term | Value | Purpose |
|
| 87 |
+
|------|-------|---------|
|
| 88 |
+
| Infection penalty | `-0.50` per district above threshold | Primary containment signal |
|
| 89 |
+
| Hospital breach | `-1.00` per collapsed hospital | Catastrophic failure deterrent |
|
| 90 |
+
| Early containment | `+0.50 Γ (1 - step/max_steps)` | Teaches proactive behaviour |
|
| 91 |
+
| Unnecessary restriction | `-0.20` | Prevents lazy blanket lockdowns |
|
| 92 |
+
| Correct prioritisation | `+0.30` | Rewards triage intelligence |
|
| 93 |
+
|
| 94 |
+
The early containment bonus decays over time β containing an outbreak on day 3 is worth more than on day 8. This single design decision is what teaches the agent to act before crises emerge rather than after.
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
## Grader
|
| 99 |
|
| 100 |
+
The grader is fully deterministic β no randomness, no LLM calls β producing a weighted composite score in `[0.0, 1.0]`:
|
| 101 |
+
|
| 102 |
+
| Component | Weight | Measures |
|
| 103 |
+
|-----------|--------|---------|
|
| 104 |
+
| Containment score | 45% | District-days below infection threshold |
|
| 105 |
+
| Hospital score | 30% | Capacity preserved across episode |
|
| 106 |
+
| Efficiency score | 15% | Resources directed to high-need districts |
|
| 107 |
+
| Speed score | 10% | Containment achieved faster than max steps |
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## Baseline Agent β GRPO-Style Episodic Memory
|
| 112 |
+
|
| 113 |
+
The baseline uses a **simulated GRPO learning loop** with episodic memory β no weight updates required:
|
| 114 |
+
|
| 115 |
+
1. **Rollout 1** runs with base prompt, no prior knowledge
|
| 116 |
+
2. After each rollout, compute advantage = `R_i - mean(completed rollouts)`
|
| 117 |
+
3. If above average β store positive-reward steps into `EpisodicMemory`
|
| 118 |
+
4. If below average β suppress, memory unchanged
|
| 119 |
+
5. Next rollout retrieves the 3 most similar past situations by L1 distance on infection profiles and injects them as concrete examples into the prompt
|
| 120 |
+
|
| 121 |
+
The prompt is the policy. Memory updates are the policy improvement. This produces measurably better decisions across rollouts without any gradient computation.
|
| 122 |
+
|
| 123 |
+
---
|
| 124 |
+
|
| 125 |
+
## Baseline Scores
|
| 126 |
+
|
| 127 |
+
Dumb greedy policy (always allocates to district 0):
|
| 128 |
+
|
| 129 |
+
| Task | Score | Hospital Breached |
|
| 130 |
+
|------|-------|-------------------|
|
| 131 |
+
| Easy | ~0.50 | No |
|
| 132 |
+
| Medium | ~0.23 | Yes |
|
| 133 |
+
| Hard | ~0.21 | Yes |
|
| 134 |
+
|
| 135 |
+
A smart LLM agent using the episodic memory baseline consistently scores 0.65β0.80 on easy and shows meaningful improvement on medium across rollouts.
|
| 136 |
+
|
| 137 |
+
---
|
| 138 |
+
|
| 139 |
+
## Usage
|
| 140 |
+
```python
|
| 141 |
from client import CascadeContainmentEnv
|
| 142 |
from models import ContainmentAction
|
| 143 |
|
| 144 |
+
with CascadeContainmentEnv(
|
| 145 |
+
base_url="https://RohitChandramouli6618-cascade-containment.hf.space"
|
| 146 |
+
).sync() as env:
|
| 147 |
+
# Run easy task
|
| 148 |
obs = env.reset(task_name="easy")
|
| 149 |
+
|
| 150 |
+
while not obs.done:
|
| 151 |
+
action = ContainmentAction(
|
| 152 |
+
action_type="allocate",
|
| 153 |
+
district_id=0
|
| 154 |
+
)
|
| 155 |
+
result = env.step(action)
|
| 156 |
+
obs = result.observation
|
| 157 |
+
print(f"Reward: {result.reward:.4f}")
|
| 158 |
+
```
|
| 159 |
|
| 160 |
+
### Running the Full Evaluation
|
| 161 |
+
```bash
|
| 162 |
+
# Set required environment variables
|
| 163 |
+
export API_BASE_URL="https://router.huggingface.co/v1"
|
| 164 |
+
export MODEL_NAME="meta-llama/Llama-3.1-8B-Instruct"
|
| 165 |
+
export HF_TOKEN="your_hf_token"
|
| 166 |
+
export ENV_BASE_URL="https://RohitChandramouli6618-cascade-containment.hf.space"
|
| 167 |
|
| 168 |
+
# Run inference
|
| 169 |
+
python inference.py
|
| 170 |
+
```
|
|
|
|
|
|
|
| 171 |
|
| 172 |
+
---
|
| 173 |
+
|
| 174 |
+
## Generalisation
|
| 175 |
+
|
| 176 |
+
This environment is not epidemic-specific. The core mechanics β spreading cascade, delayed data, resource scarcity, spatial spillover β are identical to:
|
| 177 |
+
|
| 178 |
+
- **Wildfire deployment** β pre-position crews before fire reaches populated areas
|
| 179 |
+
- **Cyberattack isolation** β quarantine systems before lateral movement completes
|
| 180 |
+
- **Misinformation containment** β deploy corrections before false narratives entrench
|
| 181 |
+
- **Poverty intervention** β allocate aid where need is growing, not just where it's visible
|
| 182 |
+
|
| 183 |
+
The environment is designed to be a lasting benchmark for this general problem class, not a pandemic novelty.
|
| 184 |
+
|
| 185 |
+
---
|
| 186 |
+
|
| 187 |
+
## Project Structure
|
| 188 |
+
```
|
| 189 |
+
epidemic_containment_env/
|
| 190 |
+
βββ models.py # Data contracts (Action, Observation, State)
|
| 191 |
+
βββ constants.py # All numeric configuration
|
| 192 |
+
βββ client.py # Client-side interface
|
| 193 |
+
βββ openenv.yaml # Environment manifest
|
| 194 |
+
βββ inference.py # Evaluation entry point
|
| 195 |
+
βββ server/
|
| 196 |
+
β βββ environment.py # Core RL loop
|
| 197 |
+
β βββ grader.py # Deterministic scorer
|
| 198 |
+
β βββ utils.py # Spread computation, observation builder
|
| 199 |
+
β βββ app.py # FastAPI server
|
| 200 |
+
β βββ Dockerfile
|
| 201 |
+
β βββ tasks/ # Easy / Medium / Hard task definitions
|
| 202 |
+
βββ baseline/
|
| 203 |
+
β βββ policy.py # LLM agent with prompt engineering
|
| 204 |
+
β βββ evaluator.py # GRPO episodic memory loop
|
| 205 |
+
β βββ run.py # CLI entry point
|
| 206 |
+
βββ core/
|
| 207 |
+
βββ trajectory.py # EpisodicMemory class
|
| 208 |
+
βββ reward.py # Score normalisation
|
| 209 |
+
βββ policy_update.py # Advantage computation
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
---
|
| 213 |
|
| 214 |
+
## Tags
|
| 215 |
+
`reinforcement-learning` Β· `resource-allocation` Β· `sequential-decision-making` Β· `partial-observability` Β· `cascade-dynamics` Β· `openenv` Β· `llm-agent`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|