Repository navigation
Layout presets by graph size: large graphs in seconds, not minutes - #81
Merged
Merged
Conversation
The ELK options in WorkflowLayoutConfigurator were tuned on diagrams of a few dozen nodes and applied to every graph alike. Measured standalone on the exact graphs the engine hands to elk.layout, four real netlist modules of a hardware product, the default preset took 5.8 s on 212 nodes, 41 s on 344, 334 s on 1528 and overflowed ELK's call stack on 3293. Two options carry the cost: NETWORK_SIMPLEX node placement (344 nodes go from 41 s to 3.8 s by that switch alone) and NODES_AND_EDGES model order (ten-fold on the cheap presets); crossing minimisation is not it. The configurator now counts the graph's nodes and edges, recursively and without compartments, and lays a preset over the default by size: above 150 nodes or 400 edges the middle preset (model order off, Brandes-Koepf placement, thoroughness 3: 1.7 s on 344 nodes, 9.3 s on 1528), above 1000 nodes or 3000 edges the fast preset (crossing minimisation off, simple placement, thoroughness 1, polyline routing: 3.8 s on 1528 nodes, 11.5 s on 3293). The fast preset routes polylines because orthogonal routing of a simply placed graph took 2289 s on 3293 nodes and 31 s on 1528. A graph under the first threshold gets exactly the options it did, byte for byte, so every existing layout test holds. The thresholds and presets are exported constants with the measurements in their doc comment. The engine merges a layout's own graph options over the preset as before, except in the fast tier, where it re-asserts the fast preset: Untangled's Brandes-Koepf placement is among the options that take minutes there. Each layout logs `[dialogram perf] layout <network>: tier=... nodes=... edges=...`, and a fast-tier layout tells the user in the diagram's status line that it was laid out roughly, through the ActionDispatcher the engine factory now passes in. Thirteen tests hold the counting, the thresholds, the presets, the fast-tier override and the log and status lines on synthetic graphs. Claude-Session: https://claude.ai/code/session_01UtmwstZRcWJeg3C69s1TnS
endrix
force-pushed
the
feat/size-tiered-layout
branch
from
October 17, 2026 07:00
bae9594 to
c7874ac
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The server's ELK layout now picks its option preset by the graph's node and edge counts, so large graphs lay out in seconds instead of minutes (or a stack overflow). Graphs up to the first threshold get exactly the options they did before, byte for byte.
What was measured
ELK standalone on the exact graphs the engine hands to
elk.layout, four real netlist modules of a hardware product:Two options carry the default preset's cost:
elk.layered.nodePlacement.strategy: NETWORK_SIMPLEX(dominant: switching it alone takes 344 nodes from 41 s to 3.8 s) andelk.layered.considerModelOrder.strategy: NODES_AND_EDGES(ten-fold on the cheap presets). Crossing minimisation is not the cost. Orthogonal routing of a simply placed graph is: the fast preset was measured with both routings on this branch, and ORTHOGONAL is 38 minutes onconv, so the fast preset routes polylines. The middle preset stays orthogonal, where POLYLINE was the slower of the two.Tiers
Constants in
packages/diagram-server/src/server/layout-configurator.ts, with the measurements in their doc comment:MIDDLE_LAYOUT_OPTIONS): above 150 nodes or 400 edges. Default plusconsiderModelOrder.strategy: NONE,nodePlacement.strategy: BRANDES_KOEPF,thoroughness: 3.FAST_LAYOUT_OPTIONS): above 1000 nodes or 3000 edges. Middle pluscrossingMinimization.strategy: NONE,nodePlacement.strategy: SIMPLE,thoroughness: 1,edgeRouting: POLYLINE.The count is recursive over the graph's children, GNodes that are not compartments and GEdges. The rule reads nothing else, so the core learns nothing product-specific.
The tier applies wherever the configurator does: the initial layout on open, Layout Diagram, boundary flow, Untangled, Free Ports and the Free Ports probe. The engine still merges a layout's own graph options (
UNTANGLED_ELK_OPTIONS) over the preset, except in the fast tier, where it re-asserts the fast preset: Brandes-Koepf placement is among the options that take minutes there. In the middle tier Untangled's placement is the middle preset's already. Documented in both files.Each layout logs
[dialogram perf] layout <network>: tier=<default|middle|fast> nodes=<n> edges=<m>beside the existingelk=timing, and a fast-tier layout sends aStatusAction("N nodes: laid out roughly. Run Layout for the full layout.", 10 s) through the ActionDispatcher the engine factory now passes in; the stock GLSP status module shows it, so no client code changed.Tests
packages/diagram-server/test/layout-tier.test.ts, 13 tests on synthetic GGraph trees: the counting (depth, compartments skipped), the thresholds (at and above), the default preset pinned for a small graph and equal to an empty graph's, the middle and fast presets by node and by edge count, the fixed post-drag configuration untouched, Untangled overriding in the middle tier and overridden in the fast tier, the log line and the status action. The existingelk-layout-port-order.test.tssuite runs the real ELK on the 40-node speculation flow and reportstier=default, every crossing count unchanged. No netlist dump runs in the suite.Gates
npm run typecheck: PASSED (5/5 packages)npm run check:neutrality: PASSED (5/5 gates)npm run build: succeedednpm test --workspace packages/diagram-server: 68 files, 339 tests passednpm test --workspace packages/sidecar-toolkit: 42 files, 264 tests passedhttps://claude.ai/code/session_01UtmwstZRcWJeg3C69s1TnS