Skip to content

Improved Pattern.draw_graph visualization#444

Open
CodeMaverick2 wants to merge 18 commits intoTeamGraphix:masterfrom
CodeMaverick2:improve-draw-graph-visualization
Open

Improved Pattern.draw_graph visualization#444
CodeMaverick2 wants to merge 18 commits intoTeamGraphix:masterfrom
CodeMaverick2:improve-draw-graph-visualization

Conversation

@CodeMaverick2
Copy link
Contributor

@CodeMaverick2 CodeMaverick2 commented Feb 17, 2026

Before submitting, please check the following:

  • Make sure you have tests for the new code and that test passes (run nox)
  • If applicable, add a line to the [unreleased] part of CHANGELOG.md, following keep-a-changelog.
  • Format added code by ruff
    • See CONTRIBUTING.md for more details
  • Make sure the checks (github actions) pass.

Then, please fill in below:

Context (if applicable):

  • The current draw_graph uses color-only node differentiation, dashed lines for both edges and layer separators (causing visual confusion), and has no angle display or legend.

Description of the change:

  • Node shapes follow MBQC literature conventions: squares for inputs, filled circles for measured, empty circles for outputs
  • Replaced vertical dashed layer separators with gray labels and a "Measurement order" arrow
  • Graph edges are light dashed lines; correction arrows remain solid — clear visual hierarchy
  • Added show_measurements parameter to display measurement labels (plane + angle in units of π)
  • Added show_legend parameter for node/edge type legend
  • Added _format_measurement_label static method with unit tests
  • Regenerated baseline images

From issue -

523657674-01c8766d-25cb-4236-828b-76b94bc7cb19

Generated with PR changes -

after_issue_legend

The first image (before) is the example from the issue; the second (after) is the same pattern rendered with this PR.
Inputs (node 1) are now squares instead of red circles, measured nodes are filled black, Pauli-measured nodes (node 3) are blue, and outputs (2, 6, 7) are empty circles.
Dashed layer separators are replaced by a horizontal arrow with layer labels.
Measurement labels show plane and angle (e.g. XZ(π/10)).
Graph edges (light dashed) are visually distinct from correction arrows (solid). A legend explains all visual elements.

Related issue:
#387

@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 95.69892% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.38%. Comparing base (5f413e2) to head (ad2f699).

Files with missing lines Patch % Lines
graphix/visualization.py 95.69% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #444      +/-   ##
==========================================
+ Coverage   88.29%   88.38%   +0.09%     
==========================================
  Files          44       44              
  Lines        6535     6596      +61     
==========================================
+ Hits         5770     5830      +60     
- Misses        765      766       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@CodeMaverick2
Copy link
Contributor Author

CI fails on Python 3.10–3.13 due to a pre-existing bug in the veriphix reverse dependency, unrelated to this PR. Passes on Python 3.14 because veriphix is excluded from that version. All graphix tests and other reverse dependencies pass.

@thierry-martinez
Copy link
Collaborator

Veriphix CI is now fixed. It's all green now!

@CodeMaverick2
Copy link
Contributor Author

CodeMaverick2 commented Feb 18, 2026

Hey @thierry-martinez, could someone from the team please review my PR and let me know if any changes are required

@emlynsg
Copy link
Contributor

emlynsg commented Feb 19, 2026

Hi @CodeMaverick2 can you please post a few example outputs here? It will make it easier for us to review the updates you have made to the visual style.

@CodeMaverick2
Copy link
Contributor Author

@emlynsg here are a few examples using the pattern from the issue:
Basic graph (draw_graph(flow_from_pattern=False)):
pr_example_basic
With measurement labels (show_measurements=True):
pr_example_measurements

With measurements + legend (show_measurements=True, show_legend=True):
pr_example_full

From pattern with X/Z corrections (flow_from_pattern=True, show_measurements=True, show_legend=True):
pr_example_from_pattern

@CodeMaverick2
Copy link
Contributor Author

@emlynsg Let me know if there's any changes required Happy to address them

@emlynsg
Copy link
Contributor

emlynsg commented Feb 20, 2026

Hi @CodeMaverick2 , thanks for the continued good work. Appreciate your flexibility, visual PRs are definitely hard because of how much trial-and-error is involved.
Some further comments:

  • Want measurement order to be an optional parameter, default to true, that shows the direction of measurement and the layer labels
  • Measurement labels are still too far away from the nodes, want them to be close (could even be a smart way to show it inside the nodes) to avoid confusion
  • The labels, while more readable with the lighter background, look less professional than the existing plots. I suggest playing around with the opacity of the edges and corrections lines, and perhaps the size/bolding of the measurements, to ensure they are readable and professional looking
  • I think the layers would look more professional if (layers) was stated once somewhere, and the layer labels were just numbers. An alternative but similar suggestion to try a checkerboard approach (i.e. one slight shading for one layer, different/no shading for the next layer, alternate).
  • Similarly, the self-loop arrows look less professional and need to be tightened up to look more like the original plot
  • Node labels (numbers) should be centred in the circle/square they are in (currently a bit off-centre)
  • You will need to generate new images for mpl mock plot tests to cover the tests for labels/legend on/off

