Skip to content

Add python widget for Bloch Sphere - #3595

Open
Scott Carda (ScottCarda-MS) wants to merge 6 commits into
mainfrom
sccarda/PythonBloch
Open

Add python widget for Bloch Sphere#3595
Scott Carda (ScottCarda-MS) wants to merge 6 commits into
mainfrom
sccarda/PythonBloch

Conversation

@ScottCarda-MS

@ScottCarda-MS Scott Carda (ScottCarda-MS) commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Adds python support for the Bloch Sphere visualizer by making it a python widget, which is then added under the qsharp_widgets package, and additionally made available under the QDK python package.

Example python usage:

import qdk.widgets as w
bloch = w.BlochSphere('H T H') # string is initial gate sequence, can be empty
bloch

Additional fields added to the python bloch widget:

bloch.initial_gates # the initial gate sequence given when creating the widget
bloch.gates # the live gate sequence

To minimize the impact of KaTeX on the Python package:

  • KaTeX CSS and fonts are isolated in a Bloch-specific stylesheet, so existing widgets continue loading the original lightweight stylesheet.
  • The production build retains only the four .woff2 font faces used by the Bloch sphere’s matrices and math expressions, reducing the Bloch stylesheet from approximately 383 KB to 109 KB raw (264 KB to 60 KB compressed).

@ScottCarda-MS
Scott Carda (ScottCarda-MS) marked this pull request as ready for review August 17, 2026 20:53

@billti Bill Ticehurst (billti) left a comment

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.

To minimize the impact of KaTeX on the Python package:

KaTeX CSS and fonts are isolated in a Bloch-specific stylesheet, so existing widgets continue loading the original lightweight stylesheet.
The production build retains only the four .woff2 font faces used by the Bloch sphere’s matrices and math expressions, reducing the Bloch stylesheet from approximately 383 KB to 109 KB raw (264 KB to 60 KB compressed).

The index.js file for the widget however is 600kb larger after this PR (which is already too large as we still need to move the molecule visualizer out of it). This loads in every cell that just wants to show a histogram or a circuit or whatever.

As discussed, we should figure out how to make this a separate entry point so it only gets loaded when the user wants to see a Bloch sphere (which will prbably be pretty rarely). I can give you some guidance if needed.

Comment thread source/widgets/js/bloch.css
Comment thread source/widgets/js/index.tsx Outdated
};

onChange();
model.on("change:initial_gates", onChange);

Choose a reason for hiding this comment

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

This listener rerenders the wrapper when Python changes initial_gates, but the underlying BlochSphere reads initialGates only inside its mount-only useEffect([]). Since Preact preserves the mounted component when rendering it again, the updated prop is not applied.

I reproduced this with the wheel built from this PR:

from IPython.display import display
from qsharp_widgets import BlochSphere

widget = BlochSphere("X")
display(widget)

The widget's gate-program text field initially shows X. Then run:

widget.initial_gates = "H"

print("initial_gates:", widget.initial_gates)
print("gates:", widget.gates)

Observed:

initial_gates: H
gates: X

The rendered gate-program field also remains X, rather than changing to H.

Could the core component respond to subsequent initialGates prop changes, or should initial_gates not be exposed as a synchronized mutable trait?

BlochSphere remains X after initial_gates changes to H

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I built and tested the PR locally with the repository's Rust 1.95 toolchain. I left two inline comments:

  • The generated widget wheel embeds KaTeX assets but appears not to include KaTeX's MIT license notice.
  • Updating the synchronized initial_gates trait after display does not update the mounted Bloch Sphere; I included a minimal notebook reproducer and screenshot.

The widget otherwise built successfully and worked as expected in JupyterLab.

@ScottCarda-MS

Copy link
Copy Markdown
Contributor Author

The index.js file for the widget however is 600kb larger after this PR (which is already too large as we still need to move the molecule visualizer out of it). This loads in every cell that just wants to show a histogram or a circuit or whatever.

As discussed, we should figure out how to make this a separate entry point so it only gets loaded when the user wants to see a Bloch sphere (which will prbably be pretty rarely). I can give you some guidance if needed.

I've separated the bloch sphere into its own entry point with bloch.tsx, and removed it from index.tsx.

import markdownIt from "markdown-it";
import { render as prender } from "preact";

import { BlochSphere } from "../../npm/qsharp/ux/bloch/bloch.js";

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.

You should never import from the relative path the source like this. Import from qsharp-lang/ux just like it did in index.tsx.

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