hamoodey commited on
Commit
a5990d2
·
verified ·
1 Parent(s): 739eecc
.gitattributes CHANGED
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ img/p01-truth.png filter=lfs diff=lfs merge=lfs -text
37
+ img/p09-beam.png filter=lfs diff=lfs merge=lfs -text
38
+ img/p09-truth.png filter=lfs diff=lfs merge=lfs -text
39
+ img/p20-beam.png filter=lfs diff=lfs merge=lfs -text
40
+ img/p20-truth.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -8,8 +8,7 @@ pinned: false
8
  license: cc-by-4.0
9
  short_description: Beam learns to read real floor plans by writing code
10
  ---
11
-
12
- **Main finding: results are being added (Beam low, Beam high, Kimi K3 vs a reference solution at about 0.24); see the Space for live numbers.**
13
 
14
  # Floorplan Graph RL: an RL environment that teaches Beam to read real apartment floor plans by writing code
15
 
@@ -33,32 +32,63 @@ Beam (`Beam-1.0-501B-A23B`, Reflection API, `reasoning_effort` low and high) is
33
  2. Beam answers with one complete `run.py`.
34
  3. `env.step(code)` runs that program on the 24 public plans in a sandbox (30 s CPU per plan), scores it, and returns the reward, partial credit and a failure report (crashes, timeouts, invalid output, low IoU rooms, missed doors).
35
  4. Beam gets the feedback and its previous code and writes a better `run.py`. Rounds repeat.
36
- 5. The best public round is graded once on the 64 held-out plans (buildings never seen in the public set). The held-out plans never feed back into the loop.
37
 
38
- Every prompt, response, reasoning trace, program and score per round is in `runs/`.
39
 
40
  ## Reward
41
  - **Per plan:** rooms are matched one to one (Hungarian) at IoU >= 0.7; a door counts when both its rooms match and its centre lies within 0.5 m of the true door. Plan score = room F1 x door F1.
42
  - **Strict reward (the official score):** 16 clean plans form a gate, g = min(1, mean clean score / 0.6); reward = g x mean score over the 48 hard plans (16 per hard style).
43
  - **Partial credit (dense RL signal):** mean over plans of (room F1 + door F1) / 2. The strict reward is near zero for most early programs; partial credit still moves when a program gets rooms right but misses doors, so the model sees progress.
44
 
 
 
 
 
 
 
 
 
 
 
 
45
  ## Layout
46
  | Path | What |
47
  |---|---|
48
  | `task/instruction.md` | the task statement given to the model |
49
  | `task/workspace/README.md` | full input/output contract |
50
  | `task/workspace/docs/STYLE-GUIDE.md` | drawing styles, door conventions, ranges |
51
- | `task/workspace/public/` | 24 public plans (6 per style): `cases/pNN/plan.png` + `manifest.json`, `answers/pNN.json` |
52
- | `task/workspace/selfcheck/` | self-check runner and the official scorer |
53
- | `task/workspace/submission/run.py` | starting stub |
54
- | `code/` | `env.py` (the RL environment), the model loop, grading CLIs |
 
 
 
 
 
 
 
55
 
56
  ## How to run
57
  ```bash
58
- # self-check a program on the 24 public plans (Python 3.12, numpy, scipy, opencv, scikit-image, shapely, networkx, Pillow)
59
- cp my_run.py task/workspace/submission/run.py
60
- python3 task/workspace/selfcheck/selfcheck.py
 
 
 
 
 
 
 
 
 
 
 
 
61
  ```
 
62
 
63
  ## Credit and license
