Skip to content

feat(layout): crossing reduction and coordinate assignment for batched builds - #883

Merged
christian-byrne merged 2 commits into
mainfrom
fix/layout-batch-crossing-and-coords
Sep 17, 2026
Merged

christian-byrne merged 2 commits into
mainfrom
fix/layout-batch-crossing-and-coords

Conversation

@christian-byrne

Copy link
Copy Markdown
Contributor

Stacked on #882. Rebase onto main once that merges.

Why the batch path

Langfuse, 600 sampled apply_ops calls on production traffic:

batches that create nodes 489
…carrying their connect ops in the same batch 481 = 98%
node creations via apply_ops 3,120
node creations via serial add_node 461

So ~87% of layout quality is decided by assign_positions, not the serial cascade. It is also the weaker of the two placers: it implemented one of Sugiyama's three steps.

Step Status before
1. Layer assignment — which column ✅ longest-path relaxation
2. Crossing reduction — order within a column ❌ ops-array order
3. Coordinate assignment — y ❌ stacked from base_y

That is why a batched build could be correctly layered and still look wrong: three inputs feeding one sampler crossed their wires purely because of the order they were authored in, and a one-node column sat flush with the top of a five-node column instead of level with the node it feeds.

Changes

  • Step 2 — order each column by the barycentre of its neighbours, forward and backward sweeps, insertion rank as a deterministic tiebreak. Determinism matters here beyond tidiness: placement is frozen at mint time so replay converges, and a non-deterministic ordering would break that.
  • Step 3 — centre each node on the mean centre of its predecessors, then push down only as far as column order requires.
  • Pinned siblings are now obstacles. collides() compared movable nodes only against existing workflow nodes, so a new node could be placed on top of a new node pinned to an explicit at.

Measured

A fixture whose ops are deliberately authored in a crossing order:

crossings mean input-alignment deviation
before 3 176px
after 0 0px

A realistic SDXL graph: alignment deviation 46.6px → 30.4px, crossings 0 both ways.

The scoring helper lands in the test file so future layout changes are measured rather than eyeballed — that metric is the prerequisite for the A/B/C prototype comparison.

Tests

Three regression tests, verified red on the parent commit and green here. Full suite: 45 failures vs 46 on parent, zero introduced.

One thing shipping unproven

The direct-jump collision change — replacing a ROW_GAP-at-a-time march bounded at _GUARD * ROW_GAP = 40,000px — has no red-green test, and there is a comment in the test file saying so. Every fixture I tried either did not reach the collision branch or was cleared by the old march as well, so the intended proof (old code exhausts its budget and leaves an overlap) was never demonstrated. Treat it as a robustness and efficiency change, not a verified bug fix.

@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 8ccfcd52-033a-49b7-9fa1-1f3839e0ffa6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

…d builds

Langfuse says 98% of apply_ops batches that create nodes carry their connects in
the same batch, and 3,120 of 3,581 sampled node creations come through
apply_ops -- so the BATCH path is where ~87% of layout quality is decided, not
the serial one. It was also the weaker of the two: assign_positions implemented
only Sugiyama's step 1.

  step 1 layer assignment  -> which column      ALREADY DONE
  step 2 crossing reduction -> order in column  MISSING (ops-array order)
  step 3 coordinate assignment -> y            MISSING (stacked from base_y)

Step 2: order each column by the barycentre of its neighbours, forward and
backward sweeps, insertion rank as a deterministic tiebreak so replay stays
convergent. Three inputs feeding one sampler no longer cross their wires purely
because of the order they were authored in.

Step 3: centre each node on the mean centre of its predecessors, then push down
only as far as column order requires. A one-node column used to sit flush with
the top of a five-node column instead of level with the node it feeds.

Also: collides() compared movable nodes only against EXISTING workflow nodes, so
a new node could be placed on top of a new node PINNED to an explicit `at`.
Pinned siblings are now obstacles.

Measured on a fixture whose ops are authored in a crossing order:
  before  crossings=3  mean input-alignment deviation=176px
  after   crossings=0  mean input-alignment deviation=0px
and on a realistic SDXL graph, alignment deviation 46.6px -> 30.4px.

Three regression tests plus a scoring helper, verified red on the parent.

The direct-jump collision change (replacing a ROW_GAP-at-a-time march bounded at
_GUARD * ROW_GAP = 40,000px) ships WITHOUT a red-green test and is flagged as
such in the test file: every fixture tried either missed the collision branch or
was cleared by the old march too. Robustness change, not a verified fix.

Suite: 45 failures vs 46 on parent, zero introduced.
@christian-byrne
christian-byrne force-pushed the fix/layout-batch-crossing-and-coords branch from 69b9017 to a3e1dbb Compare September 17, 2026 04:51
@coderabbitai
coderabbitai Bot requested a review from skishore23 September 17, 2026 04:51
@christian-byrne

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

This repo is public and the ids reference an internal program board nobody
outside can resolve. The section headers say what the tests cover without
them.
@christian-byrne

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@comfy-greenlight-bot

comfy-greenlight-bot commented Sep 17, 2026

Copy link
Copy Markdown

Swarmhost agentic review

The detailed evaluation is available to employees in the internal Slack review thread.

Updated by Swarmhost's agentic review process.

@huntcsg huntcsg left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR extends batch layout from longest-path column assignment to a bounded barycentre pass that reorders nodes within columns, then vertically centres movable nodes on already-positioned movable predecessors. It also treats explicitly positioned new nodes as collision obstacles and replaces 40px-at-a-time collision shifts with direct jumps below the blocking rectangle. Three focused tests cover crossing reduction, simple predecessor alignment, and pinned-node collision avoidance.

Risks

  • The quoted comprehension is dense, but it is not an unchecked lookup: preds is populated only for endpoints already present in the movable-node map, every such adds[p] has a size, and the "pos" filter skips predecessors not yet placed. I do not see a crash path in that line under this function's construction.
  • The filtering immediately above it excludes pinned new nodes from preds. Consequently, a movable node connected from a pinned predecessor at [40, 1000] is placed at y=60 rather than centred near y=1000. The same limitation means crossing reduction ignores pinned neighbours. This is not worse than the parent behavior, but it is a meaningful gap in the advertised coordinate assignment for mixed explicit/automatic batches.
  • The direct-jump collision rewrite changes production behavior without a regression test, as the PR itself notes. Its geometry looks internally consistent, but this is the highest regression risk in the diff.
  • The scoring tests cover a clean two-column fixture only; they do not exercise mixed pinned/movable graphs, existing anchors, unequal node heights, long edges, or cyclic/invalid batches.

@christian-byrne
christian-byrne merged commit f35e41c into main Sep 17, 2026
18 of 19 checks passed
@christian-byrne
christian-byrne deleted the fix/layout-batch-crossing-and-coords branch September 17, 2026 19:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants