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:

  1. Specifying a measurement in a program with reset=True, e.g. program.add_measurement(qubits, reset=True).

  2. Implementing an Acquisition that uses a Classifier with a Register to which the results are written, followed by a ConditionalOperation 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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 5
Targets 2
Repetitions
Layer #0
Layer #0
Duration undefined
Layer #1
Layer #1
Duration undefined
Layer #2
Layer #2
Duration undefined
Layer #3
Layer #3 (Data Transactions)
Duration 0 s
Transaction Source Measure on ('qudits', 0)
Transaction Destinations ('qudits', 0)
Layer #4
Layer #4
Duration undefined
qudits 0
H
Gate H on ('qudits', 0)

Matrix:
0.71 0.71
0.71 -0.71
CX
Gate CX on (('qudits', 0), ('qudits', 1))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Measure on ('qudits', 0)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=2, dim=2)
ConditionalOperation on ('qudits', 0)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate X

Matrix:
0 1
1 0

Register
Name qudits_results
Num Bits 2
Dim 2
1
CX
Gate CX on (('qudits', 1), ('qudits', 0))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Measure on ('qudits', 1)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=2, dim=2)
ConditionalOperation on ('qudits', 1)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate X

Matrix:
0 1
1 0

Register
Name qudits_results
Num Bits 2
Dim 2

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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 5
Targets 2
Repetitions
Layer #0
Layer #0
Duration undefined
Layer #1
Layer #1
Duration undefined
Layer #2
Layer #2
Duration undefined
Layer #3
Layer #3 (Data Transactions)
Duration 0 s
Transaction Source Measure on ('qudits', 0)
Transaction Destinations ('qudits', 0)
Layer #4
Layer #4
Duration undefined
qudits 0
H
Gate H on ('qudits', 0)

Matrix:
0.71 0.71
0.71 -0.71
CX
Gate CX on (('qudits', 0), ('qudits', 1))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Measure on ('qudits', 0)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=2, dim=2)
ConditionalOperation on ('qudits', 0)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate Y

Matrix:
0 -1j
1j 0

Register
Name qudits_results
Num Bits 2
Dim 2
1
CX
Gate CX on (('qudits', 1), ('qudits', 0))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Measure on ('qudits', 1)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=2, dim=2)
ConditionalOperation on ('qudits', 1)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate Y

Matrix:
0 -1j
1j 0

Register
Name qudits_results
Num Bits 2
Dim 2

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()
keysight-logo-svg
Program
Program
Duration undefined
Layers 7
Targets 1
Repetitions
Layer #0
Layer #0
Duration undefined
Layer #1
Layer #1 (Data Transactions)
Duration 0 s
Transaction Source Measure on ('qudits', 0)
Transaction Destinations ('qudits', 0)
Layer #2
Layer #2
Duration undefined
Layer #3
Layer #3 (Data Transactions)
Duration 0 s
Transaction Source Measure on ('qudits', 0)
Transaction Destinations ('qudits', 0)
Layer #4
Layer #4
Duration undefined
Layer #5
Layer #5 (Data Transactions)
Duration 0 s
Transaction Source Measure on ('qudits', 0)
Transaction Destinations ('qudits', 0)
Layer #6
Layer #6
Duration undefined
qudits 0
Measure on ('qudits', 0)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=1, dim=2)
ConditionalOperation on ('qudits', 0)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate X

Matrix:
0 1
1 0

Register
Name qudits_results
Num Bits 1
Dim 2
Measure on ('qudits', 0)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=1, dim=2)
ConditionalOperation on ('qudits', 0)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate X

Matrix:
0 1
1 0

Register
Name qudits_results
Num Bits 1
Dim 2
Measure on ('qudits', 0)

Parameters
Dim 2
Register Register(name=qudits_results, num_bits=1, dim=2)
ConditionalOperation on ('qudits', 0)

Operations
Outcome 0
Gate ID

Matrix:
1 0
0 1
Outcome 1
Gate X

Matrix:
0 1
1 0

Register
Name qudits_results
Num Bits 1
Dim 2

Defining conditional operations directly

Conditional operations are executed conditioned on an outcome stored in a Register. This register is written to by a Classifierspecified 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()
keysight-logo-svg
Program
Program
Duration 30 ns
Layers 1
Targets 2
Repetitions
Layer #0
Layer #0
Duration 30 ns
awg 0
RFWaveform on ('awg', 0)

Parameters
Duration 30 ns
Amplitude 1
Frequency 5 GHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
dig 0
Acquisition on ('dig', 0)

Parameters
Duration 30 ns
Integration Filter
RFWaveform

Parameters
Duration 30 ns
Amplitude 1
Frequency 0 Hz
Envelope ConstantEnvelope()
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=_implicit, shape=(2,), dtype=complex, unit=none))

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()
keysight-logo-svg
Program
Program
Duration 60 ns
Layers 3
Targets 2
Repetitions
Layer #0
Layer #0
Duration 30 ns
Layer #1
Layer #1 (Data Transactions)
Duration 0 s
Transaction Source Acquisition on ('dig', 0)
Transaction Destinations ('awg', 0)
Layer #2
Layer #2
Duration 30 ns
awg 0
RFWaveform on ('awg', 0)

Parameters
Duration 30 ns
Amplitude 1
Frequency 5 GHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
ConditionalOperation on ('awg', 0)

Operations
Outcome 0
RFWaveform

Parameters
Duration 30 ns
Amplitude 1
Frequency 5 GHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Outcome 1
RFWaveform

Parameters
Duration 30 ns
Amplitude 0.5
Frequency 5 GHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad

Register
Name results
Num Bits 1
Dim 2
dig 0
Acquisition on ('dig', 0)

Parameters
Duration 30 ns
Integration Filter
RFWaveform

Parameters
Duration 30 ns
Amplitude 1
Frequency 0 Hz
Envelope ConstantEnvelope()
Instantaneous Phase 0 rad
Post-phase 0 rad
Classifier Classifier(Array(name=_implicit, shape=(2,), dtype=complex, unit=none))

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.

On this page