Update DESIGN.md with findings from source review of Urb
- §4.5: slide() re-randomises cuts (no fine-tuning operator exists) — strengthens the geometry-headroom explanation - §4.6: throughput arithmetic shows native fitness effectively gates topology search at scale; oracle suffices for Phase 1 + small Phase 2 - §5: new decision 6 — Lamarckian warm-start of the inner loop; penalty reshaping must preserve inner-loop cliff protection - §6: native-fitness port scope expanded (occlusion/daylight subsystem, cost denominator, structural failures, missing-space failure stacking, two-phase graph build, has_vertical_connection stub) - §7: Phase 2 re-scoped as small-scale proof with budgets in oracle evaluations; Phase 1 gains warm-vs-cold + optimiser bake-off experiments - §8: risks updated (reshaping tension, height DOF, confirmed t3 bug)
This commit is contained in:
parent
40b97ac74c
commit
8efe25601f
1 changed files with 121 additions and 37 deletions
158
DESIGN.md
158
DESIGN.md
|
|
@ -1,7 +1,12 @@
|
||||||
# homemaker — Design & Plan
|
# homemaker — Design & Plan
|
||||||
|
|
||||||
**Status:** validated direction, pre-implementation.
|
**Status:** validated direction, pre-implementation. Reviewed against the Urb
|
||||||
**Audience:** a fresh session that will break this into `bd` (beads) tasks. Self-contained — assumes no memory of the originating conversation.
|
source 2026-06-12; review findings folded in (see §4.5 evidence note, §4.6
|
||||||
|
throughput arithmetic, §5 decision 6, §6 port-scope expansion, §7 re-scoped
|
||||||
|
phases, §8).
|
||||||
|
**Audience:** a fresh session that will break this into `bd` (beads) tasks
|
||||||
|
(note: no beads database exists yet — run `bd init` first). Self-contained —
|
||||||
|
assumes no memory of the originating conversation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -143,12 +148,17 @@ the proxy solver protects the full-objective optimiser.**
|
||||||
|
|
||||||
**Implications:**
|
**Implications:**
|
||||||
- There is large, unclaimed **geometry headroom above every EA design** — even
|
- There is large, unclaimed **geometry headroom above every EA design** — even
|
||||||
the best. Urb's EA under-optimises geometry (its slide mutations are weak/
|
the best. Urb's EA under-optimises geometry: source inspection confirms
|
||||||
under-applied).
|
`slide()` (Mutate.pm:256-269) *re-randomises* the cut position uniformly
|
||||||
|
across the span — Urb has **no fine-tuning geometry operator at all**, which
|
||||||
|
fully explains the headroom.
|
||||||
- A **full-objective geometry inner loop is genuinely valuable** (the proxy
|
- A **full-objective geometry inner loop is genuinely valuable** (the proxy
|
||||||
solver is not).
|
solver is not).
|
||||||
- The EA/search should therefore own **topology**; geometry is delegated to the
|
- The EA/search should therefore own **topology**; geometry is delegated to the
|
||||||
inner loop. This is the memetic architecture (§5).
|
inner loop. This is the memetic architecture (§5).
|
||||||
|
- Corroboration for §4.3: Urb's own mutations use equal offsets
|
||||||
|
(`Divide($division, $division)`) — equal-offset cuts match how every corpus
|
||||||
|
design was generated.
|
||||||
|
|
||||||
### 4.6 Oracle throughput (measured)
|
### 4.6 Oracle throughput (measured)
|
||||||
`urb-fitness.pl` scores **many `.dom` files per invocation**, so the Perl startup
|
`urb-fitness.pl` scores **many `.dom` files per invocation**, so the Perl startup
|
||||||
|
|
@ -162,10 +172,20 @@ Consequences:
|
||||||
**population/parallel-evaluating optimisers** (CMA-ES, differential evolution,
|
**population/parallel-evaluating optimisers** (CMA-ES, differential evolution,
|
||||||
island EA, pattern search) over inherently sequential ones (Nelder-Mead), both
|
island EA, pattern search) over inherently sequential ones (Nelder-Mead), both
|
||||||
inner loop and outer search, so a whole generation scores in one oracle call.
|
inner loop and outer search, so a whole generation scores in one oracle call.
|
||||||
- The memetic search can be **prototyped on the batched oracle** before any
|
- **Do the arithmetic before scoping topology search on the oracle.** §4.5 used
|
||||||
fitness port. A **native Python fitness is strongly desirable** (faster
|
~200 inner evaluations per topology ⇒ ~3 min/topology at 1 s/dom. A run
|
||||||
assessment, independence, enables penalty reshaping and large programmes) but
|
comparable to `urb-evolve` (pop 128 × 768 generations) is *years* of oracle
|
||||||
is **a later speed/scale optimisation, not a hard gate**.
|
time; even 32 topologies × 100 generations with a trimmed 50-eval inner loop
|
||||||
|
is ~2 days. Therefore:
|
||||||
|
- The oracle supports **Phase 1 fully** and **Phase 2 only as a small-scale
|
||||||
|
proof** (tens of topologies, budgets counted in oracle calls).
|
||||||
|
- A **native Python fitness is effectively a gate for topology search at any
|
||||||
|
real scale** — not merely a later optimisation. (It also brings
|
||||||
|
independence, penalty reshaping, and large programmes.)
|
||||||
|
- **Warm-starting the inner loop from the parent's optimised ratios**
|
||||||
|
(Lamarckian inheritance, §5 decision 6) is the main lever for cutting the
|
||||||
|
per-topology cost — with high-locality moves most cuts survive a mutation,
|
||||||
|
so an order-of-magnitude reduction is plausible. Measure this in Phase 1.
|
||||||
|
|
||||||
### 4.7 The `0.5^n` failure penalty is a first-order pathology
|
### 4.7 The `0.5^n` failure penalty is a first-order pathology
|
||||||
Multiplicative `0.5^n` over failure *count* (a) makes the landscape a cliff (no
|
Multiplicative `0.5^n` over failure *count* (a) makes the landscape a cliff (no
|
||||||
|
|
@ -207,14 +227,26 @@ Key decisions, all evidence-backed:
|
||||||
regulariser, via `Below`-inheritance).
|
regulariser, via `Below`-inheritance).
|
||||||
3. **Prefer population/batch-evaluating optimisers** so the batched oracle is
|
3. **Prefer population/batch-evaluating optimisers** so the batched oracle is
|
||||||
efficient (§4.6). A **native Python fitness** (faithful to Urb, validated
|
efficient (§4.6). A **native Python fitness** (faithful to Urb, validated
|
||||||
against the oracle on the 35-file corpus) is a later speed/scale optimisation
|
against the oracle on the 35-file corpus) **gates topology search at scale**
|
||||||
— desirable, not a gate.
|
(§4.6 arithmetic); the oracle suffices for the inner loop and a small-scale
|
||||||
|
topology-search proof only.
|
||||||
4. **Reshape the failure penalty** (§4.7) — additive/soft or multi-objective —
|
4. **Reshape the failure penalty** (§4.7) — additive/soft or multi-objective —
|
||||||
so the search has a gradient and isn't dominated by flag-count.
|
so the search has a gradient and isn't dominated by flag-count. **Caution:**
|
||||||
|
the `0.5^n` cliff is what *protects* the inner loop from trading into new
|
||||||
|
failures (§4.5); reshaping must not lose that property. Candidate
|
||||||
|
resolutions: keep the cliff inside the inner loop only, lexicographic
|
||||||
|
ordering (failure count first, score second), or genuine multi-objective
|
||||||
|
Pareto. Decide in Phase 4 with measurements.
|
||||||
5. **Representation upgrade (later):** canonical slicing encoding (normalized
|
5. **Representation upgrade (later):** canonical slicing encoding (normalized
|
||||||
Polish expression / skewed slicing tree, Wong–Liu) for redundancy-free,
|
Polish expression / skewed slicing tree, Wong–Liu) for redundancy-free,
|
||||||
high-locality topology moves; bottom-up shape feasibility checks. Defer until
|
high-locality topology moves; bottom-up shape feasibility checks. Defer until
|
||||||
the inner loop + native fitness are in place.
|
the inner loop + native fitness are in place.
|
||||||
|
6. **Lamarckian geometry inheritance.** A child topology's inner loop
|
||||||
|
warm-starts from the parent's optimised ratios (cuts that survive the
|
||||||
|
topology move keep their values; new cuts get heuristic defaults). This is
|
||||||
|
the main cost lever for the memetic loop (§4.6) and a standard memetic
|
||||||
|
design choice (Lamarckian vs Baldwinian — we write the optimised geometry
|
||||||
|
back into the genome). Validate the warm-vs-cold speedup in Phase 1.
|
||||||
|
|
||||||
What we are **not** doing: the bottom-up area-proxy solver; independent-offset
|
What we are **not** doing: the bottom-up area-proxy solver; independent-offset
|
||||||
cuts; non-slicing representations (sequence-pair/B*-tree — excluded by §2).
|
cuts; non-slicing representations (sequence-pair/B*-tree — excluded by §2).
|
||||||
|
|
@ -230,11 +262,11 @@ cuts; non-slicing representations (sequence-pair/B*-tree — excluded by §2).
|
||||||
| `programme.py` | ✅ done | extend as fitness needs grow |
|
| `programme.py` | ✅ done | extend as fitness needs grow |
|
||||||
| `oracle.py` (Perl bridge) | ✅ done | throwaway; the validation reference |
|
| `oracle.py` (Perl bridge) | ✅ done | throwaway; the validation reference |
|
||||||
| `solver.py` (area proxy) | ⚠️ keep as artifact | falsified; do not build on it |
|
| `solver.py` (area proxy) | ⚠️ keep as artifact | falsified; do not build on it |
|
||||||
| **geometry inner loop** | ❌ to build | full-objective ratio optimiser (DOF = free branches); batch/population so the oracle batches |
|
| **geometry inner loop** | ❌ to build | full-objective ratio optimiser (DOF = free branches); batch/population so the oracle batches; warm-start support (§5.6) |
|
||||||
| **topology genome + operators** | ❌ to build | base tree + per-floor deltas; high-locality moves |
|
| **topology genome + operators** | ❌ to build | base tree + per-floor deltas; high-locality moves |
|
||||||
| **search driver** | ❌ to build | memetic EA / SA over topology |
|
| **search driver** | ❌ to build | memetic EA / SA over topology; small-scale on oracle, full-scale needs native fitness |
|
||||||
| **native fitness** | ❌ later | speed/scale optimisation (not gating); port + validate vs oracle |
|
| **native fitness** | ❌ to build | **gates topology search at scale** (§4.6); port + validate vs oracle; scope is larger than the term list — see below |
|
||||||
| **penalty reshaping** | ❌ to design | additive/soft or multi-objective |
|
| **penalty reshaping** | ❌ to design | additive/soft or multi-objective; must preserve inner-loop cliff protection (§5.4) |
|
||||||
| canonical encoding (Polish expr.) | ❌ later | representation upgrade once core lands |
|
| canonical encoding (Polish expr.) | ❌ later | representation upgrade once core lands |
|
||||||
|
|
||||||
Urb fitness terms the native port must reproduce (all couple to geometry):
|
Urb fitness terms the native port must reproduce (all couple to geometry):
|
||||||
|
|
@ -244,26 +276,66 @@ outside ratios, min internal area.** Source of truth:
|
||||||
`/home/bruno/src/urb/lib/Urb/Dom/Fitness/ProgrammeDriven.pm` and the `Storey`/
|
`/home/bruno/src/urb/lib/Urb/Dom/Fitness/ProgrammeDriven.pm` and the `Storey`/
|
||||||
`Building`/`Leaf`/`Base` submodules.
|
`Building`/`Leaf`/`Base` submodules.
|
||||||
|
|
||||||
|
**Port scope beyond the term list** (found by source review — budget for these):
|
||||||
|
|
||||||
|
- **Daylight + occlusion subsystem.** `quality_daylight` (Leaf.pm:281-296)
|
||||||
|
needs the occlusion field and sun-path model (`Urb::Misc::Sun`,
|
||||||
|
`Urb::Field::Occlusion`, CIESky); `quality_uncrinkliness` also takes the
|
||||||
|
occlusion object. This is a whole subsystem, not a term. (Indoor spaces
|
||||||
|
return 1; the cost is for outdoor spaces and crinkliness.)
|
||||||
|
- **The cost denominator.** Fitness is value/**cost**: per-leaf area costs,
|
||||||
|
interior/exterior wall edge costs, boundary costs
|
||||||
|
(Leaf.pm:194-251, Storey.pm:122-147). Cost couples to geometry too.
|
||||||
|
- **Structural failures** not in the term list: "edge too long" (>8 m, two
|
||||||
|
variants), "unsupported covered outside", "covered outside above ground",
|
||||||
|
"level N not connected".
|
||||||
|
- **Missing-space failure stacking** (ProgrammeDriven.pm:192-212): a missing
|
||||||
|
space generates 2 base failures plus one per size/width/proportion/adjacency/
|
||||||
|
level requirement — up to ~7 failures. Penalty reshaping (Phase 4) must
|
||||||
|
preserve this hierarchy or the search will happily drop rooms.
|
||||||
|
- **Two-phase graph build**: adjacency/level/vertical checks run on the
|
||||||
|
*unmerged* tree; graphs are rebuilt after `Merge_Divided` for storey
|
||||||
|
processing (ProgrammeDriven.pm:83-103). Easy to get subtly wrong; the
|
||||||
|
35-file validation gate will catch it, but anticipate it.
|
||||||
|
- **Known stub to decide on** (fidelity-vs-fix, §8.1):
|
||||||
|
`has_vertical_connection` (ProgrammeDriven.pm:399-423) matches any leaf of
|
||||||
|
the target type anywhere on the level below — no spatial-overlap check. A
|
||||||
|
faithful port reproduces the bug; decide explicitly.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Phased roadmap
|
## 7. Phased roadmap
|
||||||
|
|
||||||
- **Phase 0 — diagnostics** *(done this session)*: geometry port validated;
|
- **Phase 0 — diagnostics** *(done)*: geometry port validated; proxy solver
|
||||||
proxy solver falsified; full-fitness geometry headroom validated; oracle
|
falsified; full-fitness geometry headroom validated; oracle throughput
|
||||||
throughput measured (~1 s/dom batched).
|
measured (~1 s/dom batched).
|
||||||
- **Phase 1 — geometry inner loop (on batched oracle)**: full-objective ratio
|
- **Phase 1 — geometry inner loop (on batched oracle)**: full-objective ratio
|
||||||
optimiser; use a population/batch optimiser so a generation scores in one
|
optimiser; use a population/batch optimiser so a generation scores in one
|
||||||
oracle call. Reproduce/exceed the §4.5 gains. Integrate as
|
oracle call. Reproduce/exceed the §4.5 gains. Integrate as
|
||||||
`optimise(topology) -> (geometry, fitness)`.
|
`optimise(topology, x0=None) -> (geometry, fitness)`. Two cheap experiments
|
||||||
- **Phase 2 — topology search (on batched oracle)**: base-tree + per-floor-delta
|
belong here: (a) **warm-vs-cold start** — quantify the §5.6 speedup;
|
||||||
genome, high-locality operators, memetic driver wrapping the Phase-1 inner
|
(b) **optimiser bake-off** — DOF is only ≈ rooms−1, so batched multi-start
|
||||||
loop. Compare against `urb-evolve` from the same seeds/programmes.
|
pattern search may beat CMA-ES on simplicity; measure, don't commit blind.
|
||||||
- **Phase 3 — native Python fitness**: port Urb's programme-driven fitness;
|
*Gate:* match §4.5 gains at materially lower oracle-call budget.
|
||||||
validate score + failure set against the oracle across the 35-file corpus
|
- **Phase 2 — topology search, small-scale proof (on batched oracle)**:
|
||||||
(float tolerance, identical failure sets). Swap behind the same interface for
|
base-tree + per-floor-delta genome, high-locality operators, memetic driver
|
||||||
speed/scale; retire the oracle.
|
wrapping the Phase-1 inner loop. **Explicitly small** (§4.6 arithmetic):
|
||||||
- **Phase 4 — penalty reshaping**: replace `0.5^n` with additive/soft or
|
tens of topologies, budgets counted in **oracle evaluations**, not
|
||||||
multi-objective (easier once fitness is native); measure landscape + search.
|
generations. Compare against `urb-evolve` from the same seeds/programmes *at
|
||||||
|
equal oracle-call budget* (urb-evolve has diversity injection/culling baked
|
||||||
|
in, so generations are not comparable). *Gate:* memetic loop beats
|
||||||
|
equal-budget urb-evolve. Scaling up waits for Phase 3.
|
||||||
|
- **Phase 3 — native Python fitness** (**gates scaled topology search**): port
|
||||||
|
Urb's programme-driven fitness — including the §6 "port scope beyond the
|
||||||
|
term list" items (occlusion/daylight subsystem, cost denominator, structural
|
||||||
|
failures, failure stacking, two-phase graph build). Validate score + failure
|
||||||
|
set against the oracle across the 35-file corpus (float tolerance, identical
|
||||||
|
failure sets). Swap behind the same interface; retire the oracle. Then
|
||||||
|
re-run Phase 2 at scale.
|
||||||
|
- **Phase 4 — penalty reshaping**: replace `0.5^n` with additive/soft,
|
||||||
|
lexicographic, or multi-objective (easier once fitness is native), while
|
||||||
|
preserving the inner loop's no-new-failures protection (§5.4) and the
|
||||||
|
missing-space hierarchy (§6); measure landscape + search.
|
||||||
- **Phase 5 — representation upgrade**: canonical slicing encoding
|
- **Phase 5 — representation upgrade**: canonical slicing encoding
|
||||||
(Polish expression) + bottom-up shape feasibility; scale to larger programmes.
|
(Polish expression) + bottom-up shape feasibility; scale to larger programmes.
|
||||||
|
|
||||||
|
|
@ -275,18 +347,30 @@ Each phase has a concrete go/no-go gate; do not advance on faith.
|
||||||
|
|
||||||
1. **Native-fitness fidelity vs simplification.** Port Urb's fitness exactly
|
1. **Native-fitness fidelity vs simplification.** Port Urb's fitness exactly
|
||||||
(maximise comparability) or take the opportunity to clean up known issues
|
(maximise comparability) or take the opportunity to clean up known issues
|
||||||
(the `0.5^n` cliff, the t3 width-default contradiction below)? Recommend:
|
(the `0.5^n` cliff, the t3 width-default contradiction below, the
|
||||||
*port faithfully first*, validate, then reshape in Phase 3.
|
`has_vertical_connection` no-overlap stub — §6)? Recommend: *port faithfully
|
||||||
2. **Programme contradictions exist.** e.g. t3 (3 m² WC) inherits a 4 m default
|
first* (bugs included), validate, then reshape in Phase 4.
|
||||||
width — geometrically impossible; the original "passes" only by failing
|
2. **Programme contradictions exist.** e.g. t3 (3 m² WC) inherits the 4 m
|
||||||
`size` instead. Need a sane width default scaled to area, or per-room widths.
|
`width_inside` default (Fitness/Base.pm:60) — geometrically impossible; the
|
||||||
3. **Inner-loop optimiser choice.** Nelder-Mead worked for diagnostics; for
|
original "passes" only by failing `size` instead. *Confirmed in source.*
|
||||||
production consider CMA-ES, Powell, or gradient-based once native fitness is
|
Need a sane width default scaled to area, or per-room widths.
|
||||||
differentiable-ish. DOF is small (≈ rooms−1).
|
3. **Inner-loop optimiser choice.** Nelder-Mead worked for diagnostics; DOF is
|
||||||
|
small (≈ rooms−1, 6–7 on the corpus), so CMA-ES may be overkill — batched
|
||||||
|
multi-start pattern search parallelises across the oracle and is simpler.
|
||||||
|
Resolve via the Phase 1 bake-off, not upfront. Gradient-based becomes an
|
||||||
|
option once native fitness is differentiable-ish.
|
||||||
4. **Search algorithm for topology.** Memetic GA (keep crossover — now
|
4. **Search algorithm for topology.** Memetic GA (keep crossover — now
|
||||||
meaningful, since a subtree = a contiguous region) vs simulated annealing
|
meaningful, since a subtree = a contiguous region) vs simulated annealing
|
||||||
(the floorplanning workhorse with M1/M2/M3 moves on Polish expressions).
|
(the floorplanning workhorse with M1/M2/M3 moves on Polish expressions).
|
||||||
5. **End-state confirmed: 100% Python**; Perl oracle is scaffold only.
|
5. **Penalty reshaping vs inner-loop protection.** One fitness shape cannot
|
||||||
|
naively be both soft for the outer search and cliff-protected for the inner
|
||||||
|
loop (§5.4). Resolve in Phase 4: cliff-inside-inner-loop, lexicographic, or
|
||||||
|
Pareto.
|
||||||
|
6. **Other continuous DOF are out of scope for Phase 1 — deliberately.**
|
||||||
|
Floor-to-floor height is an Urb mutation (Mutate.pm:279-291, bounded
|
||||||
|
2.7–3.6 m) and feeds cost and stair fit; stair riser/width similar. Cut
|
||||||
|
ratios dominate. Revisit (+1 DOF per storey) if Phase 2 plateaus.
|
||||||
|
7. **End-state confirmed: 100% Python**; Perl oracle is scaffold only.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue