# Logical and Experimental Signals

LabOne Q abstracts the real, physical channels in your system into logical signal lines, which are grouped in logical signal groups. Logical signal lines abstract instruments, physical channels, and hardware calibration settings into a single Python object. This separates the hardware from the quantum experiments — which you can define freely, and whose experimental signal lines are mapped only later to the logical signal lines. This allows for instrumentation-agnostic experiment definitions, which can be re-used later.

In this chapter, we will explain the concept of logical signals, experimental signals, and how they are related to, but differ, from physical instrument lines. First, logical signals and how they are defined in the Descriptor are introduced. Then, it is shown how they are linked to experimental signals.

## Logical Signals

In a qubit experiment, you need to both control and measure your qubits. To control your qubits, you need a combination of drive lines and flux lines, and to measure, you need to send a pulse down a measurement line and evaluate the response in an acquire line. For our example experiment, we have four logical signal types: drive, flux, measure, and acquire, which form a logical signal group for each qubit.

 The signal type names, as well as the group names, are entirely arbitrary and determined by the user. However, the associated type of signal on the device is not arbitrary and will be either acquire_signal, rf_signal, or iq_signal, see details here.

Within a logical signal group, we can have more than one line of a single type, two drive lines, for example. Each group will have its own sets of signal lines, and they are programmatically distinct from one another. Logical signal lines retain this distinction even if the physical lines used are the same. For example, if we have our first qubit, q0, and our second qubit, q1, on the same physical multiplexed acquisition line, they will still have separate logical measure and acquire lines associated with them.

## Defining Logical Signals

Logical signal lines are defined in the Descriptor of the DeviceSetup object. Let’s dissect a more complex Descriptor example than was previously discussed to understand how we can use logical signal lines.

my_descriptor = """\
instrument_list:
HDAWG:
uid: device_hdawg
SHFQA:
uid: device_shfqa
SHFSG:
uid: device_shfsg
PQSC:
uid: device_pqsc
connections:
device_hdawg:
- iq_signal: q0/drive_line
ports: [SIGOUTS/0, SIGOUTS/1]
- rf_signal: q0/flux_line
ports: [SIGOUTS/4]
- rf_signal: q1/flux_line
ports: [SIGOUTS/6]
device_shfsg:
- iq_signal: q1/drive_line
ports: SGCHANNELS/0/OUTPUT
device_shfqa:
- iq_signal: q0/measure_line
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q0/acquire_line
ports: QACHANNELS/0/INPUT
- iq_signal: q1/measure_line
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q1/acquire_line
ports: QACHANNELS/0/INPUT
device_pqsc:
- to: device_hdawg
port: ZSYNCS/0
- to: device_shfsg
port: ZSYNCS/1
- to: device_shfqa
port: ZSYNCS/2
"""

In our Descriptor, we first listed the instruments used and specified their serial numbers. We also have given them a user-defined uid.

In the connections part of our Descriptor, for each uid, we defined the type of signal (acquire_signal, rf_signal, iq_signal, or to), the corresponding logical signal group and line, and the associated physical ports of the device.

For our HDAWG, we have three logical signal lines. Let’s take a look at the first one:

  device_hdawg:
- iq_signal: q0/drive_line
ports: [SIGOUTS/0, SIGOUTS/1]

This logical signal line is named drive_line. It is in the q0 logical signal group, and it has an iq_signal type. To specify both the I and Q components of the signal, we need two physical outputs of the HDAWG, given by the first (SIGOUTS/0) and second (SIGOUTS/1) wave outputs on the HDAWG.

The other two logical signal lines both have the same name: flux_line, but they are located in different logical signal groups, q0 and q1. Names of logical signal lines must only be unique if they are located in the same group. Both of these lines have the signal type rf_signal and, therefore, only need a single physical wave output:

  device_hdawg:
- rf_signal: q0/flux_line
ports: [SIGOUTS/4]
- rf_signal: q1/flux_line
ports: [SIGOUTS/6]

The next device, the SHFSG, is straight forward: The logical_signal q1/drive_line is mapped to the SHFSG’s first physical output channel:

  device_shfsg:
- iq_signal: q1/drive_line
ports: SGCHANNELS/0/OUTPUT

To measure our qubits, we need to specify some more logical signal lines, which we do for the next instrument in our Descriptor:

  device_shfqa:
- iq_signal: q0/measure_line
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q0/acquire_line
ports: QACHANNELS/0/INPUT
- iq_signal: q1/measure_line
ports: QACHANNELS/0/OUTPUT
- acquire_signal: q1/acquire_line
ports: QACHANNELS/0/INPUT

Here, we have four logical signal lines, two named measure_line, which are iq_signal types for sending qubit readout pulses, and two named acquire_line, for data acquisition on the input of SHFQA. Two of the measure and acquire lines are in the logical group q0 and the others are in q1. As both line pairs use the same physical channel on the instrument, the output signals are added, resulting in a frequency multiplexing. On the input side, each logical signal line has its own integration weights.

The PQSC has no logical signals associated with it.

## Experimental Signals

In every Experiment definition, the signals used are defined within the Experiment itself, with no reference to either logical signals or physical device channels. This allows experiments to be reused and adapted quickly and independently of the hardware itself. For a qubit spectroscopy experiment, the definition of the experiment might look like:

exp_qubit_spec = Experiment(
uid="Qubit Spectroscopy",
signals=[
ExperimentSignal("drive"),
ExperimentSignal("measure"),
ExperimentSignal("acquire"),
]
)

### Signal Maps

Within the experiment definition, all pulses of the sequence are played on ExperimentSignals. Later, these experimental signals must be mapped to logical signals, and it is easy to repeat the same Experiment by creating multiple maps with the same Experiment signals but different logical signal lines.

# signal map for qubit 0
q0_map = {"drive": "/logical_signal_groups/q0/drive_line",
"measure": "/logical_signal_groups/q0/measure_line",
"acquire": "/logical_signal_groups/q0/acquire_line",
}

# signal map for qubit 1
q1_map = {"drive": "/logical_signal_groups/q1/drive_line_ge",
"measure": "/logical_signal_groups/q1/measure_line",
"acquire": "/logical_signal_groups/q1/acquire_line",
}

Once a signal map is defined, setting the mapping and running your experiment is straightforward:

# apply qubit 0 signal map and run the experiment
exp_qubit_spec.set_signal_map(q0_map)
my_results_q0 = my_session.run(exp_qubit_spec)
# apply qubit 1 signal map and run the experiment
exp_qubit_spec.set_signal_map(q1_map)
my_results_q1 = my_session.run(exp_qubit_spec)
 The experiment definition is agnostic of the fact that the q0/drive signal is coming from an HDAWG and the q1/drive_ge signal is coming from an SHFSG.

By keeping instrumentation, logical signals, and experimental signals separate, it is easy to modify, reuse, and adapt your code for any hardware or qubit configuration, with very little to rewrite or modify.