I do a VQE using the pennylane UCCSD ansatz for execution on the 'default.qubit' device and am interested in the actual circuit that is executed on the device (ideally without making changes in any pennylane code directly). Is there a way to retrieve it?

So far, I found out that the QNode constructs a QuantumTape which has operations and measurements. These operations are executed using execute() or new_execute(). Is that what is executed directly on the device or is there a step where something like QuantumTape.expand() is done. If yes, what controls the depth? Is there a further compilation step? How do I access the tape? What if I use a different device (eg. qiskitAer as plug-in)?

(Addition: If anyone could explain the queueing happening in the QNode in this context a bit as well I would be grateful.)

Follow up: Using the expasion_strategy argument of draw() as explained in the answer is very helpful. But is there a way to also show the transpiled circuit including the rotations for the measurement? Using draw() shows the measurement only as a whole (eg. '' in the example by Isaac), but when using eg. the qiskit plugin the full circuit including the pre-measurement rotations is transpiled, isn't it?


1 Answer 1


The PennyLane circuit drawer (both the text version qml.draw and the matplotlib version qml.draw_mpl) have keyword arguments that control how you want the circuit displayed. By default, it will print the circuit as defined in the QNode, but you can pass expansion_strategy="device" to instead display the circuit as it will be expanded for the device.

For example:

dev = qml.device("default.qubit", wires=4)

hf_state = np.array([1, 1, 0, 0], requires_grad=True)
coordinates = np.array([0.0, 0.0, -0.66140414, 0.0, 0.0, 0.66140414])
H, _ = qml.qchem.molecular_hamiltonian(["H", "H"], coordinates)

def circuit(weights):
        wires=[0, 1, 2, 3],
        s_wires=[[0, 1, 2], [1, 2, 3]],
        d_wires=[[[0, 1], [2, 3]]],
    return qml.expval(H)

weights = np.random.normal(0, np.pi, 3)

Drawing the circuit with no arguments:

>>> print(qml.draw(circuit)(weights))
0: ─╭UCCSD(M0)─┤ ╭<𝓗>
1: ─├UCCSD(M0)─┤ ├<𝓗>
2: ─├UCCSD(M0)─┤ ├<𝓗>
3: ─╰UCCSD(M0)─┤ ╰<𝓗>

Drawing the circuit with the expansion strategy set:

>>> print(qml.draw(circuit, expansion_strategy="device")(weights))
0: ─╭BasisState(M0)──H─────────╭●───────────────────────╭●──────────H──────────RX(-1.57)─╭●──────────────────────────────╭●──────────RX(1.57)───H─────────╭●──────────────────────────────╭●──────────H─────────H─╭●──────────────────────────────╭●──H──RX(-1.57)─╭●────────────────────────╭●──RX(1.57)──H─────────╭●────────────────────────╭●──H──────────RX(-1.57)─╭●────────────────────────╭●──────────RX(1.57)──RX(-1.57)─╭●───────────────────────────────╭●──RX(1.57)──RX(-1.57)─╭●─────────────────╭●──RX(1.57)───H─╭●──────────────────╭●─────────H──────────────────────────────────────────────────────────────────────────────┤ ╭<𝓗>
1: ─├BasisState(M0)──H─────────╰X─╭●─────────────────╭●─╰X──────────H──────────H─────────╰X─╭●─────────────────╭●────────╰X──────────H──────────RX(-1.57)─╰X─╭●─────────────────╭●────────╰X──────────RX(1.57)──H─╰X─╭●─────────────────╭●────────╰X──H──H─────────╰X─╭●──────────────────╭●─╰X──H─────────RX(-1.57)─╰X─╭●──────────────────╭●─╰X──RX(1.57)───RX(-1.57)─╰X─╭●──────────────────╭●─╰X──────────RX(1.57)──RX(-1.57)─╰X─╭●──────────────────╭●────────╰X──RX(1.57)────────────╰X─╭●───────────╭●─╰X───────────────╰X─╭●────────────╭●─╰X─────────RX(-1.57)─╭●──────────────────╭●──RX(1.57)───H─╭●─────────────────╭●─────────H─┤ ├<𝓗>
2: ─├BasisState(M0)──RX(-1.57)────╰X─╭●───────────╭●─╰X──RX(1.57)───RX(-1.57)───────────────╰X─╭●───────────╭●─╰X─────────RX(1.57)───RX(-1.57)───────────────╰X─╭●───────────╭●─╰X─────────RX(1.57)───H──────────────╰X─╭●───────────╭●─╰X─────────H──H───────────────╰X─╭●────────────╭●─╰X──H──H──────────────────────╰X─╭●────────────╭●─╰X──H──RX(-1.57)───────────────╰X─╭●────────────╭●─╰X──RX(1.57)───H──────────────────────╰X─╭●────────────╭●─╰X─────────H──H──────────────────────╰X──RZ(2.69)─╰X──H──RX(-1.57)───────╰X──RZ(-2.69)─╰X──RX(1.57)────────────╰X─╭●────────────╭●─╰X───────────────╰X─╭●───────────╭●─╰X───────────┤ ├<𝓗>
3: ─╰BasisState(M0)──H───────────────╰X──RZ(0.42)─╰X──H──RX(-1.57)─────────────────────────────╰X──RZ(0.42)─╰X──RX(1.57)──RX(-1.57)─────────────────────────────╰X──RZ(0.42)─╰X──RX(1.57)──RX(-1.57)────────────────────╰X──RZ(0.42)─╰X──RX(1.57)──H─────────────────────╰X──RZ(-0.42)─╰X──H──H────────────────────────────╰X──RZ(-0.42)─╰X──H──H─────────────────────────────╰X──RZ(-0.42)─╰X──H──RX(-1.57)────────────────────────────╰X──RZ(-0.42)─╰X──RX(1.57)──H──────────────────────────────────────────────────────────────────────────────────────────────────────╰X──RZ(-2.58)─╰X──H──RX(-1.57)───────╰X──RZ(2.58)─╰X──RX(1.57)────┤ ╰<𝓗>
  • $\begingroup$ This works, but one thing I observe is: If I use draw() as you described, I get an expanded circuit of CNOTs, Rx, Ry. But when using draw_mpl() the output looks as if I did not specify the expansion strategy $\endgroup$ Sep 28, 2022 at 14:26
  • $\begingroup$ Oh interesting, this must be a bug! Thanks for letting me know, I can report this :) $\endgroup$
    – Josh Izaac
    Sep 28, 2022 at 14:39
  • $\begingroup$ Your text is the answer to my question, thank you. Despite that, could you maybe say a few words on how the shown circuit comes about? $\endgroup$ Sep 28, 2022 at 14:46
  • 1
    $\begingroup$ Got it :) The expansion of circuits is 'owned' by the device; the device is free to specify any logic it requires. However, there is also 'default' logic that is specified here: github.com/PennyLaneAI/pennylane/blob/master/pennylane/… $\endgroup$
    – Josh Izaac
    Sep 28, 2022 at 15:59
  • 1
    $\begingroup$ One more comment for completeness: bug report: github.com/PennyLaneAI/pennylane/issues/3117 $\endgroup$ Sep 28, 2022 at 17:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.