Note

This page was generated from tutorials/circuits_advanced/05_pulse_gates.ipynb.

# Pulse gates¶

Most quantum algorithms can be described with circuit operations alone. When we need more control over the low-level implementation of our program, we can use pulse gates. Pulse gates remove the constraint of executing circuits with basis gates only, and also allow you to override the default implementation of any basis gate.

Pulse gates allow you to map a logical circuit gate (e.g., X) to a Qiskit Pulse program, called a Schedule. This mapping is referred to as a calibration. A high fidelity calibration is one which faithfully implements the logical operation it is mapped from (e.g., whether the X gate calibration drives $$|0\rangle$$ to $$|1\rangle$$, etc.).

A schedule specifies the exact time dynamics of the input signals across all input channels to the device. There are usually multiple channels per qubit, such as drive and measure. This interface is more powerful, and requires a deeper understanding of the underlying device physics.

It’s important to note that Pulse programs operate on physical qubits. A drive pulse on qubit $$a$$ will not enact the same logical operation on the state of qubit $$b$$ – in other words, gate calibrations are not interchangeable across qubits. This is in contrast to the circuit level, where an X gate is defined independent of its qubit operand.

This page shows you how to add a calibration to your circuit.

Note: To execute a program with pulse gates, the backend has to be enabled with OpenPulse. You can check via backend.configuration().open_pulse, which is True when OpenPulse is enabled. If it is enabled and the pulse gates feature is not enabled, you can schedule your input circuit.

## Build your circuit¶

Let’s start with a very simple example, a Bell state circuit.

[1]:

from qiskit import QuantumCircuit

circ = QuantumCircuit(2, 2)
circ.h(0)
circ.cx(0, 1)
circ.measure(0, 0)
circ.measure(1, 1)

circ.draw('mpl')

[1]:


## Build your calibrations¶

Now that we have our circuit, let’s define a calibration for the Hadamard gate on qubit 0.

In practice, the pulse shape and its parameters would be optimized through a series of Rabi experiments (see the Qiskit Textbook for a walk through). For this demonstration, our Hadamard will be a Gaussian pulse. We will play our pulse on the drive channel of qubit 0.

Don’t worry too much about the details of building the calibration itself; you can learn all about this on the following page: building pulse schedules.

[2]:

from qiskit import pulse
from qiskit.pulse.library import Gaussian
from qiskit.test.mock import FakeValencia

backend = FakeValencia()

with pulse.build(backend, name='hadamard') as h_q0:
pulse.play(Gaussian(duration=128, amp=0.1, sigma=16), pulse.drive_channel(0))

/tmp/ipykernel_17326/2332443860.py:3: DeprecationWarning: The module 'qiskit.test.mock' is deprecated since Qiskit Terra 0.21.0, and will be removed 3 months or more later. Instead, you should import the desired object directly 'qiskit.providers.fake_provider'.
from qiskit.test.mock import FakeValencia


Let’s draw the new schedule to see what we’ve built.

[3]:

h_q0.draw()

[3]:


## Custom gates¶

We’ll briefly show the same process for nonstandard, completely custom gates. This demonstration includes a gate with parameters.

[6]:

from qiskit import QuantumCircuit
from qiskit.circuit import Gate

circ = QuantumCircuit(1, 1)
custom_gate = Gate('my_custom_gate', 1, [3.14, 1])
# 3.14 is an arbitrary parameter for demonstration
circ.append(custom_gate, [0])
circ.measure(0, 0)

circ.draw('mpl')

[6]:

[7]:

with pulse.build(backend, name='custom') as my_schedule:
pulse.play(Gaussian(duration=64, amp=0.2, sigma=8), pulse.drive_channel(0))

circ.add_calibration('my_custom_gate', [0], my_schedule, [3.14, 1])
# Alternatively: circ.add_calibration(custom_gate, [0], my_schedule)


If we use the Gate instance variable custom_gate to add the calibration, the parameters are derived from that instance. Remember that the order of parameters is meaningful.

[8]:

circ = transpile(circ, backend)
circ.draw('mpl', idle_wires=False)

[8]:


Normally, if we tried to transpile our circ, we would get an error. There was no functional definition provided for "my_custom_gate", so the transpiler can’t unroll it to the basis gate set of the target device. We can show this by trying to add "my_custom_gate" to another qubit which hasn’t been calibrated.

[9]:

circ = QuantumCircuit(2, 2)
circ.append(custom_gate, [1])

from qiskit import QiskitError
try:
circ = transpile(circ, backend)
except QiskitError as e:
print(e)

"Cannot unroll the circuit to the given basis, ['id', 'u1', 'u2', 'u3', 'cx']. Instruction my_custom_gate not found in equivalence library and no rule found to expand."

[10]:

import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright


### Version Information

Qiskit SoftwareVersion
qiskit-terra0.21.0
qiskit-aer0.10.4
qiskit-ibmq-provider0.19.2
qiskit0.37.0
qiskit-nature0.4.1
qiskit-finance0.3.3
qiskit-optimization0.4.0
qiskit-machine-learning0.4.0
System information
Python version3.8.13
Python compilerGCC 9.4.0
Python builddefault, Jun 20 2022 14:28:56
OSLinux
CPUs2
Memory (Gb)6.783603668212891
Thu Jun 30 18:29:38 2022 UTC

### This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.