Download
Download this file as Jupyter notebook: parallel_program.ipynb.
Running Programs in Parallel
The QCS is able to execute programs in parallel so long as they act on separate chassis.
To submit programs to run in parallel, users can submit from either one SDK client or
from two separate SDK clients. When executing from two separate clients, no additional
setup is required from the clients. The targeted QCS system will receive the program
submissions independently and manage the queues for each chassis accordingly. When
executing from one client, the user must set the blocking property of their
HclBackend
instance to False. This allows the
code to continue to a run second execution submission without being blocked by the
completion of the first program. See below for an example of this.
The QCS reserves hardware for execution on a per-chassis basis. When the system receives separate programs that each act on a single chassis in the system, it is able to execute those programs in parallel. Parallel execution of programs that act on multiple chassis is dependent on the synchronization topology between the chassis.
Submitting programs with blocking set to False
In this example we will be executing on a two chassis system. One program will use channels exclusive to chassis 1, and the other program will use channels exclusive to chassis 2.
First we will load the channel mapper that we will use for both programs
[2]:
import keysight.qcs as qcs
# set the following to True when connected to hardware:
run_on_hw = False
# instantiate channels representing two AWGs and two digitizers
# Index `0` of these virtual channels are mapped to channels on chassis 1
# Index `1` of these virtual channels are mapped to channels on chassis 2
awgs = qcs.Channels(range(2), "readoutawg")
digs = qcs.Channels(range(2), "readoutreceiver")
mapper = qcs.load("../../assets/channel_mapper.qcs")
[3]:
# create a program for the first chassis
program1 = qcs.Program()
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, 4.15e9)
program1.add_waveform(gauss, awgs[0])
program1.add_acquisition(80e-9, digs[0])
program1.n_shots(10)
Program
Program
|
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||
|
|
Acquisition on ('readoutreceiver', 0)
Parameters
|
[3]:
Program([Layer(Channels(labels=[0], name=readoutawg, absolute_phase=False)=[RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=_implicit, value=1.0, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4150000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))], Channels(labels=[0], name=readoutreceiver, absolute_phase=False)=[Acquisition(Scalar(name=_implicit, value=8e-08, dtype=float, unit=s))])])
[4]:
# create a program for the second chassis
program2 = qcs.Program()
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, 4.15e9)
program2.add_waveform(gauss, awgs[1])
program2.add_acquisition(80e-9, digs[1])
program2.n_shots(10)
Program
Program
|
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||
|
|
RFWaveform on ('readoutawg', 1)
Parameters
|
||||||||||||
|
|
Acquisition on ('readoutreceiver', 1)
Parameters
|
[4]:
Program([Layer(Channels(labels=[1], name=readoutawg, absolute_phase=False)=[RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=_implicit, value=1.0, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4150000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))], Channels(labels=[1], name=readoutreceiver, absolute_phase=False)=[Acquisition(Scalar(name=_implicit, value=8e-08, dtype=float, unit=s))])])
To execute these programs in parallel, we make sure to instantiate our
HclBackend
with the blocking property set
to False.
[5]:
if run_on_hw:
# initialize the backend pass
backend = qcs.HclBackend(channel_mapper=mapper, blocking=False)
# Submit the first program for execution
program1 = qcs.Executor(backend).execute(program1)
# Submit the secon program for execution before while the first program is running.
program2 = qcs.Executor(backend).execute(program2)
We can check the status of these programs with two methods.
get_program_state()
will return the
status of the program in string form (i.e. “Queued”, “Running”,
“CompletedInSuccess”).
is_program_completed()
will return a
boolean representing if the program is completed.
[6]:
if run_on_hw:
prog1_state = backend.get_program_state(program1.results.accession_id)
prog1_completed = backend.is_program_completed(program1.results.accession_id)
print(f"Program Chassis 1 state: {prog1_state}. Completed: {prog1_completed}")
prog2_state = backend.get_program_state(program2.results.accession_id)
prog2_completed = backend.is_program_completed(program2.results.accession_id)
print(f"Program Chassis 2 state: {prog2_state}. Completed: {prog2_completed}")
After the programs complete we can fetch results.
[7]:
if run_on_hw:
if prog1_completed:
trace_data1 = program1.get_trace()
if prog2_completed:
trace_data2 = program2.get_trace()
Download
Download this file as Jupyter notebook: parallel_program.ipynb.