core_divide: divides a C leaf simultaneously on ALL storeys that share that
path, maintaining staircase consistency as an atomic invariant rather than
requiring multi-step recovery.
core_undivide: reverses core_divide consistently across all floors, merging
a C sub-core back into a single C leaf everywhere.
level_fix: atomically moves a level-constrained room to its required floor
by retyping the largest leaf there and vacating the wrong-floor leaf to C.
Requires `reqs` (SpaceReq dict); disabled (zero probability) without it.
mutate() gains `reqs=None` parameter; driver.search() passes its already-
loaded reqs so level_fix fires during the main memetic loop.
Together these let the optimiser escape the deceptive valley around the
2-fail warmstart: level_fix moves l1 to level 0 (reducing fails 2→1),
then core_divide can split the C core to accommodate the displaced t3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cross-storey equivalent of mutate_retype. Directly addresses
level-constraint failures ("l1 on wrong level") by moving a room type
from one floor to another without changing topology or geometry.
Registered in MUTATIONS at default weight (1.0); no drastic geometry
perturbation so it does not need the reduced level_add/delete weight.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously level_add copied the top storey exactly, duplicating all
named programme rooms and immediately triggering space-count failures
for every room on the new floor. The lex outer-search comparison
(-n_fails, score) then always rejected the multi-storey child because
its fail count was far higher than the single-storey parent.
Fix: retype all named-room leaves on the new storey to generic C or O
before admitting the child. The outer search then retypes them
incrementally via the normal retype operator. This allows level_add to
produce designs with the same fail count as the parent (storey_minimum
fail removed, no duplication fails added), making the multi-storey
transition visible to the lex selector.
Result on programme-house cold start (init.dom, 100k evals, 4 workers):
before: 6 fails, single-storey, stuck after 40k evals
after: 4 fails, two-storey, still improving at 100k
Also adds examples/harbor-house/ from urb/examples for future runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>