64
  Plan geometry: **Swiss Dwellings v3.0.0** (Standfest et al.), Zenodo, DOI [10.5281/zenodo.7788422](https://doi.org/10.5281/zenodo.7788422), CC BY 4.0.
 
8
  license: cc-by-4.0
9
  short_description: Beam learns to read real floor plans by writing code
10
  ---
11
+ **Results are being added: Beam (low and high reasoning effort) and Kimi K3 write floor-plan readers over 3 rounds of graded feedback; best so far, Beam low reaches 0.108 public partial credit (strict 0.000) vs a reference solution at about 0.24 strict.**
 
12
 
13
  # Floorplan Graph RL: an RL environment that teaches Beam to read real apartment floor plans by writing code
14
 
 
32
  2. Beam answers with one complete `run.py`.
33
  3. `env.step(code)` runs that program on the 24 public plans in a sandbox (30 s CPU per plan), scores it, and returns the reward, partial credit and a failure report (crashes, timeouts, invalid output, low IoU rooms, missed doors).
34
  4. Beam gets the feedback and its previous code and writes a better `run.py`. Rounds repeat.
35
+ 5. The best public round is graded once on the 64 held-out plans (buildings never seen in the public set; published in `task/hidden/` with answers, for reproducibility). The held-out plans never feed back into the loop.
36
 
37
+ Every prompt, response, reasoning trace, program and score per round is in `runs/`. The same loop also runs Kimi K3 as a comparison, and an earlier Opus 5.5 two-hour agent run (0.032 held-out) is listed as a baseline.
38
 
39
  ## Reward
40
  - **Per plan:** rooms are matched one to one (Hungarian) at IoU >= 0.7; a door counts when both its rooms match and its centre lies within 0.5 m of the true door. Plan score = room F1 x door F1.
41
  - **Strict reward (the official score):** 16 clean plans form a gate, g = min(1, mean clean score / 0.6); reward = g x mean score over the 48 hard plans (16 per hard style).
42
  - **Partial credit (dense RL signal):** mean over plans of (room F1 + door F1) / 2. The strict reward is near zero for most early programs; partial credit still moves when a program gets rooms right but misses doors, so the model sees progress.
43
 
44
+ ## Results
45
+ | Model | Rounds | Best public strict | Best public partial | Held-out strict (64 plans) | Held-out partial | Best round |
46
+ |---|---|---|---|---|---|---|
47
+ | Beam, reasoning low | 3 | 0.000 | 0.108 | 0.000 | 0.118 | round 3 |
48
+ | Beam, reasoning high | 1 | 0.000 | 0.000 | pending | pending | round 1 |
49
+ | Kimi K3 | 0 | - | - | pending | pending | - |
50
+ | Earlier baseline: Opus 5.5, one 2-hour agent run with tools (not this loop) | - | - | - | 0.032 | - | - |
51
+ | Reference solution (hand-written, for scale) | - | - | - | about 0.24 | - | - |
52
+
53
+ Strict = the official score (below). Partial = the dense RL signal. Public scores drive the feedback; the 64 held-out plans are used only once, for the final score. Per-round scores, plan overlays and the wheelchair route are on the Space.
54
+
55
  ## Layout
56
  | Path | What |
57
  |---|---|
58
  | `task/instruction.md` | the task statement given to the model |
59
  | `task/workspace/README.md` | full input/output contract |
60
  | `task/workspace/docs/STYLE-GUIDE.md` | drawing styles, door conventions, ranges |
61
+ | `task/workspace/public/` | 24 public plans (6 per style): `cases/pNN/plan.png` + `manifest.json`, answers in `answers/pNN.json` |
62
+ | `task/workspace/selfcheck/` | self-check, the exact run rules (`runner.py`) and scorer (`score.py`) |
63
+ | `task/workspace/submission/run.py` | starting stub (scores 0) |
64
+ | `task/tests/` | held-out grader: `verify.py`, `score.py`, the renderer `gen/`, the 64 held-out apartments (`hidden/units/`, geometry), `hidden/index.json`, the render seed and `hidden/expected.json` (hashes of every rendered plan and answer). The verifier renders the 64 held-out plans and their answers from these at grade time. |
65
+ | `task/hidden/` | the 64 held-out plans, rendered: `hNN/plan.png`, `manifest.json`, `answer.json` (same format as the public answers), `index.json` (style, scale, counts) |
66
+ | `task/environment/` | Docker setup of the sandbox the program runs in (Ubuntu 24.04, Python 3.12, pinned packages) |
67
+ | `code/env.py` | `FloorplanEnv`: `reset()` returns the task prompt; `step(code)` returns (reward, partial, feedback, info) |
68
+ | `code/grade.py` | grades a `run.py` with the Docker images (public self-check or held-out verifier); prints strict reward, partial credit, per-style F1, failures |
69
+ | `code/llm.py`, `code/loop.py` | the model loop (Beam via the Reflection API, Kimi K3) |
70
+ | `code/build_space.py` | builds the Space page: table, chart, overlays, networkx wheelchair route |
71
+ | `runs/<model>/round-<k>/` | every round: `prompt.md`, `response.md`, `reasoning.md` (Beam's thinking), `run.py`, `public.json` (scores); `runs/<model>/hidden.json` final held-out score |
72
 
73
  ## How to run
74
  ```bash
75
+ # 1. Build the sandbox and the held-out grader (Docker, linux/amd64)
76
+ docker build -t fp-agent task/environment
77
+ docker build -t fp-verifier --build-arg TASK_IMAGE=fp-agent task/tests
78
+
79
+ # 2. Public self-check of your program (24 plans, official runner and scorer)
80
+ docker run --rm -v "$PWD/my_submission:/mnt/sub:ro" --entrypoint bash fp-verifier -c \
81
+ 'rm -rf /app/submission/* && cp -a /mnt/sub/. /app/submission/ && python3 /app/selfcheck/selfcheck.py'
82
+
83
+ # 3. Held-out score (renders the 64 held-out plans, runs your program as an unprivileged user, writes /logs/verifier/reward.json)
84
+ docker run --rm -v "$PWD/my_submission:/mnt/sub:ro" --entrypoint bash fp-verifier -c \
85
+ 'rm -rf /app/submission/* && cp -a /mnt/sub/. /app/submission/ && /tests/test.sh; cat /logs/verifier/reward.json'
86
+
87
+ # 4. The RL loop (Beam writes run.py, gets scores and failures, retries); needs REFLECTION_API_KEY
88
+ # code/grade.py runs the docker commands above over ssh on FP_HOST (a Docker host with the two images)
89
+ python3 code/loop.py --model beam --effort high
90
  ```
91
+ Without Docker: `cp my_run.py task/workspace/submission/run.py && python3 task/workspace/selfcheck/selfcheck.py` (needs Python 3.12 with numpy, scipy, opencv, scikit-image, shapely, networkx, Pillow).
92
 
93
  ## Credit and license
94
  Plan geometry: **Swiss Dwellings v3.0.0** (Standfest et al.), Zenodo, DOI [10.5281/zenodo.7788422](https://doi.org/10.5281/zenodo.7788422), CC BY 4.0.
img/p01-beam.png ADDED
img/p01-truth.png ADDED

Git LFS Details

  • SHA256: 84a85fc415315d0561b09b0acd6c085c196d78c35c185d333808a17c02ab7466
  • Pointer size: 131 Bytes
  • Size of remote file: 114 kB
img/p09-beam.png ADDED

Git LFS Details

  • SHA256: a724e12b4fb8e52688bb559646612c92b1037f9cf38b81a09ff638619425e8c7
  • Pointer size: 131 Bytes
  • Size of remote file: 194 kB
img/p09-truth.png ADDED

Git LFS Details

  • SHA256: a888574aca71e83cc120aff84331be7087d4c68d40cde39706f8742f4fe38939
  • Pointer size: 131 Bytes
  • Size of remote file: 211 kB
img/p20-beam.png ADDED

Git LFS Details

  • SHA256: 16c774db001c37c59ff9e100c92b1a083f4157c22e2ede6c2bf8c6d955014211
  • Pointer size: 131 Bytes
  • Size of remote file: 322 kB
img/p20-truth.png ADDED

Git LFS Details

  • SHA256: 4adc8c161fd8db2eacb97c28d10856d8f841009f49142bff3a432dbe1a4c8dea
  • Pointer size: 131 Bytes
  • Size of remote file: 361 kB
img/p22-beam.png ADDED
img/p22-truth.png ADDED
index.html CHANGED
@@ -1,7 +1,43 @@
1
- <!doctype html><html><head><meta charset="utf-8"><title>Floorplan Graph RL</title>
2
- <style>body{font-family:system-ui,sans-serif;max-width:1000px;margin:2em auto;padding:0 1em;line-height:1.5}</style></head><body>
3
  <h1>Floorplan Graph RL: Beam reads real apartment floor plans by writing code</h1>
4
- <p>An RL environment: Beam writes <code>run.py</code>, the environment runs it on real floor plans, scores it (strict reward + partial credit) and returns failures; Beam retries.</p>
5
- <p>Results, charts and plan overlays are being added. Data and code: <a href="https://huggingface.co/datasets/hamoodey/floorplan-graph-rl">hamoodey/floorplan-graph-rl (dataset)</a>.</p>
6
- <p>Plan geometry: Swiss Dwellings v3.0.0, DOI <a href="https://doi.org/10.5281/zenodo.7788422">10.5281/zenodo.7788422</a>, CC BY 4.0.</p>
7
- </body></html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Floorplan Graph RL: Beam reads real floor plans by writing code</title><style>body{font-family:system-ui,-apple-system,sans-serif;max-width:1100px;margin:2em auto;padding:0 1em;line-height:1.5;color:#222}table{border-collapse:collapse;margin:1em 0}td,th{border:1px solid #ccc;padding:6px 10px;text-align:right}td:first-child,th:first-child{text-align:left}.figs{display:grid;grid-template-columns:1fr 1fr;gap:12px}.figs img{width:100%;border:1px solid #ddd}.cap{font-size:.9em;color:#444;margin-bottom:1.5em}code{background:#f3f3f3;padding:1px 4px}.note{color:#666;font-size:.9em}</style></head><body>
 
2
  <h1>Floorplan Graph RL: Beam reads real apartment floor plans by writing code</h1>
3
+ <p><b>Results are being added: Beam (low and high reasoning effort) and Kimi K3 write floor-plan readers over 3 rounds of graded feedback; best so far, Beam low reaches 0.108 public partial credit (strict 0.000) vs a reference solution at about 0.24 strict.</b></p>
4
+ <p>Beam is text-only. It cannot see a floor plan, but it can write a program that reads one. This RL environment gives Beam a raster plan of a real Swiss apartment and asks for <code>run.py</code>, a program that recovers the <b>room graph</b>: every room as a polygon, every door as an edge. The environment runs the program on 24 public plans in a sandbox, scores it, and sends back the reward, partial credit and the worst failures. Beam rewrites the program. The best round is then scored once on 64 held-out plans from other buildings.</p>
5
+ <p>Data, environment, grader, all plans and every run (prompts, reasoning, code, scores): <a href="https://huggingface.co/datasets/hamoodey/floorplan-graph-rl">huggingface.co/datasets/hamoodey/floorplan-graph-rl</a></p>
6
+ <h2>Results</h2><table><tr><th>Model</th><th>Rounds</th><th>Best public strict</th><th>Best public partial</th><th>Held-out strict (64 plans)</th><th>Held-out partial</th><th>Best round</th></tr>
7
+ <tr><td>Beam, reasoning low</td><td>3</td><td>0.000</td><td>0.108</td><td>0.000</td><td>0.118</td><td>round 3</td></tr>
8
+ <tr><td>Beam, reasoning high</td><td>1</td><td>0.000</td><td>0.000</td><td>pending</td><td>pending</td><td>round 1</td></tr>
9
+ <tr><td>Kimi K3</td><td>0</td><td>-</td><td>-</td><td>pending</td><td>pending</td><td>-</td></tr>
10
+ <tr><td>Earlier baseline: Opus 5.5, one 2-hour agent run with tools (not this loop)</td><td>-</td><td>-</td><td>-</td><td>0.032</td><td>-</td><td>-</td></tr>
11
+ <tr><td>Reference solution (hand-written, for scale)</td><td>-</td><td>-</td><td>-</td><td>about 0.24</td><td>-</td><td>-</td></tr></table>
12
+ <p class="note"><b>Strict</b> = the official score: plan score = room F1 x door F1 (rooms matched one to one at IoU &ge; 0.7, a door counts when its rooms match and its centre is within 0.5 m); gate g = min(1, mean clean / 0.6); strict = g x mean over the 48 hard plans. <b>Partial</b> = mean over plans of (room F1 + door F1) / 2, the dense RL signal. Public scores drive the feedback; held-out plans are used only for the final score.</p>
13
+ <h2>Score per round (public plans)</h2><svg viewBox="0 0 640 300" width="100%" style="max-width:640px" xmlns="http://www.w3.org/2000/svg" font-family="system-ui" font-size="12">
14
+ <line x1="55" x2="490" y1="260.0" y2="260.0" stroke="#ddd"/><text x="49" y="264.0" text-anchor="end">0.00</text>
15
+ <line x1="55" x2="490" y1="212.0" y2="212.0" stroke="#ddd"/><text x="49" y="216.0" text-anchor="end">0.02</text>
16
+ <line x1="55" x2="490" y1="164.0" y2="164.0" stroke="#ddd"/><text x="49" y="168.0" text-anchor="end">0.05</text>
17
+ <line x1="55" x2="490" y1="116.0" y2="116.0" stroke="#ddd"/><text x="49" y="120.0" text-anchor="end">0.07</text>
18
+ <line x1="55" x2="490" y1="68.0" y2="68.0" stroke="#ddd"/><text x="49" y="72.0" text-anchor="end">0.10</text>
19
+ <line x1="55" x2="490" y1="20.0" y2="20.0" stroke="#ddd"/><text x="49" y="24.0" text-anchor="end">0.12</text>
20
+ <text x="55.0" y="278" text-anchor="middle">round 1</text>
21
+ <text x="272.5" y="278" text-anchor="middle">round 2</text>
22
+ <text x="490.0" y="278" text-anchor="middle">round 3</text>
23
+ <text x="14" y="130.0" transform="rotate(-90 14 130.0)" text-anchor="middle">public partial credit</text>
24
+ <polyline fill="none" stroke="#2b8cbe" stroke-width="2.5" points="55.0,260.0 272.5,260.0 490.0,51.3"/>
25
+ <circle cx="55.0" cy="260.0" r="4" fill="#2b8cbe"><title>Beam, reasoning low round 1: partial 0.000, strict 0.000</title></circle>
26
+ <circle cx="272.5" cy="260.0" r="4" fill="#2b8cbe"><title>Beam, reasoning low round 2: partial 0.000, strict 0.000</title></circle>
27
+ <circle cx="490.0" cy="51.3" r="4" fill="#2b8cbe"><title>Beam, reasoning low round 3: partial 0.108, strict 0.000</title></circle>
28
+ <line x1="502" x2="522" y1="30" y2="30" stroke="#2b8cbe" stroke-width="3"/><text x="528" y="34">Beam, reasoning low</text>
29
+ <polyline fill="none" stroke="#08306b" stroke-width="2.5" points="55.0,260.0"/>
30
+ <circle cx="55.0" cy="260.0" r="4" fill="#08306b"><title>Beam, reasoning high round 1: partial 0.000, strict 0.000</title></circle>
31
+ <line x1="502" x2="522" y1="50" y2="50" stroke="#08306b" stroke-width="3"/><text x="528" y="54">Beam, reasoning high</text>
32
+ </svg>
33
+ <h2>What Beam's program sees</h2><p>Left: the rooms (colour), doors (red dot; green = door to outside) and door edges recovered by Beam, reasoning low (round 3), drawn over the plan. Right: the true room graph. The blue line is a <b>wheelchair route to the exit</b>: a networkx shortest path over the door graph from the room farthest from the exit to the outside door, computed only from the recovered map. Blue dot = start.</p>
34
+ <h3>p22 (clean)</h3><div class="figs"><div><img src="img/p22-beam.png" alt="Beam recovered graph for p22"><div>Beam</div></div><div><img src="img/p22-truth.png" alt="true graph for p22"><div>Truth</div></div></div>
35
+ <div class="cap">Rooms matched 4/6 (pred 5), doors matched 0/6 (pred 0), plan score 0.000, partial 0.364. Beam map: no route: the recovered map has no door to outside. True route: 3 door(s), narrowest 0.67 m, 1 under 0.8 m (too narrow for many wheelchairs).</div>
36
+ <h3>p01 (outline+clutter)</h3><div class="figs"><div><img src="img/p01-beam.png" alt="Beam recovered graph for p01"><div>Beam</div></div><div><img src="img/p01-truth.png" alt="true graph for p01"><div>Truth</div></div></div>
37
+ <div class="cap">Rooms matched 2/5 (pred 9), doors matched 0/5 (pred 0), plan score 0.000, partial 0.143. Beam map: no route: the recovered map has no door to outside. True route: 2 door(s), narrowest 0.84 m, all at least 0.8 m wide.</div>
38
+ <h3>p09 (clutter+scan)</h3><div class="figs"><div><img src="img/p09-beam.png" alt="Beam recovered graph for p09"><div>Beam</div></div><div><img src="img/p09-truth.png" alt="true graph for p09"><div>Truth</div></div></div>
39
+ <div class="cap">Rooms matched 2/8 (pred 2), doors matched 0/10 (pred 0), plan score 0.000, partial 0.200. Beam map: no route: the recovered map has no door to outside. True route: 3 door(s), narrowest 0.92 m, all at least 0.8 m wide.</div>
40
+ <h3>p20 (outline+scan)</h3><div class="figs"><div><img src="img/p20-beam.png" alt="Beam recovered graph for p20"><div>Beam</div></div><div><img src="img/p20-truth.png" alt="true graph for p20"><div>Truth</div></div></div>
41
+ <div class="cap">Rooms matched 3/4 (pred 3), doors matched 0/4 (pred 0), plan score 0.000, partial 0.429. Beam map: no route: the recovered map has no door to outside. True route: 2 door(s), narrowest 0.84 m, all at least 0.8 m wide.</div>
42
+ <h2>Why this is useful</h2><p>Floor plans exist mostly as images. A room graph turns one into something a program can answer questions about: is every room reachable from the exit, what is the step-free route for a wheelchair, how many bedrooms, which rooms are interior. The environment is a ready-made, exactly graded RL task for teaching a text-only model to do visual work through code, with dense partial credit so early programs still get a learning signal.</p>
43
+ <h2>Credit</h2><p>Plan geometry: Swiss Dwellings v3.0.0, Zenodo, DOI <a href="https://doi.org/10.5281/zenodo.7788422">10.5281/zenodo.7788422</a>, CC BY 4.0. Plans re-drawn from that geometry. Built by Hamood (hamoodey) for Reflection's Build with Beam contest. Beam: <code>Beam-1.0-501B-A23B</code> via the Reflection API.</p></body></html>