Thanks!

@CodeMaverick2
Copy link
Contributor Author

@emlynsg Thanks for the detailed feedback. Here are the changes addressing each point:

  1. show_measurement_order parameter - Added show_measurement_order: bool = True to draw_graph(). When set to False, layer labels and the measurement-order arrow
    are hidden.
  2. Measurement labels closer to nodes - Labels are now centered directly above each node instead of offset to the side. Removed the white background box.
  3. Label styling - Labels are now bold at fontsize 7 with no background box, which keeps them readable without cluttering the graph.
  4. Layer labels - Changed from L0, L1, L2 to just 0, 1, 2 with Layer stated once on the left. Removed the Measurement order subtitle text below the arrow.
  5. Self-loop arrows - Tightened the Bezier control points (reduced from 7 to 6 points, narrowed angle range from 170°–95° to 160°–100°, reduced max distance from 0.45 to 0.3).
  6. Node labels centered - Replaced nx.draw_networkx_labels with manual plt.text calls using ha="center", va="center" so labels are properly centered in their circles/squares.
  7. New mpl image comparison tests - Added test_draw_graph_with_labels (measurements + legend on) and test_draw_graph_without_labels (measurements + legend + order off) as @mpl_image_compare baseline tests, plus a smoke test for show_measurement_order=False. Baselines regenerated.

Example outputs:

Basic graph (draw_graph(flow_from_pattern=False)):

review_basic

With measurement labels (show_measurements=True):

review_measurements

Measurements + legend (show_measurements=True, show_legend=True):

review_full

From pattern with corrections (flow_from_pattern=True, show_measurements=True, show_legend=True):

review_from_pattern

With show_measurement_order=False:

review_no_order

@emlynsg
Copy link
Contributor

emlynsg commented Feb 23, 2026

Hi @CodeMaverick2 , some more comments for you:

  • It is essential the whole plot is shown, no features cut off by the border
  • Layer label under the arrow would mean you can remove the whitespace on the left
  • Measurement labels are somewhat better, but sometimes overlap with nodes and sometimes difficult to read if overlapping with arrows. Play around with this some more to find a better solution.
  • Arrows still look a bit sloppy compared to the original plots. Want the ends of arrows to more-or-less just touch the node they are pointing to, and be clearly separated from each other as much as possible. Still not convinced this part is an improvement yet.
  • Input label in the legend just refers to the square shape/border (since the fill can be black, blue or white). Change to reflect this.
  • Turning off all the optional features in the plot shouldn't leave lots of empty space at the bottom of the plot (as in your example)

At a higher level, it is important you are continuing to compare back to the existing plot design in Graphix and convincing yourself that your changes have improved the readability of the plots.

Since we hope to start moving towards a PR we can accept this week, I would like you to choose a small and a larger graph (i.e. one with say <8 nodes, and another with ~20 nodes), and compare side-by-side with the existing Graphix plots in your PR updates. This will give you a chance to see where you might make some further improvements (for which you can update the PR), and will give us (the maintainer team) what we need to review.

@CodeMaverick2
Copy link
Contributor Author

CodeMaverick2 commented Feb 23, 2026

@emlynsg Thanks for the detailed feedback. Here are the changes addressing each point:

  1. Plot fully visible - Increased top margin when measurement labels are shown so labels above the topmost node are never cut off by the border.
  2. "Layer" moved under the arrow - Moved the "Layer" text from the left side to centered below the arrow. This removes the unnecessary whitespace on the left.
  3. Measurement label overlap solved - Replaced the fixed above-node placement with adaptive positioning. For each measured node, the algorithm checks three candidate directions (above, upper-right, upper-left) and picks whichever is farthest from any neighbouring node. In dense vertical stacks, labels now stagger between upper-right and upper-left instead of all piling up directly above. Also added a white stroke outline (matplotlib.patheffects) so labels remain readable
    over arrows.
  4. Self-loop arrows Reverted to the original Bezier control points to match the existing plot style. Arrow routing is otherwise unchanged from the current codebase.
  5. Input legend entry Changed to "Input (shape)" with an empty white square, since the square border indicates input regardless of fill color (which can be black, blue, or white).
  6. No empty space when features off - Bottom margin now adapts based on whether the measurement order is actually shown. Setting show_measurement_order=False produces a tight layout with no wasted space.

Side-by-side comparison with existing Graphix plots:

Small graph (~7 nodes):

Existing:

small_original

Updated:

small_current

Large graph (~20 nodes):

Existing:

large_original

Updated:

large_current

With show_measurement_order=False:

Small:

small_no_order

Large:

large_no_order

@emlynsg
Copy link
Contributor

emlynsg commented Feb 24, 2026

Hi @CodeMaverick2, thanks for the continued engagement.
The layer label and measurement angle labels are a good improvement on the last version. Measurement angle label placement and arrowhead placement still aren't fully fixed. It is also clearer now that the original graph plot design is more readable in some ways (e.g. the layers are clearer due to the dashed lines, although these had their own downsides).
Overall, we still don't think this is a complete improvement compared to the original design. It is hard to articulate exactly why this is (but factors include a combination of readability, how precise the plot lines are, and how "academic" the plot design is overall) - the only way I know to be confident of this is to see two versions side-by-side. What we need from you for the remainder of the week is to explore options for how to create a visual that is definitively better than our original (i.e. each aspect e.g. readability, professional style etc., is either the same as, or better than, the original). Use your judgement to figure out good and bad tweaks and changes, and let us know again when you have another final product for review.
Best of luck!

