Download
Download this file as Jupyter notebook: linker.ipynb.
Introduction to linkers
This guide provides a few examples that show how to initialize linkers in QCS, as well as to use a linker pass inside an executor to modify quantum programs.
In this example, Mapping from circuit to hardware, we will show how to map circuit-level instructions to pulses that can be executed on hardware.
Mapping from circuit to hardware
Consider the following program, which applies a Pauli-X gate to a qubit labelled
0
and subsequently measures the qubit in the computational basis.
[2]:
import keysight.qcs as qcs
# initialize a qubit and an array to hold results
qubit = qcs.Qudits([0])
# define a program
program_in = qcs.Program()
program_in.add_gate(qcs.GATES.x, qubit)
program_in.add_measurement(qubit)
program_in.draw()
Program
Program
|
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||
|
|
X
Gate X on ('qudits', 0)
Matrix:
|
Measure on ('qudits', 0)
Parameters
|
We now define two linkers. The first linker maps the Pauli-X gate to a pulse executed on an AWG.
[3]:
# define the replacement in terms of a pulse and channel
pulse = qcs.RFWaveform(
duration=8e-8, envelope=qcs.GaussianEnvelope(), amplitude=1, rf_frequency=5e9
)
channel = qcs.Channels(range(1), "control_channel")
replacement_gt = qcs.Program()
replacement_gt.add_waveform(pulse, channel)
# specify the instruction to be replaced and create the Linker
linker_gt = qcs.Linker(qcs.GATES.x, qubit, replacement_gt)
The second linker maps the measurement instruction to an acquisition.
[4]:
replacement_meas = qcs.Program()
integration_filter = qcs.IntegrationFilter(pulse)
classifier = qcs.Classifier([0.0, 1.0])
replacement_meas.add_acquisition(integration_filter, channel, classifier)
linker_meas = qcs.ParameterizedLinker(qcs.Measure(), qubit, replacement_meas)
Finally, we use the Executor
to replace the
circuit-level instructions with the new instructions.
[5]:
program_out = qcs.Executor(qcs.LinkerPass(linker_gt, linker_meas)).execute(program_in)
By plotting program_out
, we can see that the circuit-level instructions have been
correctly replaced.
[6]:
program_out.draw()
Program
Program
|
|||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||||||||||||||||||||||
|
|
RFWaveform on ('control_channel', 0)
Parameters
|
Acquisition on ('control_channel', 0)
Parameters
|
Linking conditional operations
Rather than linking from a conditional operation to another conditional operation, we instead iterate through the possibile operations and link them all individually. Consider the case of active qubit reset.
[7]:
program = qcs.Program()
program.add_measurement(qubit, reset=True, repeat_reset=2)
program.draw()
Program
Program
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
Layer #1
Layer #1 (Data Transactions)
|
Layer #2
Layer #2
|
Layer #3
Layer #3 (Data Transactions)
|
Layer #4
Layer #4
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
Here, a conditional operation has been added after the measurement that will either perform a Pauli-X gate or the identity gate. To link this object properly, we add a linker for the identity gate in addition to the linker we already have.
[8]:
replacement_id = qcs.Program()
replacement_id.add_waveform(qcs.Delay(0), channel)
linker_id = qcs.Linker(qcs.GATES.id, qubit, replacement_id)
Applying the linkers, we see we get a new conditional operation except with a waveform and delay:
[9]:
program_out = qcs.LinkerPass(linker_gt, linker_id, linker_meas).apply(program)
program_out.draw()
Program
Program
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
Layer #1
Layer #1
|
Layer #2
Layer #2 (Data Transactions)
|
Layer #3
Layer #3
|
Layer #4
Layer #4
|
Layer #5
Layer #5 (Data Transactions)
|
Layer #6
Layer #6
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
Acquisition on ('control_channel', 0)
Parameters
|
ConditionalOperation on ('control_channel', 0)
Operations
Register
|
Acquisition on ('control_channel', 0)
Parameters
|
ConditionalOperation on ('control_channel', 0)
Operations
Register
|
Download
Download this file as Jupyter notebook: linker.ipynb.