Skip to content

perf(layout): index stack alignment collisions and read stream hosts once - #568

Merged
Alpha9463 merged 3 commits into
mainfrom
perf/553-index-layout-collisions
Oct 2, 2026
Merged

Alpha9463 merged 3 commits into
mainfrom
perf/553-index-layout-collisions

Conversation

@Alpha9463

Copy link
Copy Markdown
Owner

Summary

Makes stacked-sheet layout linear by indexing the stack-alignment collision check. It also cuts routing time by reading stream hosts once per pass instead of once per segment per unit. Every drawing is unchanged. The benchmarks now report collision checks, routing-graph size and a dense synthetic sheet.

Related Issues

Closes #553
Refs: #547, #505, #567

Changes

  • pandid/layout/coordinates.py: a _RowIndex files units by height band, built once before _straighten's horizontal pass (which moves units only along x). _overlaps_x runs the same predicate on the units sharing the mover's bands. Units with non-finite extents still meet every query.
  • pandid/layout/conflicts.py, pandid/validate.py: read each unit's host once per pass. Asking per segment made every unit without a host fall through to Unit.__getattr__ and build an error message that was thrown away.
  • scripts/layout_bench.py: a checks column counting collision comparisons, measured in an untimed pass.
  • scripts/route_bench.py: --manifold N for a dense synthetic sheet, plus edges and graph MB columns.
  • tests/test_layout_spatial.py: the index checked against the full scan over a compact case table, and stacked-sheet frames checked against the unindexed layout.

How to Test

  1. Run the focused tests:

    python3 -m pytest -q tests/test_layout_spatial.py tests/test_layout.py tests/test_placement.py tests/test_layout_workflows.py tests/test_layout_conflicts.py
  2. Compare with main:

    python3 scripts/layout_bench.py -s 400 -s 800 -s 1600 -n 3 --shape stacked
    python3 scripts/route_bench.py -n 3
    python3 scripts/route_bench.py --manifold 4 --manifold 8 --manifold 16
  3. Run the full suite and gates:

    python3 -m pytest -q
    python3 -m ruff check .
    python3 -m ruff format --check tests
    python3 -m mypy pandid
    python3 -m pyright pandid

Evidence

Python 3.14.6, macOS arm64, best of 3, main and branch back to back:

main Branch
Stacked 1,600 blocks: collision checks 5,123,200 29,251
Stacked 1,600 blocks: layout 0.497 s 0.213 s
Stacked growth per doubling up to 2.9x 2.0–2.1x
Examples: route total 3.59–3.78 s 3.16 s
11_ethanol_pid route 0.85 s 0.59 s
  • Drawings unchanged: frames, routes and validation findings are identical to main for 46 drawings (21 examples as authored and with placement stripped, plus four benchmark sheets). The placement-stripped corpus still has 168 crossings.
  • Full suite: 11,494 passed, 188 skipped.
  • Lint and types: ruff, test formatting, mypy and git diff --check are clean. Pyright reports 0 errors plus the existing __all__ warning.
  • Dense sheets: the routing graph grows linearly (4.6 MB at 32 branches), but route() does not (59.8 s at 32 branches). That is tracked separately in Routing time grows about tenfold per doubling on dense parallel sheets #567.

Checklist

  • Base branch is main and the branch is mergeable
  • Commit subjects follow Conventional Commits; tracked work links its issue
  • Self-reviewed the full diff on the Files changed tab
  • Relevant local checks pass; all CI jobs pass before merge
  • Documentation and examples updated if behaviour changed (no behaviour change)
  • Before/after drawings included if rendered output changed (no rendered output changed)
  • Blocking review feedback resolved, if review was requested

🤖 Generated with Claude Code

Count the stack-alignment collision check's rectangle comparisons in layout_bench.py, in one untimed pass, so the index can be judged independently of the machine. Add a synthetic manifold sheet and graph edge and memory columns to route_bench.py to measure how routing grows with parallel branches.

Refs: #553
_straighten's horizontal pass compared every candidate move against every unit. Build a row index once before that pass, since it moves units only along x, and check each move against the units sharing its height bands. Decisions are unchanged: the same predicate runs on a superset of the units it can reject, and units with non-finite extents still meet every query. On a 1,600-block stacked sheet the checks fall from 5,123,200 to 29,251 and layout from 0.80 s to 0.31 s.

Refs: #553
analyze_conflicts, _exit_conflicts and geometry_issues asked each unit for its host once per route segment. Most units have none, so every ask fell through to Unit.__getattr__ and built an error message that was thrown away. Read the hosts once per pass instead. Decisions are unchanged; routing over the examples is about 15% faster, and 25% on
11_ethanol_pid. Also clear two type-checker notes in the row-index tests.

Refs: #553
@Alpha9463
Alpha9463 merged commit de61fd2 into main Oct 2, 2026
7 checks passed
@Alpha9463
Alpha9463 deleted the perf/553-index-layout-collisions branch October 2, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rebaseline dense-sheet layout and routing performance before indexing

1 participant