Visualization
Visualize quantum circuits, quantum states, and simulation results using pyqpanda3's built-in drawing and plotting tools.
Prerequisites: Circuit Construction -- you should be comfortable building QProg and QCircuit objects before working through this tutorial.
Table of Contents
- Overview
- 1. Circuit Drawing
- 2. Print Options
- 3. The visualization Module
- 4. Practical Examples
- 5. Visualization Workflow
- 6. API Quick Reference
- Summary
Overview
pyqpanda3 provides two layers of visualization tools:
Core module (
pyqpanda3.core) -- Circuit drawing functions (draw_qprog,PIC_TYPE,set_print_options) that produce text or LaTeX representations directly, no extra dependencies required.Visualization module (
pyqpanda3.visualization) -- Rich plotting functions (Bloch sphere, state city plots, density matrices, probability bar charts) that depend on matplotlib for rendering graphical output.
The following diagram shows how these two layers relate to the quantum objects you create:
graph TD
subgraph "Quantum Objects"
QProg["QProg"]
QCircuit["QCircuit"]
SimResult["Simulator Result"]
StateVec["State Vector"]
end
subgraph "Core Drawing"
DrawQprog["core.draw_qprog()"]
PrintStr["print() / __str__"]
SetPrint["core.set_print_options()"]
PIC_TEXT["PIC_TYPE.TEXT"]
PIC_LATEX["PIC_TYPE.LATEX"]
end
subgraph "visualization Module"
PlotBloch["plot_bloch()"]
PlotState["plot_state()"]
PlotProb["plot_probabilities()"]
CircuitSum["circuit_summary()"]
DrawMember[".draw() method"]
end
QProg --> DrawQprog
QProg --> PrintStr
QProg --> DrawMember
QCircuit --> DrawQprog
QCircuit --> PrintStr
QCircuit --> DrawMember
StateVec --> PlotBloch
StateVec --> PlotState
SimResult --> PlotProb
QProg --> CircuitSum
QCircuit --> CircuitSum
DrawQprog --> PIC_TEXT
DrawQprog --> PIC_LATEX
SetPrint -.-> PrintStr
style QProg fill:#4a90d9,color:#fff
style QCircuit fill:#50b86c,color:#fff
style SimResult fill:#e8913a,color:#fff
style StateVec fill:#7b68ee,color:#fff1. Circuit Drawing
Circuit drawing is the most common visualization task. It renders a quantum circuit as a human-readable diagram showing qubit wires, gates, measurements, and their connectivity. pyqpanda3 supports three output formats: text (ASCII art), image (matplotlib), and LaTeX source code.
1.1 Quick Text Output with print()
The fastest way to visualize a circuit is the built-in print() function. Both QProg and QCircuit implement __repr__ so that print() renders a text circuit diagram automatically.
from pyqpanda3 import core
# Build a Bell state circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
# Quick text rendering
print(prog)运行结果:
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 1This produces a text circuit diagram showing the gate sequence on qubit wires:
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 1You can also print a QCircuit object the same way:
circuit = core.QCircuit()
circuit << core.H(0) << core.RY(1, 1.57) << core.CNOT(0, 1)
print(circuit)运行结果:
┌─┐
q_0: |0>─┤H├───────────── ───*──
├─┴────────────┐ ┌──┴─┐
q_1: |0>─┤RY(1.57000000)├ ┤CNOT├
└──────────────┘ └────┘
c : / ════════════════════════The output displays each gate on the appropriate qubit wire, with control and target markers for two-qubit gates.
1.2 Drawing with core.draw_qprog()
The core.draw_qprog() function gives you more control over the output format and parameters. It accepts either a QProg or QCircuit as its first argument and a PIC_TYPE enum to select the output format.
The two available picture types are:
| Constant | Description |
|---|---|
core.PIC_TYPE.TEXT | ASCII text diagram (default for most uses) |
core.PIC_TYPE.LATEX | LaTeX source code for publication-quality rendering |
Text Output
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
# Draw as text with gate parameters shown
result = core.draw_qprog(
prog,
p=core.PIC_TYPE.TEXT,
param_show=True,
line_length=100
)
print(result)运行结果:
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 1The param_show=True flag reveals numerical parameters on rotation gates (for example, showing the angle on an RX gate).
LaTeX Output
# Draw as LaTeX source code
latex_result = core.draw_qprog(
prog,
p=core.PIC_TYPE.LATEX,
with_logo=False
)
print(latex_result)运行结果:
Latex format is temporarily not supportedNote: The core.draw_qprog() function with PIC_TYPE.LATEX currently returns a placeholder message indicating that LaTeX generation via this API path is temporarily not supported. For working LaTeX output, use the .draw('latex') method described in Section 1.5.
Saving to File
Both text and LaTeX output can be saved directly to a file:
# Save text diagram to file
core.draw_qprog(
prog,
p=core.PIC_TYPE.TEXT,
output_file="bell_circuit.txt"
)
# Save LaTeX source to file (via .draw() method)
prog.draw('latex', filename="bell_circuit.tex")The output_file parameter writes the result to the specified path. When the output_file is empty (the default), the function returns the result as a string instead.
1.3 The draw() Member Method
Both QProg and QCircuit have a draw() method injected by the visualization module. This provides a convenient, object-oriented interface for circuit drawing. The method is available as soon as pyqpanda3 is imported.
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
# Text output (default)
text = prog.draw()
print(text)
# Explicit text output
text = prog.draw('text')
print(text)运行结果:
┌─┐
q_0: |0>─┤H├ ───*──
└─┘ ┌──┴─┐
q_1: |0>──── ┤CNOT├
└────┘
c : / ═══════════
┌─┐
q_0: |0>─┤H├ ───*──
└─┘ ┌──┴─┐
q_1: |0>──── ┤CNOT├
└────┘
c : / ═══════════The draw() method accepts the following keyword arguments:
| Parameter | Type | Default | Description |
|---|---|---|---|
output | str | 'text' | Output format: 'text', 'pic', or 'latex' |
filename | str | None | Save output to this file path |
scale | float | 0.7 | Image scale factor ('pic' only) |
fold | int | 30 | Maximum image width in gates ('pic' only) |
with_logo | bool | False | Include logo in LaTeX output |
with_gate_params | bool | False | Show gate parameters |
line_length | int | 100 | Maximum line length for text output |
console_encode_type | str | 'utf8' | Character encoding: 'utf8' or 'gbk' |
1.4 Drawing as an Image (PIC)
The 'pic' output format uses matplotlib to render a colorful circuit diagram and save it as an image file (JPEG or PNG). This is useful for reports, presentations, and Jupyter notebooks.
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
# Save as JPEG with default filename
prog.draw('pic')
# Save to a custom path with custom scale
prog.draw('pic', filename='my_circuit.png', scale=0.5)
# Show gate parameters in the image
prog.draw('pic', filename='circuit_with_params.jpg', with_gate_params=True)When using 'pic' output without specifying a filename, the image is saved to QCircuit_pic.jpg in the current working directory.
flowchart LR
A["QProg / QCircuit"] --> B[".draw('pic')"]
B --> C["MatplotlibDrawer"]
C --> D["JPEG / PNG file"]
C --> E["Jupyter inline display"]
style A fill:#4a90d9,color:#fff
style D fill:#50b86c,color:#fff
style E fill:#e8913a,color:#fff1.5 LaTeX Output
LaTeX output produces source code that you can embed in academic papers, theses, or any LaTeX document. The output uses the qcircuit TikZ library for typesetting circuit diagrams.
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0) << core.T(1) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
# Get LaTeX source code
latex = prog.draw('latex')
print(latex)
# Save to .tex file with logo
prog.draw('latex', filename='paper_circuit.tex', with_logo=True)
# Also accessible via core.draw_qprog (returns placeholder text)
# Use prog.draw('latex') for working LaTeX output运行结果:
\documentclass[border=2px]{standalone}
\usepackage[braket, qm]{qcircuit}
\usepackage{graphicx}
\begin{document}
\scalebox{1.0}{
\Qcircuit @C = 1.0em @R = 0.5em @!R { \\
\nghost{} & \lstick{\ket{q_0}} & \gate{\mathrm{H}} & \qw & \ctrl{1} & \meter & \qw & \rstick{}\qw & \nghost{} \\
\nghost{} & \lstick{\ket{q_1}} & \qw & \gate{\mathrm{T}} & \targ\qw & \qw & \meter & \rstick{}\qw & \nghost{} \\
\nghost{c0} & \lstick{\mathrm{c:c0}} & \cw & \cw & \cw & \dstick{__{0}}\cw \ar @{<=} [-2,0] & \cw & \rstick{}\cw & \nghost{} \\
\nghost{c1} & \lstick{\mathrm{c:c1}} & \cw & \cw & \cw & \cw & \dstick{__{1}}\cw \ar @{<=} [-2,0] & \rstick{}\cw & \nghost{} \\
}}
\end{document}To compile the LaTeX output into a PDF, you need a LaTeX distribution with the qcircuit package installed. A minimal compilation command is:
pdflatex circuit.tex1.6 Drawing Parameters in Detail
param_show / with_gate_params
By default, parameterized gates (such as RX, RY, RZ, U3) are drawn without their numerical parameters. Set param_show=True (for core.draw_qprog) or with_gate_params=True (for .draw()) to display them:
from pyqpanda3 import core
prog = core.QProg()
prog << core.RX(0, 1.5708) << core.RY(1, 0.7854) << core.CNOT(0, 1)
# Without parameters (default)
print(prog.draw())
# With parameters
print(prog.draw(with_gate_params=True))运行结果:
┌──┐
q_0: |0>─┤RX├ ───*──
├──┤ ┌──┴─┐
q_1: |0>─┤RY├ ┤CNOT├
└──┘ └────┘
c : / ════════════
┌──────────────┐
q_0: |0>─┤RX(1.57080000)├ ───*──
├──────────────┤ ┌──┴─┐
q_1: |0>─┤RY(0.78540000)├ ┤CNOT├
└──────────────┘ └────┘
c : / ════════════════════════with_logo
When using LaTeX output, setting with_logo=True includes the pyqpanda3 logo in the circuit diagram header. This is mainly useful for presentations and demonstrations.
line_length
Controls the maximum number of characters per line in text output. Circuits that exceed this length wrap to multiple lines:
# Short lines for narrow terminals
result = prog.draw('text', line_length=60)expend_map
The expend_map parameter controls expansion of nested circuits. Passing {"all": 1} expands all nested sub-circuits into individual gates:
# Expand all nested circuits
result = core.draw_qprog(
prog,
p=core.PIC_TYPE.TEXT,
expend_map={"all": 1}
)encode
Controls the character encoding for text output. The default is "utf-8". Use "gbk" for compatibility with terminals that use GBK encoding (common in some Chinese locales).
2. Print Options
The core.set_print_options() function configures global defaults for how circuit information is displayed when using print() or core.draw_qprog(). This affects all subsequent print operations in your session.
from pyqpanda3 import core
# Configure print options
core.set_print_options(
precision=8, # Decimal places for gate parameters
param_show=True, # Show gate parameters by default
linewidth=100 # Maximum line width
)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
precision | int | 8 | Number of decimal places for floating-point parameters |
param_show | bool | True | Whether to display gate parameters |
linewidth | int | 100 | Maximum line width for text output |
Usage Example
from pyqpanda3 import core
# Build a circuit with parameterized gates
prog = core.QProg()
prog << core.RX(0, 3.14159265) << core.RY(1, 1.57079633) << core.CNOT(0, 1)
# Default print options
print("Default:")
print(prog)
# Set high precision with parameters visible
core.set_print_options(precision=4, param_show=True, linewidth=80)
print("\nWith options (precision=4, param_show=True):")
print(prog)
# Reset to defaults
core.set_print_options(precision=8, param_show=False, linewidth=100)运行结果:
Default:
┌──────────────┐
q_0: |0>─┤RX(3.14159265)├ ───*──
├──────────────┤ ┌──┴─┐
q_1: |0>─┤RY(1.57079633)├ ┤CNOT├
└──────────────┘ └────┘
c : / ════════════════════════
With options (precision=4, param_show=True):
┌──────────┐
q_0: |0>─┤RX(3.1416)├ ───*──
├──────────┤ ┌──┴─┐
q_1: |0>─┤RY(1.5708)├ ┤CNOT├
└──────────┘ └────┘
c : / ════════════════════The precision setting is particularly useful when working with variational circuits where you need to inspect parameter values during optimization.
3. The visualization Module
The pyqpanda3.visualization module provides rich graphical visualization tools that go beyond text-based circuit drawing. It depends on matplotlib for rendering and offers functions for Bloch sphere plots, state city visualizations, density matrix heatmaps, and probability bar charts.
3.1 Importing the Module
# Import specific functions
from pyqpanda3.visualization import (
plot_bloch,
plot_state,
plot_probabilities,
circuit_summary
)
# Or import the module as a whole
import pyqpanda3.visualization as vizThe module also exposes lower-level functions for specialized use cases:
from pyqpanda3.visualization import (
plot_bloch_vector,
plot_bloch_multivector,
plot_bloch_circuit,
plot_state_city,
plot_density_matrix,
draw_probability,
draw_probability_dict
)3.2 Bloch Sphere Visualization
The Bloch sphere is a geometric representation of a single-qubit quantum state. Any pure single-qubit state can be written as:
and represented as a point on the unit sphere with coordinates
The visualization module provides three functions for Bloch sphere rendering:
plot_bloch_vector(bloch, title, axis_obj, fig_size)
Plot a single Bloch vector on a 3D sphere. The input is a list of three floats [x, y, z] representing the Cartesian coordinates of the Bloch vector.
from pyqpanda3.visualization import plot_bloch_vector
# Plot the |0> state (north pole, z-axis)
plot_bloch_vector([0, 0, 1], title="|0> state")
# Plot the |1> state (south pole)
plot_bloch_vector([0, 0, -1], title="|1> state")
# Plot the |+> state (x-axis)
plot_bloch_vector([1, 0, 0], title="|+> state")
# Plot the |-i> state (-y-axis)
plot_bloch_vector([0, -1, 0], title="|-i> state")
Common single-qubit states and their Bloch vectors:
| State | Bloch Vector | Description |
|---|---|---|
[0, 0, 1] | North pole | |
[0, 0, -1] | South pole | |
[1, 0, 0] | +X direction | |
[-1, 0, 0] | -X direction | |
[0, 1, 0] | +Y direction | |
[0, -1, 0] | -Y direction |
plot_bloch_multivector(state, title, fig_size)
Plot each qubit of a multi-qubit state on its own Bloch sphere. This function takes a state vector (list or numpy array) and computes the reduced density matrix for each qubit to determine its Bloch vector.
import numpy as np
from pyqpanda3.visualization import plot_bloch_multivector
# Bell state: (|00> + |11>) / sqrt(2)
# Each qubit is maximally mixed -> Bloch vector at origin
bell_state = np.array([1, 0, 0, 1]) / np.sqrt(2)
plot_bloch_multivector(bell_state, title="Bell State Qubits")
# Product state: |+>|0>
# Qubit 0 at +X, qubit 1 at +Z
product_state = np.array([1, 1, 0, 0]) / np.sqrt(2)
plot_bloch_multivector(product_state, title="|+>|0> State")
For entangled states like the Bell state, the reduced state of each individual qubit is a maximally mixed state, so the Bloch vector sits at the origin. This is a visual indication of entanglement.
plot_bloch_circuit(circuit, trace, saveas, fps, secs_per_gate)
Animate the evolution of a single-qubit circuit on the Bloch sphere. This function traverses the circuit gate by gate, showing how each gate rotates the state vector on the sphere. It supports common single-qubit gates: H, X, Y, Z, RX, RY, RZ, S, T, and their dagger variants.
from pyqpanda3 import core
from pyqpanda3.visualization import plot_bloch_circuit
# Build a single-qubit circuit
circuit = core.QCircuit()
circuit << core.H(0)
circuit << core.RY(0, 1.5708) # pi/2 rotation
circuit << core.Z(0)
# Animate the circuit on the Bloch sphere
plot_bloch_circuit(
circuit,
trace=True, # Show the path of the state vector
saveas='bloch_animation.gif', # Save as animated GIF
fps=20, # Frames per second
secs_per_gate=1 # Duration per gate in seconds
)
When trace=True, the animation draws a colored trail showing the path of the state vector as each gate is applied. Each gate has a distinct color to help you follow the evolution.
sequenceDiagram
participant C as Circuit
participant B as Bloch Sphere
Note over B: Initial state: |0> (north pole)
C->>B: H(0)
Note over B: State rotates to |+> (equator, +X)
C->>B: RY(0, pi/2)
Note over B: State rotates around Y axis
C->>B: Z(0)
Note over B: Phase flip around Z axisUnified plot_bloch() Interface
The plot_bloch() function provides a unified interface that automatically detects the input type and calls the appropriate function:
from pyqpanda3 import core
from pyqpanda3.visualization import plot_bloch
# Single Bloch vector (list/tuple of 3 numbers)
plot_bloch([1, 0, 0], title="X State")
# State vector (array-like)
import numpy as np
state = np.array([1, 0, 0, 1]) / np.sqrt(2)
plot_bloch(state, title="Bell State")
# Circuit (single-qubit only)
circuit = core.QCircuit()
circuit << core.H(0) << core.T(0)
plot_bloch(circuit)
3.3 State City Plots
State city plots provide a 3D visualization of a quantum state's density matrix. They show both the real and imaginary parts of the density matrix as side-by-side bar charts, giving you an intuitive picture of the state's structure.
The density matrix for a pure state
plot_state_city(state, title, figsize, color)
import numpy as np
from pyqpanda3.visualization import plot_state_city
# Bell state density matrix
bell_state = np.array([1, 0, 0, 1]) / np.sqrt(2)
plot_state_city(bell_state, title="Bell State Density Matrix")
# Custom colors and figure size
plot_state_city(
bell_state,
title="Bell State",
figsize=(12, 4),
color=["#1f77b4", "#ff7f0e"] # [real color, imaginary color]
)
The function accepts either a state vector (1D array) or a density matrix (2D array). When given a state vector, it computes the density matrix automatically.
The left subplot shows
All entries are real, so the imaginary part is zero and the right subplot shows a flat surface.
Unified plot_state() Interface
from pyqpanda3.visualization import plot_state
import numpy as np
state = np.array([1, 0, 0, 1]) / np.sqrt(2)
# City plot (default)
plot_state(state, kind='city', title="Bell State")
# Density matrix heatmap
rho = np.outer(state, np.conj(state))
plot_state(rho, kind='density', title="Bell State")
The kind parameter selects the visualization type: 'city' for a 3D bar plot, 'density' for a heatmap with phase coloring.
3.4 Density Matrix Visualization
The plot_density_matrix() function renders the density matrix as a 3D bar chart where the height of each bar represents the absolute value
import numpy as np
from pyqpanda3.visualization import plot_density_matrix
# Create a mixed state density matrix
rho = np.array([
[0.5, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0],
[0.5, 0.0, 0.0, 0.5]
])
fig, ax = plot_density_matrix(
rho,
title="Bell State Density Matrix",
xlabels=['00', '01', '10', '11'],
ylabels=['00', '01', '10', '11']
)
The color bar maps the phase angle from
3.5 Probability Bar Charts
Probability bar charts visualize measurement outcome distributions. They are useful for inspecting simulator results and understanding the output distribution of quantum circuits.
plot_probabilities(data, title, figsize, top_k, save)
The unified plot_probabilities() function accepts either a dictionary mapping state labels to probabilities, or a list/array of probabilities:
from pyqpanda3.visualization import plot_probabilities
# From a dictionary
plot_probabilities(
{'00': 0.5, '11': 0.5},
title="Bell State Probabilities"
)
# From a raw array (auto-generates labels)
probs = [0.5, 0, 0, 0.5]
plot_probabilities(probs, title="Bell State")
# Show only top-K most probable outcomes
noisy_results = {
'000': 0.485, '111': 0.492,
'001': 0.008, '010': 0.006,
'100': 0.005, '011': 0.004
}
plot_probabilities(noisy_results, top_k=3, title="Noisy GHZ (top 3)")
When top_k is specified, only the k most probable outcomes are displayed. This is useful for circuits with many qubits where most basis states have near-zero probability.
draw_probability() and draw_probability_dict()
Lower-level functions for backward compatibility:
from pyqpanda3.visualization import draw_probability, draw_probability_dict
# From a dictionary of counts or probabilities
data = {'00': 497, '11': 503}
draw_probability(data)
# From a probability dictionary (filters zero entries)
draw_probability_dict({'|00>': 0.5, '|11>': 0.5})Computing Probabilities from Simulation Results
A typical workflow combines simulation with probability visualization:
from pyqpanda3 import core
from pyqpanda3.visualization import plot_probabilities
# Build and run a circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
machine = core.CPUQVM()
machine.run(prog, shots=10000)
counts = machine.result().get_counts()
# Convert counts to probabilities
total = sum(counts.values())
probs = {state: count / total for state, count in counts.items()}
# Visualize
plot_probabilities(probs, title="Bell State Measurement (10000 shots)")3.6 Circuit Summary
The circuit_summary() function provides a statistical overview of a circuit's composition: how many single-qubit gates, two-qubit gates, and multi-control gates it contains, along with layer information.
from pyqpanda3 import core
from pyqpanda3.visualization import circuit_summary
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.CNOT(0, 1) << core.SWAP(1, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# Get summary (optionally display pie chart)
summary = circuit_summary(prog, show=False)
# Print textual summary
print(summary)
# Access statistics programmatically
info = summary.info
print(f"Total nodes: {info.node_num}")
print(f"Single gates: {info.single_gate_num}")
print(f"Two-qubit gates: {info.double_gate_num}")
print(f"Total layers: {info.layer_num}")
print(f"Single layers: {info.single_gate_layer_num}")
print(f"Double layers: {info.double_gate_layer_num}")运行结果:
ProgInfo(single_gate_num=2, double_gate_num=2, multi_control_gate_num=0, node_num=4, single_gate_layer_num=1, double_gate_layer_num=2, layer_num=3)
Total nodes: 4
Single gates: 2
Two-qubit gates: 2
Total layers: 3
Single layers: 1
Double layers: 2When show=True (the default), circuit_summary() displays a pair of pie charts showing the distribution of gate types and layer types. This is useful for quickly assessing circuit complexity and estimating execution cost on hardware.
3.7 Time Scheduling Diagrams
Starting in pyqpanda3 0.4.0, the visualization module can render time scheduling diagrams — timeline views showing when each gate executes on each physical qubit. This is essential for understanding how a transpiled circuit will behave on real superconducting hardware, where gate durations and parallelism matter.
The workflow has three stages:
- Transpile and export — Decompose to native gates, transpile for the target hardware, then call
QProg.to_instruction(backend, is_scheduling=True)to produce an instruction JSON stream with timing data. - Parse — Use
parse_instruction_schedule()to convert the JSON into normalizedInstructionScheduleItemobjects. - Draw — Use
draw_time_scheduling()to render a Matplotlib or Plotly timeline figure.
flowchart LR
A["QProg"] --> B["decompose + transpile"]
B --> C["to_instruction()"]
C --> D["parse_instruction_schedule()"]
D --> E["draw_time_scheduling()"]
E --> F["Matplotlib / Plotly figure"]
style A fill:#4a90d9,color:#fff
style F fill:#50b86c,color:#fffBuilding and Exporting a Schedule
First, create a circuit and prepare it for instruction export:
from pyqpanda3 import core
from pyqpanda3.transpilation import ChipBackend, decompose
# Build a circuit
qc = core.QCircuit()
qc.append(core.X(core.Qubit(0)))
qc.append(core.H(core.Qubit(1)))
qc.append(core.CNOT(core.Qubit(0), core.Qubit(1)))
q = core.QProg()
q.append(qc)
# Decompose to native gates and transpile
decomposed = decompose(q, ["X1", "RZ", "CZ", "RPhi"])
cb = ChipBackend()
cb.chip_topology_edges = [[0, 1]]
cb.single_gate_timing = 40
cb.double_gate_timing = 80
# Export instruction JSON with timing
instruction = decomposed.to_instruction(cb, offset=1, is_scheduling=True)
print(instruction)运行结果:
[{"RPhi":[1,0.0,90.0,0]},{"RPhi":[2,270.0,90.0,0]},{"RPhi":[1,0.0,90.0,0]},{"RPhi":[2,90.0,90.0,0]},{"CZ":[1,2,0]},{"RPhi":[2,270.0,90.0,0]}]Each instruction is a single-key object: RPhi carries [qubit, angle, phi, start_time], CZ carries [q0, q1, start_time], and Measure carries [[q0, q1, ...], start_time].
Parsing and Inspecting the Schedule
from pyqpanda3.visualization import parse_instruction_schedule
items = parse_instruction_schedule(
instruction,
durations={"RPhi": 40, "CZ": 80},
)
for item in items:
print(f"{item.name:8s} q{item.qubits} t={item.start_time:3d} dur={item.duration}")运行结果:
RPhi q(1,) t= 0 dur=40.0
RPhi q(2,) t= 0 dur=40.0
RPhi q(1,) t= 0 dur=40.0
RPhi q(2,) t= 0 dur=40.0
CZ q(1, 2) t= 0 dur=80.0
RPhi q(2,) t= 0 dur=40.0Each InstructionScheduleItem has fields: name, qubits (tuple), start_time, duration, params (tuple), order, and is_marker. The end_time property returns start_time + duration.
Drawing the Timeline
from pyqpanda3.visualization import draw_time_scheduling
fig = draw_time_scheduling(
instruction,
durations={"RPhi": 40, "CZ": 80},
title="Two-Qubit Schedule",
qubit_offset=-1,
)
fig.savefig("schedule.png", dpi=150)
The output parameter controls the rendering backend:
output="mpl"(default) — Returns a MatplotlibFigure.output="plotly"— Returns a PlotlyFigure.
Key drawing parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
output | str | "mpl" | Rendering backend: "mpl" for Matplotlib, "plotly" for Plotly. |
durations | dict | None | Gate-name to duration overrides. |
backend | ChipBackend | None | Timing source (single_gate_timing, double_gate_timing). |
qubit_offset | int | -1 | Display offset: -1 maps physical q[1] to displayed q[0]. |
title | str | "QProg Instruction Schedule" | Figure title. |
merge_common_instructions | bool | False | Merge temporally adjacent same-type instructions. |
gate_filter | list[str] | None | Only draw specified gate names. |
filter_measure | bool | False | Remove Measure markers from the plot. |
time_range | (float, float) | None | Restrict drawing to a cycle range. |
max_qubits_per_panel | int | None | Split vertically into panels of at most N qubits each. |
When using output="plotly", use fig.write_html("schedule.html") to save the interactive figure instead of fig.savefig().
Complete End-to-End Example
from pyqpanda3 import core
from pyqpanda3.transpilation import ChipBackend, Transpiler, decompose
from pyqpanda3.visualization import draw_time_scheduling, parse_instruction_schedule
# Build and transpile
qc = core.QCircuit()
qc.append(core.X(core.Qubit(0)))
qc.append(core.H(core.Qubit(1)))
qc.append(core.CNOT(core.Qubit(0), core.Qubit(1)))
q = core.QProg()
q.append(qc)
backend = ChipBackend()
backend.chip_topology_edges = [[0, 1]]
backend.basic_gates = ["RPhi", "CZ"]
backend.single_gate_timing = 40
backend.double_gate_timing = 80
transpiler = Transpiler()
transpiled = transpiler.transpile(q, backend, {}, 2)
# Decompose to X1/RZ/CZ — to_instruction calls virtual_z internally
decomposed = decompose(transpiled, ["X1", "RZ", "CZ"])
instruction = decomposed.to_instruction(backend, offset=1, is_scheduling=True)
# Parse and draw
schedule = parse_instruction_schedule(instruction, backend=backend)
print(f"Total instructions: {len(schedule)}")
print(f"Qubits used: {sorted(set(q for item in schedule for q in item.qubits))}")
fig = draw_time_scheduling(
instruction,
backend=backend,
title="Transpiled Circuit Schedule",
merge_common_instructions=True,
)
fig.savefig("transpiled_schedule.png", dpi=150)运行结果:
Total instructions: 5
Qubits used: [1, 2]Durations are resolved in priority order: (1) explicit durations dict, (2) backend timing attributes, (3) gate payload duration (for IDLE), and (4) missing durations become zero-duration markers.
4. Practical Examples
4.1 Drawing a Bell State Circuit
This complete example builds a Bell state circuit and demonstrates all three output formats:
from pyqpanda3 import core
# Build the Bell state circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
# 1. Quick text print
print("=== Text via print() ===")
print(prog)
# 2. Text with parameters via core.draw_qprog
print("\n=== Text via draw_qprog ===")
text = core.draw_qprog(prog, p=core.PIC_TYPE.TEXT, param_show=True)
print(text)
# 3. LaTeX output
print("\n=== LaTeX ===")
latex = core.draw_qprog(prog, p=core.PIC_TYPE.LATEX)
print(latex)
# 4. Save as image
prog.draw('pic', filename='bell_state.jpg')
print("\nImage saved to bell_state.jpg")运行结果:
=== Text via print() ===
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 1
=== Text via draw_qprog ===
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 1
=== LaTeX ===
Latex format is temporarily not supported
Image saved to bell_state.jpg4.2 Drawing Parameterized Circuits
Parameterized circuits are common in variational algorithms. Use param_show=True or with_gate_params=True to inspect parameter values in the circuit diagram:
import math
from pyqpanda3 import core
# Build a parameterized circuit
prog = core.QProg()
prog << core.H(0)
prog << core.RY(0, math.pi / 4)
prog << core.RX(1, math.pi / 3)
prog << core.CNOT(0, 1)
prog << core.RZ(0, 0.7854)
prog << core.U3(1, 0.1, 0.2, 0.3)
prog << core.measure([0, 1], [0, 1])
# Draw without parameters
print("Without parameters:")
print(prog.draw())
# Draw with parameters
print("\nWith parameters:")
print(prog.draw(with_gate_params=True))
# Save image with parameters visible
prog.draw('pic', filename='param_circuit.jpg', with_gate_params=True)
# Set global print options so parameters always show
core.set_print_options(precision=4, param_show=True)
print("\nAfter set_print_options:")
print(prog)运行结果:
Without parameters:
┌─┐ ┌──┐ ┌──┐ ┌─┐
q_0: |0>─┤H├─ ┤RY├ ───*── ┤RZ├ ─┤M├────
├─┴┐ └──┘ ┌──┴─┐ ├──┤ └╥┘ ┌─┐
q_1: |0>─┤RX├ ──── ┤CNOT├ ┤U3├ ──╫──┤M├
└──┘ └────┘ └──┘ ║ └╥┘
c : / ═════════════════════════╩═══╩═
0 1
With parameters:
┌─┐ ┌──────────────┐ ┌──────────────┐ ┌─┐
q_0: |0>─┤H├───────────── ┤RY(0.78539816)├ ───*── ┤RZ(0.78540000)├──────────────────────── ─┤M├────
├─┴────────────┐ └──────────────┘ ┌──┴─┐ ├──────────────┴───────────────────────┐ └╥┘ ┌─┐
q_1: |0>─┤RX(1.04719755)├ ──────────────── ┤CNOT├ ┤U3(0.10000000, 0.20000000, 0.30000000)├ ──╫──┤M├
└──────────────┘ └────┘ └──────────────────────────────────────┘ ║ └╥┘
c : / ═════════════════════════════════════════════════════════════════════════════════════╩═══╩═
0 1
After set_print_options:
┌─┐ ┌──────────┐ ┌──────────┐ ┌─┐
q_0: |0>─┤H├───────── ┤RY(0.7854)├ ───*── ┤RZ(0.7854)├──────────────── ─┤M├────
├─┴────────┐ └──────────┘ ┌──┴─┐ ├──────────┴───────────────┐ └╥┘ ┌─┐
q_1: |0>─┤RX(1.0472)├ ──────────── ┤CNOT├ ┤U3(0.1000, 0.2000, 0.3000)├ ──╫──┤M├
└──────────┘ └────┘ └──────────────────────────┘ ║ └╥┘
c : / ═════════════════════════════════════════════════════════════════╩═══╩═
0 14.3 Drawing Large Circuits
Large circuits require careful management of line length and image dimensions. Here are techniques for handling circuits with many qubits or many gates:
from pyqpanda3 import core
# Build a 6-qubit GHZ-like circuit
prog = core.QProg()
prog << core.H(0)
for i in range(1, 6):
prog << core.CNOT(i - 1, i)
prog << core.measure(list(range(6)), list(range(6)))
# Use longer line length for wide circuits
text = core.draw_qprog(prog, p=core.PIC_TYPE.TEXT, line_length=150)
print(text)
# For image output, use a larger fold value to fit more gates per row
prog.draw('pic', filename='large_circuit.jpg', fold=50, scale=0.5)运行结果:
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ───────┤M├ ────────── ────────── ────────── ────────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ───*────╫─ ───────┤M├ ────────── ────────── ────────
└────┘ ┌──┴─┐ ║ └╥┘ ┌─┐
q_2: |0>──── ────── ┤CNOT├──╫─ ───*────╫─ ───────┤M├ ────────── ────────
└────┘ ║ ┌──┴─┐ ║ └╥┘ ┌─┐
q_3: |0>──── ────── ────────╫─ ┤CNOT├──╫─ ───*────╫─ ───────┤M├ ────────
║ └────┘ ║ ┌──┴─┐ ║ └╥┘ ┌─┐
q_4: |0>──── ────── ────────╫─ ────────╫─ ┤CNOT├──╫─ ───*────╫─ ─┤M├────
║ ║ └────┘ ║ ┌──┴─┐ ║ └╥┘ ┌─┐
q_5: |0>──── ────── ────────╫─ ────────╫─ ────────╫─ ┤CNOT├──╫─ ──╫──┤M├
║ ║ ║ └────┘ ║ ║ └╥┘
c : / ════════════════════╩══════════╩══════════╩══════════╩════╩═══╩═
0 1 2 3 4 5For very large circuits (20+ qubits), text output may become unwieldy. In these cases, use the LaTeX output and compile it to PDF, or use circuit_summary() to inspect statistics rather than the full diagram:
from pyqpanda3.visualization import circuit_summary
# Get statistics instead of full diagram
summary = circuit_summary(prog, show=True)
print(f"\nCircuit has {summary.info.node_num} gates across {summary.info.layer_num} layers")运行结果:
Circuit has 6 gates across 6 layers4.4 Exporting to File
pyqpanda3 supports exporting circuit diagrams in multiple file formats for different use cases:
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
# Text file (useful for version control and diffs)
core.draw_qprog(prog, p=core.PIC_TYPE.TEXT, output_file="circuit.txt")
# LaTeX file (for papers and presentations)
prog.draw('latex', filename="circuit.tex", with_logo=True)
# Image file (for reports and quick sharing)
prog.draw('pic', filename='circuit_diagram.png', scale=0.7)
# Alternative: use the draw() method with filename
prog.draw('text', filename='circuit_text.txt')
prog.draw('latex', filename='circuit_latex.tex')The following table summarizes the output formats and their typical use cases:
| Format | File Extension | Best For |
|---|---|---|
| Text | .txt | Debugging, terminal output, version control |
| LaTeX | .tex | Academic papers, presentations, documentation |
| Image | .jpg, .png | Reports, slides, quick sharing, Jupyter |
flowchart TD
A["QProg / QCircuit"] --> B{Choose Format}
B -->|Terminal / Debug| C["Text (.txt)"]
B -->|Paper / Presentation| D["LaTeX (.tex)"]
B -->|Report / Quick View| E["Image (.jpg/.png)"]
C --> F["print() or .draw('text')"]
D --> G[".draw('latex')"]
E --> H[".draw('pic')"]
F --> I["Console output or text file"]
G --> J["pdflatex -> PDF"]
H --> K["JPEG/PNG image file"]
style A fill:#4a90d9,color:#fff
style I fill:#50b86c,color:#fff
style J fill:#7b68ee,color:#fff
style K fill:#e8913a,color:#fff4.5 Visualizing a GHZ State End-to-End
This example demonstrates a complete workflow: build a circuit, simulate it, draw the circuit diagram, and visualize the results.
from pyqpanda3 import core
from pyqpanda3.visualization import plot_probabilities, plot_bloch_multivector, circuit_summary
# Build a 3-qubit GHZ state circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.CNOT(1, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# Step 1: Draw the circuit
print("=== Circuit Diagram ===")
print(prog.draw())
# Step 2: Get circuit statistics
print("\n=== Circuit Summary ===")
summary = circuit_summary(prog, show=False)
print(f"Gates: {summary.info.node_num}, Layers: {summary.info.layer_num}")
print(f"Single-qubit gates: {summary.info.single_gate_num}")
print(f"Two-qubit gates: {summary.info.double_gate_num}")
# Step 3: Simulate
machine = core.CPUQVM()
machine.run(prog, shots=10000)
# Step 4: Visualize probabilities
counts = machine.result().get_counts()
total = sum(counts.values())
probs = {state: count / total for state, count in counts.items()}
plot_probabilities(probs, title="GHZ State Probabilities (10000 shots)")
# Step 5: Visualize the GHZ state on Bloch spheres
# (requires the state vector before measurement)
ghz_circuit = core.QCircuit()
ghz_circuit << core.H(0) << core.CNOT(0, 1) << core.CNOT(1, 2)
state = ghz_circuit.matrix()[:, 0] # first column = |000> input
plot_bloch_multivector(state, title="GHZ State (Per-Qubit Bloch)")For the GHZ state
4.6 Bloch Sphere Animation of a Single-Qubit Circuit
This example creates an animated Bloch sphere visualization showing how a sequence of gates transforms a single qubit:
import math
from pyqpanda3 import core
from pyqpanda3.visualization import plot_bloch_circuit, plot_bloch_vector
# Build a single-qubit circuit with several gates
circuit = core.QCircuit()
circuit << core.H(0) # Move to equator (+X)
circuit << core.T(0) # Rotate pi/4 around Z
circuit << core.RY(0, math.pi/4) # Tilt toward +Z
circuit << core.Z(0) # Flip phase
# Animate the circuit on the Bloch sphere
plot_bloch_circuit(
circuit,
trace=True,
saveas='single_qubit_evolution.gif',
fps=20,
secs_per_gate=1
)
# Static visualization of specific states
from pyqpanda3.visualization import plot_bloch
# Show the |0> starting state
plot_bloch([0, 0, 1], title="Initial |0> state")
# Show the state after Hadamard (|+>)
plot_bloch([1, 0, 0], title="After H: |+> state")
# Show the state after H then T
plot_bloch([1/math.sqrt(2), 1/math.sqrt(2), 0], title="After H+T")The animation proceeds through each gate in order, showing the state vector rotating on the sphere. The trace=True parameter leaves a colored trail so you can see the full path of the state.
5. Visualization Workflow
The following diagram illustrates the complete visualization workflow in pyqpanda3, from circuit construction to visual output:
flowchart TD
A["Build Circuit<br/>(QProg / QCircuit)"] --> B{What to visualize?}
B -->|"Circuit diagram"| C{Output format?}
C -->|"Quick view"| D["print(prog)"]
C -->|"Text file"| E["core.draw_qprog(..., TEXT)"]
C -->|"LaTeX / PDF"| F["prog.draw('latex')"]
C -->|"Image"| G["prog.draw('pic')"]
B -->|"Quantum state"| H{How many qubits?}
H -->|"1 qubit"| I["plot_bloch_vector()"]
H -->|"Multi-qubit"| J["plot_bloch_multivector()"]
H -->|"Density matrix"| K["plot_state_city() or plot_density_matrix()"]
B -->|"Measurement results"| L["plot_probabilities()"]
B -->|"Circuit statistics"| M["circuit_summary()"]
B -->|"State evolution"| N["plot_bloch_circuit()"]
B -->|"Instruction timing"| O["draw_time_scheduling()"]
style A fill:#4a90d9,color:#fff
style D fill:#50b86c,color:#fff
style E fill:#50b86c,color:#fff
style F fill:#7b68ee,color:#fff
style G fill:#e8913a,color:#fff
style I fill:#c0392b,color:#fff
style J fill:#c0392b,color:#fff
style K fill:#c0392b,color:#fff
style L fill:#2ecc71,color:#fff
style M fill:#2ecc71,color:#fff
style N fill:#c0392b,color:#fff
style O fill:#c0392b,color:#fffDecision Guide
| Your Goal | Recommended Function |
|---|---|
| Quick circuit inspection | print(prog) |
| Save circuit to text file | core.draw_qprog(prog, p=core.PIC_TYPE.TEXT, output_file='out.txt') |
| Circuit image for a report | prog.draw('pic', filename='circuit.jpg') |
| Circuit for a paper (LaTeX) | prog.draw('latex', filename='circuit.tex') |
| See gate parameters | prog.draw(with_gate_params=True) or core.set_print_options(param_show=True) |
| Inspect a single-qubit state | plot_bloch_vector([x, y, z]) |
| Inspect a multi-qubit state | plot_bloch_multivector(state_vector) |
| View density matrix structure | plot_state_city(state) or plot_state(state, kind='density') |
| View measurement distribution | plot_probabilities(counts_dict) |
| Animate single-qubit evolution | plot_bloch_circuit(circuit) |
| Get circuit gate statistics | circuit_summary(prog) |
| Visualize instruction timing | draw_time_scheduling(instruction, backend=...) |
6. API Quick Reference
Core Module (pyqpanda3.core)
| Function / Constant | Description |
|---|---|
core.draw_qprog(prog, p, ...) | Draw circuit as text or LaTeX |
core.PIC_TYPE.TEXT | Text output format |
core.PIC_TYPE.LATEX | LaTeX output format |
core.set_print_options(precision, param_show, linewidth) | Configure global print settings |
core.draw_qprog() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
prog | QProg or QCircuit | (required) | Circuit to draw |
p | PIC_TYPE | PIC_TYPE.TEXT | Output format |
expend_map | dict | None | Control expansion, e.g., {"all": 1} |
param_show | bool | False | Show gate parameters |
with_logo | bool | False | Include logo (LaTeX only) |
line_length | int | 100 | Max line length (text only) |
output_file | str | "" | Save to file (empty = return string) |
encode | str | "utf-8" | Character encoding |
Member Method (.draw())
| Parameter | Type | Default | Description |
|---|---|---|---|
output | str | 'text' | 'text', 'pic', or 'latex' |
filename | str | None | Output file path |
scale | float | 0.7 | Image scale (pic only) |
fold | int | 30 | Max image width in gates (pic only) |
with_logo | bool | False | Include logo (LaTeX only) |
with_gate_params | bool | False | Show gate parameters |
line_length | int | 100 | Max line length (text only) |
console_encode_type | str | 'utf8' | 'utf8' or 'gbk' |
Visualization Module (pyqpanda3.visualization)
| Function | Description |
|---|---|
plot_bloch(state, title, figsize, save) | Unified Bloch sphere interface |
plot_bloch_vector(bloch, title, axis_obj, fig_size) | Single Bloch vector |
plot_bloch_multivector(state, title, fig_size) | Per-qubit Bloch spheres |
plot_bloch_circuit(circuit, trace, saveas, fps, secs_per_gate) | Animate circuit on Bloch sphere |
plot_state(state, kind, title, figsize) | Unified state visualization |
plot_state_city(state, title, figsize, color) | 3D density matrix bars |
plot_density_matrix(M, title, ...) | Density matrix heatmap |
plot_probabilities(data, title, figsize, top_k, save) | Probability bar chart |
circuit_summary(prog, show) | Circuit statistics and pie charts |
InstructionScheduleItem | Frozen dataclass for one normalized instruction |
parse_instruction_schedule(instruction, durations, backend) | Parse instruction JSON into schedule items |
draw_time_scheduling(instruction, durations, backend, output, ...) | Draw instruction timing diagram |
Summary
In this tutorial you learned:
Circuit drawing -- Use
print(prog)for quick inspection,core.draw_qprog()for controlled output, and.draw()for an object-oriented interface. Three output formats are available: text (ASCII), image (matplotlib), and LaTeX.Print options --
core.set_print_options()configures the precision, parameter visibility, and line width for all text-based circuit output.Bloch sphere -- Visualize single-qubit states with
plot_bloch_vector(), multi-qubit states per-qubit withplot_bloch_multivector(), and animate single-qubit circuit evolution withplot_bloch_circuit().State visualization -- Use
plot_state_city()for 3D bar plots of the density matrix,plot_density_matrix()for phase-colored heatmaps, andplot_state()as a unified interface.Probability charts --
plot_probabilities()renders measurement outcome distributions, with optionaltop_kfiltering for large state spaces.Exporting -- Save diagrams as text files, LaTeX source, or JPEG/PNG images using
output_fileorfilenameparameters.Time scheduling -- Parse instruction JSON from
QProg.to_instruction()into normalized schedule items, and render per-qubit timing diagrams usingdraw_time_scheduling().
The next tutorial in the series covers Transpilation, where you will learn how to optimize circuits for specific hardware topologies and gate sets.