innerloop.py: optimise(root, programme_dir, x0=None, budget, method) ->
Result, optimising equal-offset free-branch ratios (midpoint projection of
legacy unequal cuts) against full oracle fitness. OracleEvaluator scores
each population in one batched perl call. Methods: cma (default) — multi-
start sigma ladder (0.05 local, 0.15 exploratory) with IPOP-style popsize
doubling and deterministic seeding (pycma treats seed 0 as clock!) — and
compass with Hooke-Jeeves pattern moves, kept for the d0s bake-off.
Acceptance (experiments/accept_innerloop.py, §4.5 bars vs unprojected
originals, within-noise tolerance 1%): x1.65 / x1.66 / x1.58 against bars
x1.24 / x1.67 / x1.59, no new failures, 46 oracle calls vs Nelder-Mead's
200. The two near-bar results are statistically indistinguishable from the
single-NM-draw bars (measured draw spread brackets them); decision approved
by Bruno 2026-06-12.
Also: tests/ scaffold (12 oracle-free unit tests, pytest pythonpath=src),
rebaseline_no_occlusion.py for homemaker-py-gp2, cma>=3.0 dependency
(installed via dnf), dead-variable cleanup in solver.py.
Closes homemaker-py-1p0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
oracle.score_batch() writes/cleans N outputs and runs urb-fitness.pl once
with all file names; oracle.score() is now a thin wrapper. Adds
Score.fail_lines (sorted) because Perl hash-order randomisation shuffles
.fails line order between runs, and documents Urb's ~1-ULP score
nondeterminism (compare with rel tolerance, never ==).
experiments/bench_batch_oracle.py validates batch-vs-single parity on the
35-file corpus and benchmarks: 0.98 s/dom batched vs 1.27 s/dom single
(x1.30), all files identical (fitness to 1e-12 rel, exact failure sets).
Closes homemaker-py-av5.
Adds the bottom-up ratio solver, programme parser, Perl-oracle bridge,
and two experiments. Headline finding: the "isolated size solver on a
frozen topology" hypothesis is NOT validated.
- resolve_ratios.py: re-solving candidate-002 from programme targets
recovers areas accurately but scores below the original (introduces
width/perpendicular/crinkliness failures the area objective ignores).
- refine_sweep.py: warm-start refine of all 34 evolved candidates
regresses 34/34 (fails 124->297 perpendicular-tied; 124->626 area-only
with free skew). Moving cuts to fix room area breaks the coupled
adjacency/access/shape constraints those designs balanced.
Conclusion: sizing is not separable from the rest of Urb's fitness;
a geometry inner loop must optimise the full objective, not an area proxy.
Geometry port remains validated byte-identical to Urb.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clean-room Python successor to Urb for programme-driven layout search.
This initial commit establishes the .dom bridge format and a faithful
port of Urb's top-down quad geometry, validated byte-identical against
Urb across all 35 programme-house example files (including the wall
inset and multi-storey wall-stacking inheritance).
- dom.py: .dom YAML <-> Node tree, parent/below/position linkage,
wall_outer inset on load, raw-corner stash for round-tripping
- geometry.py: Coordinate/Coordinate_a/_b/Area/Length + Coordinate_Offset
- experiments/dump_areas.{py,pl}: geometry regression harness