qml.drawer.draw¶
- draw(qnode, wire_order=None, show_all_wires=False, decimals=2, *, max_length=100, show_matrices=True, show_wire_labels=True, level='gradient')[source]¶
Create a function that draws the given QNode or quantum function.
- Parameters:
qnode (.QNode or Callable) – the input QNode or quantum function that is to be drawn
wire_order (Sequence[Any]) – The order (from top to bottom) to print the wires of the circuit. Defaults to the device wires. If device wires are not available, the circuit wires are sorted if possible.
show_all_wires (bool) – If True, all wires, including empty wires, are printed.
decimals (int) – How many decimal points to include when formatting operation parameters. Defaults to
2decimal points.Nonewill omit parameters from operation labels.max_length (int) – Maximum string width (columns) when printing the circuit. Defaults to
100.show_matrices (bool) – Show matrix valued parameters below all circuit diagrams. Defaults to
False.show_wire_labels (bool) – Whether or not to show the wire labels. Defaults to
True.level (None, str, int, slice) – An indication of what transforms to apply before drawing. Defaults to
"gradient". Checkget_transform_program()for more information on the allowed values and usage details of this argument.
- Returns:
A function that has the same argument signature as
qnode. When called, the function will draw the QNode/qfunc.
Example
@qml.qnode(qml.device('lightning.qubit', wires=2)) def circuit(a, w): qml.Hadamard(0) qml.CRX(a, wires=[0, 1]) qml.Rot(*w, wires=[1], id="arbitrary") qml.CRX(-a, wires=[0, 1]) return qml.expval(qml.Z(0) @ qml.Z(1))
>>> print(qml.draw(circuit)(a=2.3, w=[1.2, 3.2, 0.7])) 0: ──H─╭●─────────────────────────────────────────╭●─────────┤ ╭<Z@Z> 1: ────╰RX(2.30)──Rot(1.20,3.20,0.70,"arbitrary")─╰RX(-2.30)─┤ ╰<Z@Z>
Usage Details
By specifying the
decimalskeyword, parameters are displayed to the specified precision.>>> print(qml.draw(circuit, decimals=4)(a=2.3, w=[1.2, 3.2, 0.7])) 0: ──H─╭●─────────────────────────────────────────────────╭●───────────┤ ╭<Z@Z> 1: ────╰RX(2.3000)──Rot(1.2000,3.2000,0.7000,"arbitrary")─╰RX(-2.3000)─┤ ╰<Z@Z>
Parameters can be omitted by requesting
decimals=None:>>> print(qml.draw(circuit, decimals=None)(a=2.3, w=[1.2, 3.2, 0.7])) 0: ──H─╭●────────────────────╭●──┤ ╭<Z@Z> 1: ────╰RX──Rot("arbitrary")─╰RX─┤ ╰<Z@Z>
If the parameters are not acted upon by classical processing like
-a, thenqml.drawcan handle string-valued parameters as well:>>> @qml.qnode(qml.device('lightning.qubit', wires=1)) ... def circuit2(x): ... qml.RX(x, wires=0) ... return qml.expval(qml.Z(0)) >>> print(qml.draw(circuit2)("x")) 0: ──RX(x)─┤ <Z>
When requested with
show_matrices=True(the default), matrix valued parameters are printed below the circuit. Forshow_matrices=False, they are not printed:>>> @qml.qnode(qml.device('default.qubit', wires=2)) ... def circuit3(): ... qml.QubitUnitary(np.eye(2), wires=0) ... qml.QubitUnitary(-np.eye(4), wires=(0,1)) ... return qml.expval(qml.Hermitian(np.eye(2), wires=1)) >>> print(qml.draw(circuit3)()) 0: ──U(M0)─╭U(M1)─┤ 1: ────────╰U(M1)─┤ <𝓗(M0)> M0 = [[1. 0.] [0. 1.]] M1 = [[-1. -0. -0. -0.] [-0. -1. -0. -0.] [-0. -0. -1. -0.] [-0. -0. -0. -1.]] >>> print(qml.draw(circuit3, show_matrices=False)()) 0: ──U(M0)─╭U(M1)─┤ 1: ────────╰U(M1)─┤ <𝓗(M0)>
The
max_lengthkeyword warps long circuits:rng = np.random.default_rng(seed=42) shape = qml.StronglyEntanglingLayers.shape(n_wires=3, n_layers=3) params = rng.random(shape) @qml.qnode(qml.device('lightning.qubit', wires=3)) def longer_circuit(params): qml.StronglyEntanglingLayers(params, wires=range(3)) return [qml.expval(qml.Z(i)) for i in range(3)]
>>> print(qml.draw(longer_circuit, max_length=65, level="device")(params)) 0: ──Rot(0.77,0.44,0.86)─╭●────╭X──Rot(0.45,0.37,0.93)─╭●─╭X ··· 1: ──Rot(0.70,0.09,0.98)─╰X─╭●─│───Rot(0.64,0.82,0.44)─│──╰● ··· 2: ──Rot(0.76,0.79,0.13)────╰X─╰●──Rot(0.23,0.55,0.06)─╰X─── ··· 0: ··· ──Rot(0.83,0.63,0.76)──────────────────────╭●────╭X─┤ <Z> 1: ··· ─╭X────────────────────Rot(0.35,0.97,0.89)─╰X─╭●─│──┤ <Z> 2: ··· ─╰●────────────────────Rot(0.78,0.19,0.47)────╰X─╰●─┤ <Z>
The
wire_orderkeyword specifies the order of the wires from top to bottom:>>> print(qml.draw(circuit, wire_order=[1,0])(a=2.3, w=[1.2, 3.2, 0.7])) 1: ────╭RX(2.30)──Rot(1.20,3.20,0.70,"arbitrary")─╭RX(-2.30)─┤ ╭<Z@Z> 0: ──H─╰●─────────────────────────────────────────╰●─────────┤ ╰<Z@Z>
If the device or
wire_orderhas wires not used by operations, those wires are omitted unless requested withshow_all_wires=True>>> empty_qfunc = lambda : qml.expval(qml.Z(0)) >>> empty_circuit = qml.QNode(empty_qfunc, qml.device('lightning.qubit', wires=3)) >>> print(qml.draw(empty_circuit, show_all_wires=True)()) 0: ───┤ <Z> 1: ───┤ 2: ───┤
Drawing also works on batch transformed circuits:
from functools import partial from pennylane import numpy as np @partial(qml.gradients.param_shift, shifts=[(0.1,)]) @qml.qnode(qml.device('default.qubit', wires=1)) def transformed_circuit(x): qml.RX(x, wires=0) return qml.expval(qml.Z(0))
>>> print(qml.draw(transformed_circuit)(np.array(1.0, requires_grad=True))) 0: ──RX(1.10)─┤ <Z> 0: ──RX(0.90)─┤ <Z>
The function also accepts quantum functions rather than QNodes. This can be especially helpful if you want to visualize only a part of a circuit that may not be convertible into a QNode, such as a sub-function that does not return any measurements.
>>> def qfunc(x): ... qml.RX(x, wires=[0]) ... qml.CNOT(wires=[0, 1]) >>> print(qml.draw(qfunc)(1.1)) 0: ──RX(1.10)─╭●─┤ 1: ───────────╰X─┤
Levels:
The
levelkeyword argument allows one to select a subset of the transforms to apply on theQNodebefore carrying out any drawing. Take, for example, this circuit:@qml.transforms.merge_rotations @qml.transforms.cancel_inverses @qml.qnode(qml.device("default.qubit"), diff_method="parameter-shift") def circ(weights, order): qml.RandomLayers(weights, wires=(0, 1)) qml.Permute(order, wires=(0, 1, 2)) qml.PauliX(0) qml.PauliX(0) qml.RX(0.1, wires=0) qml.RX(-0.1, wires=0) return qml.expval(qml.PauliX(0)) order = [2, 1, 0] weights = qml.numpy.array([[1.0, 20]])
One can print the circuit without any transforms applied by passing
level="top"orlevel=0:>>> print(qml.draw(circ, level="top")(weights, order)) 0: ─╭RandomLayers(M0)─╭Permute──X──X──RX(0.10)──RX(-0.10)─┤ <X> 1: ─╰RandomLayers(M0)─├Permute────────────────────────────┤ 2: ───────────────────╰Permute────────────────────────────┤ M0 = [[ 1. 20.]]
Or print the circuit after applying the transforms manually applied on the QNode (
merge_rotationsandcancel_inverses):>>> print(qml.draw(circ, level="user", show_matrices=False)(weights, order)) 0: ─╭RandomLayers(M0)─╭Permute─┤ <X> 1: ─╰RandomLayers(M0)─├Permute─┤ 2: ───────────────────╰Permute─┤
To apply all of the transforms, including those carried out by the differentiation method and the device, use
level="device":>>> print(qml.draw(circ, level="device", show_matrices=False)(weights, order)) 0: ──RY(1.00)──╭SWAP─┤ <X> 1: ──RX(20.00)─│─────┤ 2: ────────────╰SWAP─┤
Slices can also be passed to the
levelargument. So one can, for example, request that only themerge_rotationstransform is applied:>>> print(qml.draw(circ, level=slice(1, 2), show_matrices=False)(weights, order)) 0: ─╭RandomLayers(M0)─╭Permute──X──X─┤ <X> 1: ─╰RandomLayers(M0)─├Permute───────┤ 2: ───────────────────╰Permute───────┤
Operators without wires:
Some operators deviate from the standard
Operatorclass in their handling of wires. In particular, tools likeSnapshotalways occupy all qubits, and are drawn accordingly:>>> draw_kwargs = {"wire_order" : [0, 1, 2], "show_all_wires" : True} >>> print(qml.draw(qml.Snapshot, **draw_kwargs)()) 0: ──|Snap|─┤ 1: ──|Snap|─┤ 2: ──|Snap|─┤
In addition, globally acting operators like
GlobalPhaseorIdentityare always represented on all wires:>>> print(qml.draw(qml.GlobalPhase, **draw_kwargs)(phi=0.5, wires=[])) 0: ─╭GlobalPhase(0.50)─┤ 1: ─├GlobalPhase(0.50)─┤ 2: ─╰GlobalPhase(0.50)─┤
This is the case even if they are provided with a subset of all wires:
>>> print(qml.draw(qml.GlobalPhase, **draw_kwargs)(phi=0.5, wires=[0])) 0: ─╭GlobalPhase(0.50)─┤ 1: ─├GlobalPhase(0.50)─┤ 2: ─╰GlobalPhase(0.50)─┤
For controlled versions of these globally acting operators, the control nodes are exempt from the expansion:
>>> ctrl_gphase = qml.ctrl(qml.GlobalPhase, control=[2]) >>> print(qml.draw(ctrl_gphase, **draw_kwargs)(phi=0.5, wires=[0])) 0: ─╭GlobalPhase(0.50)─┤ 1: ─├GlobalPhase(0.50)─┤ 2: ─╰●─────────────────┤