Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions docs/generate_animations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Regenerate the classification animations embedded in the documentation.

Run from the repository root::

python docs/generate_animations.py

For each example this writes three assets into ``docs/source/media``:

- a ``.gif`` for a quick non-interactive preview,
- a ``.html`` interactive player (matplotlib ``to_jshtml``) with play / pause / step / loop
controls, which is what the docs embed,

and once, a ``classification_legend.png`` colour legend shared by both examples.
"""
import os

from paulie import get_pauli_string as p
from paulie import animation_anti_commutation_graph
from paulie.helpers.drawing import save_role_legend

MEDIA_DIR = os.path.join(os.path.dirname(__file__), "source", "media")

EXAMPLES = {
# A-type canonical graph -> 4*so(5)
"classification_a_type": {
"generators": ["IYZI", "IIXX", "IIYZ", "IXXI", "XXII", "YZII"],
"n": None,
},
# B-type canonical graph (a_9) -> sp(4)
"classification_b_type": {
"generators": ["XY", "XZ"],
"n": 4,
},
}


def main() -> None:
"""Generate the legend and every documentation animation."""
legend_path = os.path.join(MEDIA_DIR, "classification_legend.png")
save_role_legend(legend_path)
print(f"wrote {legend_path}")

for name, spec in EXAMPLES.items():
generators = (p(spec["generators"], n=spec["n"]) if spec["n"] is not None
else p(spec["generators"]))
print(f"Rendering {name} (algebra = {generators.get_algebra()}) ...")

# Build the animation once, then export it as both a gif and an interactive player.
ani = animation_anti_commutation_graph(generators, interval=1200, show=False)

gif_path = os.path.join(MEDIA_DIR, f"{name}.gif")
ani.save(filename=gif_path, writer="pillow")
print(f" wrote {gif_path}")

html_path = os.path.join(MEDIA_DIR, f"{name}.html")
with open(html_path, "w", encoding="utf-8") as handle:
handle.write(ani.to_jshtml(default_mode="once"))
print(f" wrote {html_path}")


if __name__ == "__main__":
main()
Binary file added docs/source/media/classification_a_type.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2,640 changes: 2,640 additions & 0 deletions docs/source/media/classification_a_type.html

Large diffs are not rendered by default.

Binary file added docs/source/media/classification_b_type.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3,507 changes: 3,507 additions & 0 deletions docs/source/media/classification_b_type.html

Large diffs are not rendered by default.

Binary file added docs/source/media/classification_legend.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 39 additions & 1 deletion docs/source/user/classification.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,32 @@ For any generator set consisting of Pauli strings, the anticommutation graph can
+----------------+------------------------------------------+-------------------------------------------------------------+


Visualizing the transformation
------------------------------
The step-by-step transformation of the anticommutation graph into its canonical form can be
animated with :code:`animation_anti_commutation_graph`. It drives the classification algorithm
through a recording wrapper that observes every step (without changing the result) and renders the
captured frames into an animation:

.. code-block:: python

from paulie import get_pauli_string as p, animation_anti_commutation_graph

generators = p(["XY", "XZ"], n=4)
animation_anti_commutation_graph(
generators,
storage={"filename": "classification.gif", "writer": "pillow"},
)

Each frame highlights the role a vertex currently plays, following this colour legend:

.. image:: ../media/classification_legend.png
:alt: Colour legend for the node roles tracked while building the canonical graph
:align: center

The two worked examples below are illustrated with interactive players. Use the controls to play,
pause, or step through the construction frame by frame.

Classification of A-type canonical graph
----------------------------------------
Let's try to classify a generator set that corresponds to an A-type canonical graph. This algebra is generated by :math:`\mathcal{P}=\{IYZI,IIXX,IIYZ,IXXI,XXII,YZII\}`.
Expand All @@ -92,7 +118,13 @@ outputs

algebra = 4*so(5)

.. Here we should add some definitions like lit, dependent etc.
The player below follows the construction of one of the four canonical components. Starting from
the input anticommutation graph, each generator is added in turn (red), attached to the centre or to
a leg (green), and length-one legs in different lit states are merged using the ``p`` (blue) and
``q`` (pink) vertices, until the A-type star graph is obtained:

.. raw:: html
:file: ../media/classification_a_type.html

According to the table, the resultant graph corresponds to :math:`\mathfrak{so}(5)\oplus \mathfrak{so}(5)\oplus \mathfrak{so}(5)\oplus \mathfrak{so}(5)`. But it is worth noting that it also corresponds to :math:`\mathfrak{sp}(2)\oplus \mathfrak{sp}(2)\oplus \mathfrak{sp}(2)\oplus \mathfrak{sp}(2)`. This shows that there is an exceptional isomorphism between :math:`\mathfrak{so}(5)` and :math:`\mathfrak{sp}(2)`.

Expand All @@ -113,6 +145,12 @@ outputs

algebra = sp(4)

Here the contractions reduce the graph to a single long leg attached to the centre, the canonical
form of type B1 corresponding to :math:`\mathfrak{sp}(4)`:

.. raw:: html
:file: ../media/classification_b_type.html

The Lie algebra plays a pivotal role in quantum control theory to understand the reachability of states.
Also measures of operator spread complexity rely on this concept.
Furthermore, determining moments of circuits can be significantly simplified when the Lie algebra is known.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies = [
"bitarray>=3.0.0",
"memory-profiler>=0.61.0",
"matplotlib>=3.10.0",
"pillow>=10.0.0",
"six>=1.17.0",
"tqdm>=4.67.1",
"scipy>=1.17.0",
Expand Down
2 changes: 2 additions & 0 deletions src/paulie/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .application.get_optimal_su2_n import get_optimal_universal_generators
from .application.average_graph_complexity import average_graph_complexity
from .application.plot import plot_anti_commutation_graph
from .application.animation import animation_anti_commutation_graph
from .application.average_pauli_weight import (
quantum_fourier_entropy,
average_pauli_weight,
Expand Down Expand Up @@ -79,6 +80,7 @@
"get_optimal_universal_generators",
"average_graph_complexity",
"plot_anti_commutation_graph",
"animation_anti_commutation_graph",
"quantum_fourier_entropy",
"average_pauli_weight",
"get_pauli_weights",
Expand Down
45 changes: 45 additions & 0 deletions src/paulie/application/animation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Module for animating the transformation of the anti-commutation graph into a canonical form.
"""
from matplotlib.animation import Animation
from paulie.helpers._recording import RecordGraph
from paulie.helpers.drawing import _animation_graph
from paulie.common.pauli_string_collection import PauliStringCollection


def animation_anti_commutation_graph(
generators: PauliStringCollection,
storage: dict[str, str] | None = None,
interval: int = 1000,
show: bool = False,
) -> Animation:
"""
Generates an animation showing the transformation of the anti-commutation
graph into canonical form.

The animation is driven by a
:class:`~paulie.classifier.recording_canonicalizer.RecordingCanonicalizer`, which observes the
classification algorithm and records each step. Use the colour legend in
:data:`paulie.helpers.drawing.NODE_ROLE_COLORS` to interpret node roles.

Args:
generators (PauliStringCollection): Collection of Pauli strings.
storage (dict[str, str], optional): Location and format to save the
animation to. Expected keys are ``"filename"`` and ``"writer"``.
interval (int, optional): Interval between recording frames in
milliseconds.
show (bool, optional): Whether to display the animation window.

Returns:
matplotlib.animation.Animation
"""
record = RecordGraph()
generators.set_record(record)
generators.classify()
generators.set_record(None)
return _animation_graph(
record,
interval=interval,
storage=storage,
show=show,
)
Loading
Loading