"""Autogenerated module for the UHFQA QCoDeS driver."""
from typing import Union, Optional, List, Dict, Any, Tuple
import numpy as np
from zhinst.toolkit import CommandTable, Waveforms, Sequence
from zhinst.qcodes.driver.devices.base import ZIBaseInstrument
from zhinst.qcodes.qcodes_adaptions import ZINode, ZIChannelList
[docs]
class CommandTableNode(ZINode):
"""CommandTable node.
This class implements the basic functionality of the command table allowing
the user to load and upload their own command table.
A dedicated class called ``CommandTable`` exists that is the preferred way
to create a valid command table. For more information about the
``CommandTable`` refer to the corresponding example or the documentation
of that class directly.
Args:
root: Node used for the upload of the command table
tree: Tree (node path as tuple) of the current node
device_type: Device type.
"""
def __init__(self, parent, tk_object, snapshot_cache=None, zi_node=None):
ZINode.__init__(
self, parent, "commandtable", snapshot_cache=snapshot_cache, zi_node=zi_node
)
self._tk_object = tk_object
[docs]
def check_status(self) -> bool:
"""Check status of the command table.
Returns:
Flag if a valid command table is loaded into the device.
Raises:
RuntimeError: If the command table upload into the device failed.
"""
return self._tk_object.check_status()
[docs]
def load_validation_schema(self) -> Dict[str, Any]:
"""Load device command table validation schema.
Returns:
JSON validation schema for the device command tables.
"""
return self._tk_object.load_validation_schema()
[docs]
def upload_to_device(
self,
ct: Union[CommandTable, str, dict],
*,
validate: bool = False,
check_upload: bool = True,
) -> None:
"""Upload command table into the device.
The command table can either be specified through the dedicated
``CommandTable`` class or in a raw format, meaning a json string or json
dict. In the case of a json string or dict the command table is
validated by default against the schema provided by the device.
Args:
ct: Command table.
validate: Flag if the command table should be validated. (Only
applies if the command table is passed as a raw json string or
json dict)
check_upload: Flag if the upload should be validated by calling
`check_status`. This is not mandatory bat strongly recommended
since the device does not raise an error when it rejects the
command table. This Flag is ignored when called from within a
transaction.
Raises:
RuntimeError: If the command table upload into the device failed.
zhinst.toolkit.exceptions.ValidationError: Incorrect schema.
.. versionchanged:: 0.4.2
New Flag `check_upload` that makes the upload check optional.
`check_status` is only called when not in a ongoing transaction.
"""
return self._tk_object.upload_to_device(
ct=ct, validate=validate, check_upload=check_upload
)
[docs]
def load_from_device(self) -> CommandTable:
"""Load command table from the device.
Returns:
command table.
"""
return self._tk_object.load_from_device()
[docs]
class AWG(ZINode):
"""AWG node.
This class implements the basic functionality for the device specific
arbitrary waveform generator.
Besides the upload/compilation of sequences it offers the upload of
waveforms and command tables.
Args:
root: Root of the nodetree
tree: Tree (node path as tuple) of the current node
session: Underlying session.
serial: Serial of the device.
index: Index of the corresponding awg channel
device_type: Device type
"""
def __init__(self, parent, tk_object, index, snapshot_cache=None, zi_node=None):
ZINode.__init__(
self, parent, f"awg_{index}", snapshot_cache=snapshot_cache, zi_node=zi_node
)
self._tk_object = tk_object
if self._tk_object.commandtable:
self.add_submodule(
"commandtable",
CommandTableNode(
self,
self._tk_object.commandtable,
zi_node=self._tk_object.commandtable.node_info.path,
snapshot_cache=self._snapshot_cache,
),
)
[docs]
def enable_sequencer(self, *, single: bool) -> None:
"""Starts the sequencer of a specific channel.
Warning:
This function is synchronous and blocks until the sequencer is enabled.
When working with multiple instruments this function is the wrong
approach and the sequencer should be enabled asynchronously.
(For more information please take a look at the awg example in the
toolkit documentation.)
Args:
single: Flag if the sequencer should be disabled after finishing
execution.
Raises:
RuntimeError: If the sequencer could not be enabled.
.. versionchanged:: 0.5.0
Check the acknowledged value instead of using `wait_for_state_change`.
"""
return self._tk_object.enable_sequencer(single=single)
[docs]
def wait_done(self, *, timeout: float = 10, sleep_time: float = 0.005) -> None:
"""Wait until the AWG is finished.
Args:
timeout: The maximum waiting time in seconds for the generator
(default: 10).
sleep_time: Time in seconds to wait between requesting generator
state
Raises:
RuntimeError: If continuous mode is enabled
TimeoutError: If the sequencer program did not finish within
the specified timeout time
"""
return self._tk_object.wait_done(timeout=timeout, sleep_time=sleep_time)
[docs]
def compile_sequencer_program(
self, sequencer_program: Union[str, Sequence], **kwargs: Union[str, int]
) -> Tuple[bytes, Dict[str, Any]]:
"""Compiles a sequencer program for the specific device.
Args:
sequencer_program: The sequencer program to compile.
Keyword Args:
samplerate: Target sample rate of the sequencer. Only allowed/
necessary for HDAWG devices. Must correspond to the samplerate
used by the device (device.system.clocks.sampleclock.freq()).
If not specified the function will get the value itself from
the device. It is recommended passing the samplerate if more
than one sequencer code is uploaded in a row to speed up the
execution time.
wavepath: path to directory with waveforms. Defaults to path used
by LabOne UI or AWG Module.
waveforms: waveform CSV files separated by ';'
output: name of embedded ELF filename.
Returns:
elf: Binary ELF data for sequencer.
extra: Extra dictionary with compiler output.
Examples:
>>> elf, compile_info = device.awgs[0].compile_sequencer_program(seqc)
>>> device.awgs[0].elf.data(elf)
>>> device.awgs[0].ready.wait_for_state_change(1)
>>> device.awgs[0].enable(True)
Raises:
RuntimeError: `sequencer_program` is empty.
RuntimeError: If the compilation failed.
.. versionadded:: 0.4.0
"""
return self._tk_object.compile_sequencer_program(
sequencer_program=sequencer_program, **kwargs
)
[docs]
def load_sequencer_program(
self, sequencer_program: Union[str, Sequence], **kwargs: Union[str, int]
) -> Dict[str, Any]:
"""Compiles the given sequencer program on the AWG Core.
Warning:
After uploading the sequencer program one needs to wait before for
the awg core to become ready before it can be enabled.
The awg core indicates the ready state through its `ready` node.
(device.awgs[0].ready() == True)
Args:
sequencer_program: Sequencer program to be uploaded.
Keyword Args:
samplerate: Target sample rate of the sequencer. Only allowed/
necessary for HDAWG devices. Must correspond to the samplerate
used by the device (device.system.clocks.sampleclock.freq()).
If not specified the function will get the value itself from
the device. It is recommended passing the samplerate if more
than one sequencer code is uploaded in a row to speed up the
execution time.
wavepath: path to directory with waveforms. Defaults to path used
by LabOne UI or AWG Module.
waveforms: waveform CSV files separated by ';'
output: name of embedded ELF filename.
Examples:
>>> compile_info = device.awgs[0].load_sequencer_program(seqc)
>>> device.awgs[0].ready.wait_for_state_change(1)
>>> device.awgs[0].enable(True)
Raises:
RuntimeError: `sequencer_program` is empty.
RuntimeError: If the upload or compilation failed.
.. versionadded:: 0.3.4
`sequencer_program` does not accept empty strings
.. versionadded:: 0.4.0
Use offline compiler instead of AWG module to compile the sequencer
program. This speeds of the compilation and also enables parallel
compilation/upload.
"""
return self._tk_object.load_sequencer_program(
sequencer_program=sequencer_program, **kwargs
)
[docs]
class Integration(ZINode):
"""Integration part for the UHFQA.
Args:
root: Underlying node tree.
tree: tree (node path as tuple) of the corresponding node.
.. versionadded:: 0.3.2
"""
def __init__(self, parent, tk_object, snapshot_cache=None, zi_node=None):
ZINode.__init__(
self, parent, "integration", snapshot_cache=snapshot_cache, zi_node=zi_node
)
self._tk_object = tk_object
[docs]
def write_integration_weights(self, weights: Union[Waveforms, dict]) -> None:
"""Upload complex integration weights.
The weight functions are applied to the real and imaginary part of
the input signal. In the hardware the weights are implemented
as 17-bit integers.
Args:
weights: Dictionary containing the weight functions, where
keys correspond to the indices of the integration weights to be
configured.
Note:
Does not raise an error when sample limit is exceeded, but applies only
the maximum number of samples. Please refer to LabOne node documentation
for the number of maximum integration weight samples.
Note:
This function calls both `/qas/n/integration/weights/n/real` and
`/qas/n/integration/weights/n/imag` nodes.
If only real or imaginary part is defined, the number of defined samples
from the other one is zeroed.
"""
return self._tk_object.write_integration_weights(weights=weights)
[docs]
class QAS(ZINode):
"""Quantum Analyzer Channel for the UHFQA.
Args:
root: Underlying node tree.
tree: tree (node path as tuple) of the corresponding node.
"""
def __init__(self, parent, tk_object, index, snapshot_cache=None, zi_node=None):
ZINode.__init__(
self, parent, f"qas_{index}", snapshot_cache=snapshot_cache, zi_node=zi_node
)
self._tk_object = tk_object
if self._tk_object.integration:
self.add_submodule(
"integration",
Integration(
self,
self._tk_object.integration,
zi_node=self._tk_object.integration.node_info.path,
snapshot_cache=self._snapshot_cache,
),
)
[docs]
def crosstalk_matrix(self, matrix: np.ndarray = None) -> Optional[np.ndarray]:
"""Sets or gets the crosstalk matrix of the UHFQA as a 2D array.
Args:
matrix: The 2D matrix used in the digital signal
processing path to compensate for crosstalk between the
different channels. The given matrix can also be a part
of the entire 10 x 10 matrix. Its maximum dimensions
are 10 x 10 (default: None).
Returns:
If no argument is given the method returns the current
crosstalk matrix as a 2D numpy array.
Raises:
ValueError: If the matrix size exceeds the maximum size of
10 x 10
"""
return self._tk_object.crosstalk_matrix(matrix=matrix)
[docs]
def adjusted_delay(self, value: int = None) -> int:
"""Set or get the adjustment in the quantum analyzer delay.
Adjusts the delay that defines the time at which the integration starts
in relation to the trigger signal of the weighted integration units.
Depending if the deskew matrix is bypassed there exists a different
default delay. This function can be used to add an additional delay to
the default delay.
Args:
value: Number of additional samples to adjust the delay. If not
specified this function will just return the additional delay
currently set.
Returns:
The adjustment in delay in units of samples.
Raises:
ValueError: If the adjusted quantum analyzer delay is outside the
allowed range of 1021 samples.
"""
return self._tk_object.adjusted_delay(value=value)
[docs]
class UHFQA(ZIBaseInstrument):
"""QCoDeS driver for the Zurich Instruments UHFQA."""
def _init_additional_nodes(self):
"""Init class specific modules and parameters."""
if self._tk_object.awgs:
channel_list = ZIChannelList(
self,
"awgs",
AWG,
zi_node=self._tk_object.awgs.node_info.path,
snapshot_cache=self._snapshot_cache,
)
for i, x in enumerate(self._tk_object.awgs):
channel_list.append(
AWG(
self,
x,
i,
zi_node=self._tk_object.awgs[i].node_info.path,
snapshot_cache=self._snapshot_cache,
)
)
# channel_list.lock()
self.add_submodule("awgs", channel_list)
if self._tk_object.qas:
channel_list = ZIChannelList(
self,
"qas",
QAS,
zi_node=self._tk_object.qas.node_info.path,
snapshot_cache=self._snapshot_cache,
)
for i, x in enumerate(self._tk_object.qas):
channel_list.append(
QAS(
self,
x,
i,
zi_node=self._tk_object.qas[i].node_info.path,
snapshot_cache=self._snapshot_cache,
)
)
# channel_list.lock()
self.add_submodule("qas", channel_list)
[docs]
def enable_qccs_mode(self) -> None:
"""Configure the instrument to work with PQSC.
This method sets the reference clock source and DIO settings
correctly to connect the instrument to the PQSC.
Info:
Use ``factory_reset`` to reset the changes if necessary
"""
return self._tk_object.enable_qccs_mode()