From a0cdd8e1b13b85296967296bfd7a5bc59054f778 Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Fri, 29 May 2026 16:42:36 +0100 Subject: [PATCH 1/9] Add argument to set alpha_min in drawings --- qse/qbits.py | 7 ++++++- qse/vis/qbits.py | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/qse/qbits.py b/qse/qbits.py index fcccb45..a32be56 100644 --- a/qse/qbits.py +++ b/qse/qbits.py @@ -540,6 +540,7 @@ def draw( colouring=None, units=None, equal_aspect=True, + alpha_min=0.0 ): """ Visualize the positions of a set of qubits. @@ -563,7 +564,10 @@ def draw( equal_aspect : bool, optional Whether to have the same scaling for the axes. Defaults to True. - + alpha_min : float, optional + Minimum alpha for bond opacity. Bond alphas are linearly rescaled + from (alpha_min, 1), where 1 is the shortest bond and alpha_min + is the longest. Defaults to 0.0. See Also -------- qse.draw @@ -575,6 +579,7 @@ def draw( colouring=colouring, units=units, equal_aspect=equal_aspect, + alpha_min=alpha_min ) def repeat(self, rep): diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index 6736025..30a0fc6 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -8,7 +8,8 @@ def draw_qbits( - qbits, radius=None, show_labels=False, colouring=None, units=None, equal_aspect=True + qbits, radius=None, show_labels=False, colouring=None, units=None, equal_aspect=True, + alpha_min=0.0 ): """ Visualize the positions of a set of qubits. @@ -34,6 +35,10 @@ def draw_qbits( equal_aspect : bool, optional Whether to have the same scaling for the axes. Defaults to True. + alpha_min : float, optional + Minimum alpha for bond opacity. Bond alphas are linearly rescaled + from (alpha_min, 1), where 1 is the shortest bond and alpha_min + is the longest. Defaults to 0.0. """ if colouring is not None: if len(colouring) != qbits.nqbits: @@ -59,7 +64,7 @@ def draw_qbits( ax.set_aspect("equal") if qbits.dim == 3: - _draw_3d(qbits, draw_bonds, radius, rij, min_dist, ax) + _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, ax) else: _draw_2d( qbits, @@ -67,6 +72,7 @@ def draw_qbits( radius, rij, min_dist, + alpha_min, units, colouring, show_labels, @@ -75,7 +81,7 @@ def draw_qbits( return fig -def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, ax): +def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, ax): positions = qbits.positions if draw_bonds: @@ -85,7 +91,7 @@ def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, ax): ii, jj = np.where(neighbours) X, Y, Z = positions[ii].T U, V, W = (positions[jj] - positions[ii]).T - alpha = (min_dist / rij[neighbours]) ** 3 + alpha = alpha_min + (1 - alpha_min) * (min_dist / rij[neighbours]) ** 3 ax.quiver( X, @@ -111,6 +117,7 @@ def _draw_2d( radius, rij, min_dist, + alpha_min, units, colouring, show_labels, @@ -136,7 +143,7 @@ def _draw_2d( ] ) for i, j in neighbours: - alpha = (min_dist / rij[i, j]) ** 3 + alpha = alpha_min + (1 - alpha_min) * (min_dist / rij[i, j]) ** 3 ax.plot([x[i], x[j]], [y[i], y[j]], c="gray", alpha=alpha, zorder=-1) if colouring is not None: From 50491c1c92efec0f46499c62ce01a1e93874b740 Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Fri, 29 May 2026 17:19:10 +0100 Subject: [PATCH 2/9] Ruff format --- qse/qbits.py | 4 ++-- qse/vis/qbits.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qse/qbits.py b/qse/qbits.py index a32be56..9bf07c3 100644 --- a/qse/qbits.py +++ b/qse/qbits.py @@ -540,7 +540,7 @@ def draw( colouring=None, units=None, equal_aspect=True, - alpha_min=0.0 + alpha_min=0.0, ): """ Visualize the positions of a set of qubits. @@ -579,7 +579,7 @@ def draw( colouring=colouring, units=units, equal_aspect=equal_aspect, - alpha_min=alpha_min + alpha_min=alpha_min, ) def repeat(self, rep): diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index 30a0fc6..a4fba94 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -8,8 +8,13 @@ def draw_qbits( - qbits, radius=None, show_labels=False, colouring=None, units=None, equal_aspect=True, - alpha_min=0.0 + qbits, + radius=None, + show_labels=False, + colouring=None, + units=None, + equal_aspect=True, + alpha_min=0.0, ): """ Visualize the positions of a set of qubits. From c5109d5edfc4c6a7de3574aee91045485cd657a7 Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 06:44:50 +0100 Subject: [PATCH 3/9] Add colouring option for 3d plots --- qse/vis/qbits.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index a4fba94..59b02ef 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -69,7 +69,7 @@ def draw_qbits( ax.set_aspect("equal") if qbits.dim == 3: - _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, ax) + _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, colouring, ax) else: _draw_2d( qbits, @@ -86,7 +86,7 @@ def draw_qbits( return fig -def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, ax): +def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, colouring, ax): positions = qbits.positions if draw_bonds: @@ -112,8 +112,31 @@ def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, ax): ) x, y, z = positions.T - for r, c in zip(rads, colors): - ax.scatter(x, y, z, s=r**2, color=(0.1, c, 0.5), zorder=1, alpha=0.8) + if colouring is not None: + inds0 = [j == 0 for j in colouring] + inds1 = [j == 1 for j in colouring] + for r, c in zip(rads, colors): + ax.scatter( + x[inds0], + y[inds0], + z[inds0], + s=r**2, + color=(0.1, c, 0.5), + zorder=1, + alpha=0.8, + ) + ax.scatter( + x[inds1], + y[inds1], + z[inds1], + s=r**2, + color=(c, 0.1, 0.5), + zorder=1, + alpha=0.8, + ) + else: + for r, c in zip(rads, colors): + ax.scatter(x, y, z, s=r**2, color=(0.1, c, 0.5), zorder=1, alpha=0.8) def _draw_2d( From 2237f2ea72d935186de442694de46b06128d7f4a Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 07:32:53 +0100 Subject: [PATCH 4/9] Add labels to 3d plots --- qse/vis/qbits.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index 59b02ef..87b8217 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -62,14 +62,15 @@ def draw_qbits( elif min_dist > radius: draw_bonds = False - fig = plt.figure() + figsize = (10, 8) if qbits.dim == 3 else (6.4, 4.8) + fig = plt.figure(figsize=figsize) projection = "3d" if qbits.dim == 3 else None ax = fig.add_subplot(projection=projection) if equal_aspect: ax.set_aspect("equal") if qbits.dim == 3: - _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, colouring, ax) + _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, units, colouring, show_labels, ax) else: _draw_2d( qbits, @@ -86,9 +87,14 @@ def draw_qbits( return fig -def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, colouring, ax): +def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, units, colouring, show_labels, ax): positions = qbits.positions + ax.set_xlabel("x" + f" ({units})" if units is not None else "x") + ax.set_ylabel("y" + f" ({units})" if units is not None else "y") + ax.set_zlabel("z" + f" ({units})" if units is not None else "z") + ax.figure.subplots_adjust(right=0.85) + if draw_bonds: f_tol = 1.01 # fractional tolerance neighbours = rij <= radius * f_tol @@ -138,6 +144,10 @@ def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, colouring, ax) for r, c in zip(rads, colors): ax.scatter(x, y, z, s=r**2, color=(0.1, c, 0.5), zorder=1, alpha=0.8) + if show_labels: + for ind in range(qbits.nqbits): + ax.text(x[ind], y[ind], z[ind], s=qbits.labels[ind]) + def _draw_2d( qbits, From 301f460536139e59b935acdea03fe5ebf303f436 Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 07:33:41 +0100 Subject: [PATCH 5/9] Ruff formatting --- qse/vis/qbits.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index 87b8217..6a7b078 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -70,7 +70,18 @@ def draw_qbits( ax.set_aspect("equal") if qbits.dim == 3: - _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, units, colouring, show_labels, ax) + _draw_3d( + qbits, + draw_bonds, + radius, + rij, + min_dist, + alpha_min, + units, + colouring, + show_labels, + ax, + ) else: _draw_2d( qbits, @@ -87,7 +98,18 @@ def draw_qbits( return fig -def _draw_3d(qbits, draw_bonds, radius, rij, min_dist, alpha_min, units, colouring, show_labels, ax): +def _draw_3d( + qbits, + draw_bonds, + radius, + rij, + min_dist, + alpha_min, + units, + colouring, + show_labels, + ax, +): positions = qbits.positions ax.set_xlabel("x" + f" ({units})" if units is not None else "x") From 78317f5621fafafd9255bd8e73046f8700951daa Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 15:23:49 +0100 Subject: [PATCH 6/9] Add interactive plots to the notebooks --- .../creating_and_manipulating_qbits.ipynb | 14 +- qse/qbits.py | 35 +++-- qse/vis/__init__.py | 3 +- qse/vis/qbits.py | 147 ++++++++++++++++++ 4 files changed, 187 insertions(+), 12 deletions(-) diff --git a/docs/tutorials/creating_and_manipulating_qbits.ipynb b/docs/tutorials/creating_and_manipulating_qbits.ipynb index 58765cd..7a8d3dd 100644 --- a/docs/tutorials/creating_and_manipulating_qbits.ipynb +++ b/docs/tutorials/creating_and_manipulating_qbits.ipynb @@ -113,6 +113,18 @@ "qbits.draw(radius=\"nearest\")" ] }, + { + "cell_type": "markdown", + "source": "The `draw` method also supports an `interactive` mode for 3D lattices. Passing `interactive=True` renders a rotatable, zoomable Plotly figure that stays interactive in the documentation website.", + "metadata": {} + }, + { + "cell_type": "code", + "source": "qbits = qse.Qbits(cell=np.eye(3), positions=np.zeros((1, 3)))\nqbits = qbits.repeat((4, 4, 4))\nqbits.draw(radius=\"nearest\", interactive=True)", + "metadata": {}, + "execution_count": null, + "outputs": [] + }, { "cell_type": "markdown", "metadata": {}, @@ -275,4 +287,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/qse/qbits.py b/qse/qbits.py index 9bf07c3..094036c 100644 --- a/qse/qbits.py +++ b/qse/qbits.py @@ -11,7 +11,7 @@ from qse.cell import Cell from qse.operator import Operator, Operators from qse.qbit import Qbit -from qse.vis import draw_qbits +from qse.vis import draw_3d_qbits_interactive, draw_qbits class Qbits: @@ -541,6 +541,7 @@ def draw( units=None, equal_aspect=True, alpha_min=0.0, + interactive=False, ): """ Visualize the positions of a set of qubits. @@ -568,19 +569,33 @@ def draw( Minimum alpha for bond opacity. Bond alphas are linearly rescaled from (alpha_min, 1), where 1 is the shortest bond and alpha_min is the longest. Defaults to 0.0. + interactive : bool, optional + If True, render an interactive 3D Plotly figure (3D lattices only). + Requires ``plotly`` to be installed. Defaults to False. + See Also -------- qse.draw """ - draw_qbits( - self, - radius=radius, - show_labels=show_labels, - colouring=colouring, - units=units, - equal_aspect=equal_aspect, - alpha_min=alpha_min, - ) + if interactive: + draw_3d_qbits_interactive( + self, + radius=radius, + show_labels=show_labels, + colouring=colouring, + units=units, + alpha_min=alpha_min, + ) + else: + return draw_qbits( + self, + radius=radius, + show_labels=show_labels, + colouring=colouring, + units=units, + equal_aspect=equal_aspect, + alpha_min=alpha_min, + ) def repeat(self, rep): """Create new repeated qbits object. diff --git a/qse/vis/__init__.py b/qse/vis/__init__.py index e4352b2..75abdea 100644 --- a/qse/vis/__init__.py +++ b/qse/vis/__init__.py @@ -15,12 +15,13 @@ "bar", "draw_amp_and_det", "draw_qbits", + "draw_3d_qbits_interactive", "draw_signal", "view_matrix", "qse_green", "qse_red", ] from qse.vis.colours import qse_green, qse_red -from qse.vis.qbits import draw_qbits +from qse.vis.qbits import draw_3d_qbits_interactive, draw_qbits from qse.vis.signal import draw_amp_and_det, draw_signal from qse.vis.visualise import bar, view_matrix diff --git a/qse/vis/qbits.py b/qse/vis/qbits.py index 6a7b078..d4466e9 100644 --- a/qse/vis/qbits.py +++ b/qse/vis/qbits.py @@ -225,3 +225,150 @@ def _draw_2d( if show_labels: for ind in range(qbits.nqbits): ax.text(x[ind], y[ind], s=qbits.labels[ind]) + + +def draw_3d_qbits_interactive( + qbits, radius=None, show_labels=False, colouring=None, units=None, alpha_min=0.0 +): + """ + Visualize the positions of a set of qubits as an interactive 3D Plotly figure. + + This function is intended for 3D lattices only. For 2D or 1D qubits use + :func:`draw_qbits` instead. + + Produces a rotatable, zoomable plot that renders as interactive HTML in + Jupyter Book without requiring a live kernel. + + Parameters + ---------- + qbits : qse.Qbits + The Qbits object. Must have ``qbits.dim == 3``. + radius : float | str, optional + A cutoff radius for visualizing bonds. + Pass 'nearest' to use the smallest distance between qubits. + If None, bonds are not drawn. + show_labels : bool, optional + Whether to show qubit labels. Defaults to False. + colouring : list, optional + A list of 0s and 1s assigning each qubit to a sublattice. + 0 → green, 1 → red. Must have the same length as the number of qubits. + units : str, optional + The units of distance, shown on the axis labels. + alpha_min : float, optional + Minimum opacity for bonds. Defaults to 0.0. + """ + import plotly.graph_objects as go + + if colouring is not None: + if len(colouring) != qbits.nqbits: + raise Exception("The length of colouring must equal the number of Qubits.") + colouring = [int(i) for i in colouring] + + positions = qbits.positions + x, y, z = positions.T + + def axis_label(name): + return name + f" ({units})" if units is not None else name + + fig = go.Figure( + layout=go.Layout( + width=800, + height=700, + scene=dict( + xaxis_title=axis_label("x"), + yaxis_title=axis_label("y"), + zaxis_title=axis_label("z"), + ), + ) + ) + + # --- bonds --- + draw_bonds = radius is not None + rij = None + if draw_bonds: + rij = qbits.get_all_distances() + min_dist = rij[np.logical_not(np.eye(qbits.nqbits, dtype=bool))].min() + if radius == "nearest": + radius = min_dist + elif min_dist > radius: + draw_bonds = False + + if draw_bonds: + f_tol = 1.01 + x_lines, y_lines, z_lines = [], [], [] + for i in range(qbits.nqbits - 1): + for j in range(i + 1, qbits.nqbits): + if rij[i, j] <= radius * f_tol: + x_lines += [positions[i, 0], positions[j, 0], None] + y_lines += [positions[i, 1], positions[j, 1], None] + z_lines += [positions[i, 2], positions[j, 2], None] + + fig.add_trace( + go.Scatter3d( + x=x_lines, + y=y_lines, + z=z_lines, + mode="lines", + line=dict(color="gray", width=2), + opacity=alpha_min + (1 - alpha_min) * 0.5, + showlegend=False, + hoverinfo="skip", + ) + ) + + # --- qubits --- + mode = "markers+text" if show_labels else "markers" + labels = list(qbits.labels) if show_labels else None + + def _rgb(r, g, b): + return "rgb({},{},{})".format(int(r * 255), int(g * 255), int(b * 255)) + + if colouring is not None: + inds0 = np.array([c == 0 for c in colouring]) + inds1 = np.array([c == 1 for c in colouring]) + for rad, c in zip(rads, colors): + fig.add_trace( + go.Scatter3d( + x=x[inds0], + y=y[inds0], + z=z[inds0], + mode=mode, + marker=dict(size=rad / 2, color=_rgb(0.1, c, 0.5), opacity=0.8), + text=np.array(labels)[inds0] if labels else None, + showlegend=False, + hoverinfo="skip", + ) + ) + fig.add_trace( + go.Scatter3d( + x=x[inds1], + y=y[inds1], + z=z[inds1], + mode=mode, + marker=dict(size=rad / 2, color=_rgb(c, 0.1, 0.5), opacity=0.8), + text=np.array(labels)[inds1] if labels else None, + showlegend=False, + hoverinfo="skip", + ) + ) + else: + for rad, c in zip(rads, colors): + fig.add_trace( + go.Scatter3d( + x=x, + y=y, + z=z, + mode=mode, + marker=dict(size=rad / 2, color=_rgb(0.1, c, 0.5), opacity=0.8), + text=labels, + showlegend=False, + hoverinfo="skip", + ) + ) + + try: + from IPython.display import HTML, display + + display(HTML(fig.to_html(full_html=False, include_plotlyjs="require"))) + except ImportError: + fig.show() From 317c23213b6614e80f194b36638605ef4d27490f Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 15:24:48 +0100 Subject: [PATCH 7/9] Update pyproject.toml file --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c9b16d5..491faaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,8 @@ docs = [ "ipywidgets", "ipympl", "pulser", # required for creating the docs - "qiskit" + "qiskit", + "plotly", ] [dependency-groups] From 215ccd093dd1c74476832d95c98db8729c25fce0 Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 15:24:59 +0100 Subject: [PATCH 8/9] Update uv.lock file --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 8d23dc2..4fe71df 100644 --- a/uv.lock +++ b/uv.lock @@ -4005,7 +4005,7 @@ wheels = [ [[package]] name = "qse" -version = "1.1.14" +version = "1.1.15" source = { editable = "." } dependencies = [ { name = "matplotlib" }, From 53337f88317ff973ea9e525579ea1ec5501dbc7a Mon Sep 17 00:00:00 2001 From: pablosuvi Date: Tue, 2 Jun 2026 15:27:22 +0100 Subject: [PATCH 9/9] Update uv.lock --- uv.lock | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/uv.lock b/uv.lock index 4fe71df..bd343b5 100644 --- a/uv.lock +++ b/uv.lock @@ -2237,6 +2237,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163, upload-time = "2024-04-28T20:22:39.985Z" }, ] +[[package]] +name = "narwhals" +version = "2.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/1c/c80cb7719721a44846c6301ef118434bae30a423924bfad3a47f16bdc064/narwhals-2.22.0.tar.gz", hash = "sha256:6486282bb7e4b4ab55963efbd8be1451b764cc4874b74d1fd625eba9dc60b86f", size = 417565, upload-time = "2026-06-01T13:34:36.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b6/e7cdde7b8e90d5dff25b622f95833ef26567ad184c977278b93a1cbd5717/narwhals-2.22.0-py3-none-any.whl", hash = "sha256:1421797ede01789cc1537619dbc3f36f840737240f748fdb24a60a0225fc80be", size = 453815, upload-time = "2026-06-01T13:34:34.127Z" }, +] + [[package]] name = "nbclient" version = "0.10.4" @@ -2698,6 +2707,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, ] +[[package]] +name = "plotly" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/7f/0f100df1172aadf88a929a9dbb902656b0880ba4b960fe5224867159d8f4/plotly-6.7.0.tar.gz", hash = "sha256:45eea0ff27e2a23ccd62776f77eb43aa1ca03df4192b76036e380bb479b892c6", size = 6911286, upload-time = "2026-04-09T20:36:45.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -4031,6 +4053,7 @@ docs = [ { name = "jupyter-book" }, { name = "jupyter-sphinx" }, { name = "myst-nb" }, + { name = "plotly" }, { name = "pulser" }, { name = "qiskit" }, { name = "sphinx-autoapi" }, @@ -4078,6 +4101,7 @@ requires-dist = [ { name = "myqlm", marker = "extra == 'myqlm'" }, { name = "myst-nb", marker = "extra == 'docs'", specifier = ">=1.1.0" }, { name = "numpy" }, + { name = "plotly", marker = "extra == 'docs'" }, { name = "pre-commit", marker = "extra == 'dev'" }, { name = "pulser", marker = "extra == 'dev'" }, { name = "pulser", marker = "extra == 'docs'" },