@CodeMaverick2
Copy link
Contributor Author

CodeMaverick2 commented Feb 26, 2026

@emlynsg Thanks for the feedback. This time I explored multiple visual variants systematically testing different combinations of label positions, layer indicators, and edge styles side-by-side against the original to find the best approach.

What changed in this update:

  1. Dotted vertical layer separators - Instead of removing separators entirely (which lost layer clarity) or keeping dashed ones (which conflicted with dashed edges), I switched to dotted lines -clearly distinct from dashed edges while restoring the layer structure visibility you mentioned was better in the original.

  2. Measurement labels moved to lower-right - Moved from above-node placement (which cluttered the graph) to lower-right, matching the original's label position. Adaptive placement tries lower-right first, falls back to lower-left or above only when a neighbor would overlap.

  3. Restored edge visibility - Edges changed back from gray to black dashed, matching the original's visibility.

  4. Subtler label styling - Removed bold, thinner white stroke, keeping labels unobtrusive like the original.

  5. Smarter angle display - Zero-angle measurements now show just the plane name (e.g. XY) matching the original's conciseness. Only non-trivial angles show the full format (e.g. XY(-π/3)). Non-rational angles display compactly (e.g. XY(1.72π)).

Side-by-side comparison:

Small graph - Original:
orig_small

Small graph - Updated:

final_small

Large graph - Original:

orig_large

Large graph -Updated:

final_large

On arrowhead placement: The arrow routing code is identical to master arrows behave exactly the same as the original. Modifying the pathfinding algorithm
would be a separate concern outside the scope of issue #387.

@CodeMaverick2
Copy link
Contributor Author

@emlynsg I think the plots are now much closer to publication quality rather than debugging-style visuals 😄 Would love your thoughts!

@emlynsg
Copy link
Contributor

emlynsg commented Feb 26, 2026

Hi @CodeMaverick2 , looking better, well done! I think we're nearly there.
Feedback:

  • Show the measurement angle for all the planes, we think it's better to be explicit.
  • Try and adjust the anchoring a bit, it is a good distance when anchored at the top, and too far away when anchored left/right.
  • Reduce the horizontal width of the layers a little bit (don't want it to be quite as tight as the original where the labels overlap with the layer lines, but still less than it currently is). There might be a smart way you can take the node and measurement label as one object, and then centre that in the layer (but this also might make things less professional looking, so see if you can try but revert if it looks bad).
  • See if you can print the fractional labels as a vertical fraction $\frac{a}{b}$ rather than $a/b$ to save some horizontal space too.
  • Node numbers are still not quite centred inside their circle/square border.
  • The arrowhead placement might then be to do with node sizing, because it is landing a little differently to the past design. See if you can diagnose this.

FYI Unitary are happy for us to finalise this by early next week so we have a few days to pull it into a final product.

@CodeMaverick2
Copy link
Contributor Author

CodeMaverick2 commented Mar 1, 2026

@emlynsg Thanks for the feedback! Here are the changes addressing each point:

1. Explicit measurement angles - Removed the zero-angle shorthand. All measurements now show their angle explicitly (e.g. XY(0) instead of just XY).

2. Label anchoring adjusted - Reduced the left/right label offset from 0.22 to 0.15 data units so labels sit closer to their nodes when placed to the side. The above-node offset (0.22) is unchanged since it was already a good distance.

3. Narrower layers - Reduced the per-layer figure width multiplier from 0.8 to 0.7, making layers more compact without being as tight as the original where labels overlapped layer lines.

4. Vertical fractions - Measurement angles now render as proper stacked fractions using matplotlib's mathtext (e.g. π over 3 instead of π/3), saving horizontal space.

5. Node number centering - Switched from va="center" to va="center_baseline" with explicit fontfamily="sans-serif" for more accurate visual centering of numbers inside their node shapes.

6. Arrowhead placement diagnosed - The root cause was a node_size mismatch: nx.draw_networkx_edges defaults to node_size=300 for calculating arrowhead endpoints, but our nodes are rendered at s=350. Added node_size=350 to the draw call so arrows now land at the correct node boundary.

Side-by-side comparison :

Small graph - Master:

master_small

Small graph - Updated:

pr_small

Large graph - Master:

master_large

Large graph - Updated:

pr_large

@CodeMaverick2
Copy link
Contributor Author

CodeMaverick2 commented Mar 1, 2026

@emlynsg Small follow-up - noticed nodes 5-6 had overlapping measurement labels in the small graph from my previous comment. Fixed this by making the adaptive label placement also track already-placed labels as obstacles, so adjacent nodes no longer merge their labels together

Updated image:

Small graph - Updated (label overlap fixed):

tw_v1_small

Large graph - Updated:
final_large

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.

3 participants