Skip to content

Add orbital entanglement diagram widget#2974

Open
nabbelbabbel wants to merge 35 commits intomainfrom
jpu/orbital-entaglement-widget
Open

Add orbital entanglement diagram widget#2974
nabbelbabbel wants to merge 35 commits intomainfrom
jpu/orbital-entaglement-widget

Conversation

@nabbelbabbel
Copy link
Copy Markdown

@nabbelbabbel nabbelbabbel commented Feb 26, 2026

Note: This PR is a lot of AI generated infra to achieve an outcome for testing purposes. While the features work, the way they are done needs some serious work and rethinking.

Adds:

  • orbital entanglement diagram widget
  • option to generate static SVG from orbital entanglement diagram widget for publications/presentations with python code
  • option to generate static SVG from histogram
  • option to generate static SVG from circuit

Widget with active space selection (gold):
image

Widget while hovering individual orbital/entangled node:
image

// ---- Circuit (imperative DOM via qviz) ----
case "Circuit": {
// qviz.draw() needs a real DOM. Use jsdom.
const { JSDOM } = await import("jsdom");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This feature assume a) Node is on the users PATH (and at least it gives an error message if not), and b) That jsdom is available as a package to import in node (which it isn't by default, the user would need to install it into the global (or local) node environment. This seems fragile and likely to fail unless users have followed some specific setup instructions first. (We also don't want to bundle JSDOM into our package as it's huge).

Not sure what to do about this, but worth thinking about.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agree, an initial version had JSDOM as dependency but I removed it for the reason you stated. I dont think I can get rid of the Node requirement if I want to allow rendering without actually displaying the widget. But I can shim/mock the few parts of JSDOM we need.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I added the shim, but its 500 lines of extra stuff, thoughts?

}

// ---- Circuit (imperative DOM via qviz) ----
case "Circuit": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did you try this at all? Thinking through how this works, you wouldn't get what's displayed on screen (unless you've not interacted with it). You'll just get the default new rendering with a circuit, which now has all blocks/calls/operations collapsed. If the user has drilled into operations to expand them and show the gates etc., that's not going to show up here, as it's re-rendering the circuit from a clean state, not the current UX state.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes that was the intended behavior (for now).

The point of these functions was not so save the user-altered state of the widget into an SVG. (Though that would be even better!)

The idea was to be able to make the widget class, call the SVG dump and never need to look at it to extract an SVG to later view/use.

Will see if I can get it to work using the actual state of the widget.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I have now added functionality to use the actual state of the widgets but still allowing a different path to render without the widget being displayed to the user.

/**
* Detect whether the host background is dark or light by sampling the
* computed background-color of the nearest ancestor with one.
* Returns a high-contrast colour for selection outlines.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why not use CSS values to match the current theme like the other widgets?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The current version should fix this but have generic fall backs in case there is no theme.

clone.querySelectorAll(stripSelector).forEach((n) => n.remove());
}
// Remove interactive-only elements common across widgets
clone
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is an interesting approach (cloning and serializing the SVG), but this implementation seems hard to maintain and fragile. Below you are baking knowledge of the internals of various widgets into this one central location outside of the widgets. This would mean if we ever touch those widgets (e.g. rename the .menu-icon class or #menu id or some of the other classes or styles hard-coded below, we'd need to remember to come here and change it also. That would be easy to miss and introduce bugs we wouldn't find for a while (being automated testing for this is hard)

I'm also not a big fan of automatically watching for changes and cloning the svg on mutation, being the SVG can be really huge (esp. for circuits, and likely for chord diagrams), and the vast majority of time people aren't going to use the 'save the svg feature'.

Is there a way to push the 'cleaning' of the SVG/DOM to the owning widget (so it's easy to see when changing the widget itself), and only clone & serialize the SVG when the user actually wants to save the SVG?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fully agree, this is all an artifact of the idea that I outlined below with your question about the second svg export route.

import circuitCss from "../../npm/qsharp/ux/qsharp-circuit.css";

const input = readFileSync(0, "utf-8"); // stdin
const { component, props } = JSON.parse(input);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you're cloning and serializing the SVG above now, why do you need to shell out to node and render the component via the prop at all? What's the use-case for this file now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The use case I have in mind is not having a browser/notebook/vscode instance at all.

Essentially thinking about someone running qdk-chemistry or any other component on a remote HPC system and wanting to generate a picture to quickly see if a calculation did what it was supposed to.

If I understand correctly, anywidget uses the DOM of whatever the widget is shown in, meaning that if there is not VSCode/jupyterlab/etc, there also is no DOM to render the SVG with.

@nabbelbabbel nabbelbabbel marked this pull request as draft February 27, 2026 17:46
@nabbelbabbel nabbelbabbel marked this pull request as ready for review April 7, 2026 20:38
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