Download

Download this file as Jupyter notebook: program_basics.ipynb.

Program basics

A Program contains a series of instructions to be performed on control hardware without requiring communication with the host computer.

Basic instructions and targets

The set of allowed instructions for a Program includes abstract quantum operations such as single- or multi-qudit gates as well as hardware operations such as RF waveforms or digitizer instructions for acquisitions. While it is possible two combine both abstraction layers within a single program, for the purposes of this tutorial we will treat them separately.

The instruction targets in a Program can either be Channels or Qudits. The former is intended for use in waveform programs while the latter is intended for abstract quantum programs. See Introduction to linkers for more information for how to translate between these layers of abstraction.

The following program contains two Gaussian pulses on two abstract AWG channels and two acquisition readouts on two digitizer channels, with the first readout using a constant acquisition window and the second one using an integrated filter.

[2]:
import keysight.qcs as qcs

# define channels representing two AWGs and two digitizers
awgs = qcs.Channels(range(2), "awgs")
digs = qcs.Channels(range(2), "digs")

# instantiate an empty program
program = qcs.Program()

# instantiate a pulse with two different amplitudes
amps = qcs.Array("amplitudes", value=[1, 0.8])
freqs = qcs.Array("frequencies", value=[1.0e8, 5.1e8])
gauss = qcs.RFWaveform(8e-8, qcs.GaussianEnvelope(), amps, freqs)
readout = qcs.RFWaveform(8e-8, qcs.GaussianEnvelope(2), 0.5, 4.8e8)
# add the pulses to the program
program.add_waveform(gauss, awgs)

# add an acquisition to the program
program.add_acquisition(readout, digs[0])
program.add_acquisition(8e-8, digs[1])
program.add_acquisition(readout, digs[1], pre_delay=5e-9)
# visualize the program using the draw method:
program.draw()
keysight-logo-svg
Program
Program
Duration 165 ns
Layers 1
Targets 4
Repetitions
Layer #0
Layer #0
Duration 165 ns
awgs 0
RFWaveform on ('awgs', 0)

Parameters
Duration 80 ns
Amplitude Array(name=amplitudes, shape=(2,), dtype=float, unit=none, value=[1, 0.8])
Frequency Array(name=frequencies, shape=(2,), dtype=float, unit=Hz, value=[100 MHz, 510 MHz])
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('awgs', 1)

Parameters
Duration 80 ns
Amplitude Array(name=amplitudes, shape=(2,), dtype=float, unit=none, value=[1, 0.8])
Frequency Array(name=frequencies, shape=(2,), dtype=float, unit=Hz, value=[100 MHz, 510 MHz])
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
digs 0
Acquisition on ('digs', 0)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 0.5
Frequency 480 MHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
Acquisition on ('digs', 1)

Parameters
Duration 80 ns
Delay on ('digs', 1)

Parameters
Duration 5 ns
Acquisition on ('digs', 1)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 0.5
Frequency 480 MHz
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad

Hover over the operations in this visualization to view their specifications.

In this program, the first channel in awgs plays the pulse with amplitude 0.8 and the second channel plays the pulse with amplitude 1.0. Meanwhile, the first channel in digs rendered with a constant gate and the second channel in digs rendered with an integration filter, with amplitude 1.0 and 0.5, respectively. This can be verified by visualizing the pulses using the render() method.

[3]:
program.render(channel_subplots=False, sample_rate=5e9)

Timing model

A Program consists of a series of Layers that represent instructions within a fixed time interval. When a program is constructed, new operations are added to the current layer if possible, otherwise a new Layer is created. The example below creates a program with the same instructions as before, but this time adds each to a new layer explicitly, causing them to be executed sequentially.

[4]:
program = qcs.Program()

# instantiate a pulse with two different amplitudes
gauss = qcs.RFWaveform(100e-9, qcs.GaussianEnvelope(), amps, freqs)

# add the pulses to the program using explicit indexing this time
program.add_waveform(gauss[0], awgs[0])

# create a new layer for the next pulse by setting new_layer=True
program.add_waveform(gauss[1], awgs[1], new_layer=True)

# add the acquisition
program.add_acquisition(100e-9, digs[0], new_layer=True)

program.draw()
keysight-logo-svg
Program
Program
Duration 300 ns
Layers 3
Targets 3
Repetitions
Layer #0
Layer #0
Duration 100 ns
Layer #1
Layer #1
Duration 100 ns
Layer #2
Layer #2
Duration 100 ns
awgs 0
RFWaveform on ('awgs', 0)

Parameters
Duration 100 ns
Amplitude ScalarRef(name=amplitudes, value=1, dtype=float, unit=none)
Frequency ScalarRef(name=frequencies, value=100 MHz, dtype=float, unit=Hz)
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('awgs', 1)

Parameters
Duration 100 ns
Amplitude ScalarRef(name=amplitudes, value=0.8, dtype=float, unit=none)
Frequency ScalarRef(name=frequencies, value=510 MHz, dtype=float, unit=Hz)
Envelope GaussianEnvelope(2.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
digs 0
Acquisition on ('digs', 0)

Parameters
Duration 100 ns

The instructions are now in separate layers. The duration of each layer is determined by the maximum duration of each operation in that layer, or, if there are multiple operations in each layer, the maximum of the sum of operation durations.

Quantum Programs

The Program class also supports circuit-level quantum programming where the targets are specified as Qudits and the operations are :Gates. Multi-qudit targets can be created using tuples of single-qudit targets.

[5]:
qudits1 = qcs.Qudits(range(2), "xy_qudits")
qudits2 = qcs.Qudits(2, "other")

prog = qcs.Program()

prog.add_gate(qcs.GATES.x, qudits1[0])
prog.add_gate(qcs.GATES.cx, (qudits1[0], qudits2[0]))
prog.add_gate(qcs.GATES.y, qudits2, new_layer=True)

prog.add_gate(qcs.GATES.cx, (qudits1[1], qudits2[0]))

prog.add_measurement(qudits1[1], new_layer=True)
prog.add_measurement(qudits2, new_layer=True)

prog.add_gate(qcs.GATES.x, qudits1[0])

prog.draw()
keysight-logo-svg
Program
Program
Duration undefined
Layers 6
Targets 3
Repetitions
Layer #0
Layer #0
Duration undefined
Layer #1
Layer #1
Duration undefined
Layer #2
Layer #2
Duration undefined
Layer #3
Layer #3
Duration undefined
Layer #4
Layer #4
Duration undefined
Layer #5
Layer #5
Duration undefined
xy_qudits 0
X
Gate X on ('xy_qudits', 0)

Matrix:
0 1
1 0
CX
Gate CX on (('xy_qudits', 0), ('other', 2))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
X
Gate X on ('xy_qudits', 0)

Matrix:
0 1
1 0
1
CX
Gate CX on (('xy_qudits', 1), ('other', 2))

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

Parameters
Dim 2
other 2
CX
Gate CX on (('other', 2), ('xy_qudits', 0))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Y
Gate Y on ('other', 2)

Matrix:
0 -1j
1j 0
CX
Gate CX on (('other', 2), ('xy_qudits', 1))

Matrix:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
Measure on ('other', 2)

Parameters
Dim 2

The available built-in gates can be viewed through the aliases attribute:

[6]:
qcs.GATES.aliases
[6]:
{'cx',
 'cy',
 'cz',
 'h',
 'id',
 'iswap',
 'swap',
 'x',
 'x90',
 'y',
 'y90',
 'z',
 'z90'}

Users can also specify their own gates with a matrix.


Download

Download this file as Jupyter notebook: program_basics.ipynb.

On this page