Download
Download this file as Jupyter notebook: branching_logic.ipynb.
Real-time decision logic
Using the basic program structure as outlined in Program basics, we now show
how to add branching logic to a Program
using
a ConditionalOperation
.
There are two ways decision logic can be implemented:
Specifying a measurement in a program with
reset=True
, e.g.program.add_measurement(qubits, reset=True)
.Implementing an
Acquisition
that uses aClassifier
with aRegister
to which the results are written, followed by aConditionalOperation
that uses the outcome of the register to determine which operation to execute.
We begin by demonstrating the basic use case of qubit reset and then show how the reset operations can be customized.
Qubit reset
For quantum systems with long T1 times and no direct ground state initialization method, it is common practice to reset the qubit state to zero after a single execution of an experiment when it is not in that state. This requires a measurement and an optional subsequent reset operation.
In its simplest form, this can be done with an argument to the
add_measurement()
method:
[2]:
import keysight.qcs as qcs
# generate a simple quantum circuit on two qubits
qubits = qcs.Qudits(range(2))
circuit = qcs.Program()
circuit.add_gate(qcs.GATES.h, qubits[0])
circuit.add_gate(qcs.GATES.cx, (qubits[0], qubits[1]))
# add a measurement on both qubits with reset
circuit.add_measurement(qubits, reset=True)
circuit.draw()
Program
Program
|
||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
Layer #1
Layer #1
|
Layer #2
Layer #2
|
Layer #3
Layer #3 (Data Transactions)
|
Layer #4
Layer #4
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
H
Gate H on ('qudits', 0)
Matrix:
|
CX
Gate CX on (('qudits', 0), ('qudits', 1))
Matrix:
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
|||||||||||||||||||||||||||||||||||||||||||
|
CX
Gate CX on (('qudits', 1), ('qudits', 0))
Matrix:
|
Measure on ('qudits', 1)
Parameters
|
ConditionalOperation on ('qudits', 1)
Operations
Register
|
When setting reset=True
, a new empty layer (the data transaction
layer) gets inserted after the measurement layer, followed by another layer with the
conditional operation. The data transaction layer contains a list of
DataTransaction
objects that map
measurements to operations, as can be seen in the tooltip when hovering over the
layer column header.
If we hover over the conditional operations in the last layer, we can inspect which
operations are played depending on the outcome of the measurement.
Let’s take a closer look at those:
[3]:
# print the program layer that contains the conditional operation:
circuit[-1].operations
[3]:
{Qudits(labels=[0, 1], name=qudits, dim=2): [ConditionalOperation(operations=[Gate(matrix=[[(1+0j), 0j], [0j, (1+0j)]], name=ID), Gate(matrix=[[0j, (1+0j)], [(1+0j), 0j]], name=X)], register=Register(name=qudits_results, num_bits=2, dim=2))]}
This conditional operation will execute the first operation, the identity gate, if the
outcome of the measurement is 0
, and the second operation, an X gate, if the
outcome is 1
, thereby resetting the qubit state to zero after each
execution of the program. It is also possible to pass a list of operations to the
reset
argument whose length should match the dimension of the measurement:
[4]:
circuit = qcs.Program()
circuit.add_gate(qcs.GATES.h, qubits[0])
circuit.add_gate(qcs.GATES.cx, (qubits[0], qubits[1]))
# add a measurement on both qubits with a reset operation
# if the state is 0, play the identity
# if the state is 1, play a Y gate
circuit.add_measurement(qubits, reset=[qcs.GATES.id, qcs.GATES.y])
circuit.draw()
Program
Program
|
||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
Layer #1
Layer #1
|
Layer #2
Layer #2
|
Layer #3
Layer #3 (Data Transactions)
|
Layer #4
Layer #4
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
H
Gate H on ('qudits', 0)
Matrix:
|
CX
Gate CX on (('qudits', 0), ('qudits', 1))
Matrix:
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
|||||||||||||||||||||||||||||||||||||||||||
|
CX
Gate CX on (('qudits', 1), ('qudits', 0))
Matrix:
|
Measure on ('qudits', 1)
Parameters
|
ConditionalOperation on ('qudits', 1)
Operations
Register
|
We can repeat reset instruction multiple times by setting repeat_reset
:
[5]:
circuit = qcs.Program()
# add a reset operation at the start of the program and repeat it three times
circuit.add_measurement(qubits[0], reset=True, repeat_reset=3)
circuit.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
|
Layer #5
Layer #5 (Data Transactions)
|
Layer #6
Layer #6
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
Measure on ('qudits', 0)
Parameters
|
ConditionalOperation on ('qudits', 0)
Operations
Register
|
Defining conditional operations directly
Conditional operations are executed conditioned on an outcome stored in a
Register
.
This register is written to by a Classifier
specified on an Acquisition
.
Consider the following example program on a single channel:
[6]:
# define a classifier with a register
register = qcs.Register(name="results", num_outcomes=1)
classifier = qcs.Classifier([0, 0.1], register)
# define two channels for the readout waveform and acquisiton
awg = qcs.Channels(0, "awg")
dig = qcs.Channels(0, "dig")
# define the readout pulse and the integration filter
frequency = 5e9
readout_pulse = qcs.RFWaveform(30e-9, qcs.GaussianEnvelope(), 1, frequency)
integration_filter = qcs.RFWaveform(30e-9, qcs.ConstantEnvelope(), 1, 0)
program = qcs.Program()
program.add_waveform(readout_pulse, awg)
program.add_acquisition(integration_filter, dig, classifier)
program.draw()
Program
Program
|
||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||||||||
|
|
RFWaveform on ('awg', 0)
Parameters
|
||||||||||||||||||
|
|
Acquisition on ('dig', 0)
Parameters
|
This program now contains a single RF waveform played on the AWG channel and a simultaneous acquisition on a digitizer channel. We can now add the conditional operation based on the register that is connected to this acquisition:
[7]:
# first, define two waveforms to be played for either outcome (0 or 1)
waveform_0 = qcs.RFWaveform(30e-9, qcs.GaussianEnvelope(), 1, frequency)
waveform_1 = qcs.RFWaveform(30e-9, qcs.GaussianEnvelope(), 0.5, frequency)
# add the conditional operation using those waveforms and targeting the awg
# channel
program.add_conditional_operation(
operations=[waveform_0, waveform_1], target=awg, register=classifier.register
)
program.draw()
Program
Program
|
||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
Layer #1
Layer #1 (Data Transactions)
|
Layer #2
Layer #2
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
RFWaveform on ('awg', 0)
Parameters
|
ConditionalOperation on ('awg', 0)
Operations
Register
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|
Acquisition on ('dig', 0)
Parameters
|
This program contains a conditional operation with two waveforms that will be
played depending on the outcome stored in the register. If the outcome is 0
, the
waveform at index 0 of the operations
list will be played, and if it is
1
, the waveform at the index 1 will be played.
Download
Download this file as Jupyter notebook: branching_logic.ipynb.