Skip to content

Commit

Permalink
Merge pull request #551 from qutech-lab/triton_200/hdawg_no_matlab_ch…
Browse files Browse the repository at this point in the history
…anges

Changes to hdawg driver from experiment
  • Loading branch information
terrorfisch committed Mar 3, 2021
2 parents 78be892 + 137e36e commit 7a139d2
Show file tree
Hide file tree
Showing 17 changed files with 928 additions and 329 deletions.
4 changes: 3 additions & 1 deletion MATLAB/+qc/daq_operations.m
Expand Up @@ -56,10 +56,12 @@
% --- get length --------------------------------------------------------
elseif strcmp(ctrl, 'get length') % output is length
% Operations need to have been added beforehand
mask_maker = py.getattr(daq, '_make_mask');
masks = util.py.py2mat(py.getattr(daq, '_registered_programs'));
masks = util.py.py2mat(masks.(a.program_name));
operations = masks.operations;
masks = util.py.py2mat(masks.masks);
masks = util.py.py2mat(masks.masks(mask_maker));


maskIdsFromOperations = cellfun(@(x)(char(x.maskID)), util.py.py2mat(operations), 'UniformOutput', false);
maskIdsFromMasks = cellfun(@(x)(char(x.identifier)), util.py.py2mat(masks), 'UniformOutput', false);
Expand Down
2 changes: 1 addition & 1 deletion MATLAB/+qc/load_pulse.m
@@ -1,4 +1,4 @@
function pulse = load_pulse(pulse_name)

global plsdata
pulse = plsdata.qc.serializer.deserialize(pulse_name);
pulse = plsdata.qc.pulse_storage{pulse_name};
26 changes: 16 additions & 10 deletions MATLAB/+qc/save_pulse.m
Expand Up @@ -8,17 +8,23 @@

file_written = false;

try
plsdata.qc.serializer.serialize(pyargs('serializable', pulse_template, 'overwrite', overwrite));
file_written = true;
if py.operator.contains(plsdata.qc.pulse_storage, pulse_template.identifier)
if overwrite
py.operator.delitem(plsdata.qc.pulse_storage, pulse_template.identifier);
else
warning('Did not write file as it exists and overwrite == false');
return;
end
end

try
plsdata.qc.pulse_storage{pulse_template.identifier} = pulse_template;
file_written = true;
% fprintf('File(s) written\n');
catch err
if util.str_contains(err.message, 'FileExistsError')
warning('%s\n', strrep(err.message, 'Python Error: ', ''));
else
warning(err.getReport());
end
end
catch err
warning(err.getReport());
end
end



428 changes: 343 additions & 85 deletions qupulse/_program/seqc.py

Large diffs are not rendered by default.

342 changes: 227 additions & 115 deletions qupulse/hardware/awgs/zihdawg.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion qupulse/hardware/dacs/alazar.py
Expand Up @@ -294,7 +294,7 @@ def delete_program(self, program_name: str) -> None:
# todo [2018-06-14]: what if program to delete is currently armed?

def clear(self) -> None:
self._registered_programs = dict()
self._registered_programs.clear()
self.__armed_program = None

@property
Expand Down
15 changes: 10 additions & 5 deletions qupulse/pulses/multi_channel_pulse_template.py
Expand Up @@ -229,22 +229,26 @@ def overwritten_channels(self) -> Mapping[str, ExpressionScalar]:
return self._overwritten_channels

def _get_overwritten_channels_values(self,
parameters: Mapping[str, Union[numbers.Real]]
parameters: Mapping[str, Union[numbers.Real]],
channel_mapping: Dict[ChannelID, Optional[ChannelID]]
) -> Dict[str, numbers.Real]:
return {name: value.evaluate_in_scope(parameters)
for name, value in self.overwritten_channels.items()}
return {channel_mapping[name]: value.evaluate_in_scope(parameters)
for name, value in self.overwritten_channels.items()
if channel_mapping[name] is not None}

def _internal_create_program(self, *,
scope: Scope,
global_transformation: Optional[Transformation],
channel_mapping: Dict[ChannelID, Optional[ChannelID]],
**kwargs):
overwritten_channels = self._get_overwritten_channels_values(parameters=scope)
overwritten_channels = self._get_overwritten_channels_values(parameters=scope, channel_mapping=channel_mapping)
transformation = ParallelConstantChannelTransformation(overwritten_channels)

if global_transformation is not None:
transformation = chain_transformations(global_transformation, transformation)

self._template._create_program(scope=scope,
channel_mapping=channel_mapping,
global_transformation=transformation,
**kwargs)

Expand All @@ -253,7 +257,8 @@ def build_waveform(self, parameters: Dict[str, numbers.Real],
inner_waveform = self._template.build_waveform(parameters, channel_mapping)

if inner_waveform:
overwritten_channels = self._get_overwritten_channels_values(parameters=parameters)
overwritten_channels = self._get_overwritten_channels_values(parameters=parameters,
channel_mapping=channel_mapping)
transformation = ParallelConstantChannelTransformation(overwritten_channels)
return TransformingWaveform(inner_waveform, transformation)

Expand Down
8 changes: 8 additions & 0 deletions qupulse/utils/__init__.py
Expand Up @@ -60,6 +60,14 @@ def pairwise(iterable: Iterable[Any],
return zip_function(a, b, **kwargs)


def grouper(iterable: Iterable[Any], n: int, fillvalue=None) -> Iterable[Tuple[Any, ...]]:
"""Collect data into fixed-length chunks or blocks"""
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
# this is here instead of using more_itertools because there were problems with the old version's argument order
args = [iter(iterable)] * n
return itertools.zip_longest(fillvalue=fillvalue, *args)


def replace_multiple(s: str, replacements: Mapping[str, str]) -> str:
"""Replace multiple strings at once. If multiple replacements overlap the precedence is given by the order in
replacements.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -36,7 +36,7 @@ def extract_version(version_file):
package_dir={'qupulse': 'qupulse', 'qctoolkit': 'qctoolkit'},
packages=packages,
python_requires='>=3.6',
install_requires=['sympy>=1.1.1', 'numpy', 'cached_property', 'more_itertools'],
install_requires=['sympy>=1.1.1', 'numpy', 'cached_property'],
extras_require={
'plotting': ['matplotlib'],
'VISA': ['pyvisa'],
Expand Down
66 changes: 47 additions & 19 deletions tests/_program/seqc_tests.py
@@ -1,8 +1,7 @@
import unittest
from unittest import TestCase, mock
import time
from more_itertools import take
from itertools import zip_longest
from itertools import zip_longest, islice
import sys
import tempfile
import pathlib
Expand All @@ -26,18 +25,23 @@
zhinst = None


def take(n, iterable):
"Return first n items of the iterable as a list"
return list(islice(iterable, n))


def make_binary_waveform(waveform):
if waveform.duration == 0:
data = np.asarray(3 * [1, 2, 3, 4, 5], dtype=np.uint16)
return BinaryWaveform(data)
return (BinaryWaveform(data),)
else:
chs = sorted(waveform.defined_channels)
t = np.arange(0., waveform.duration, 1.)

sampled = [None if ch is None else waveform.get_sampled(ch, t)
for _, ch in zip_longest(range(6), take(6, chs), fillvalue=None)]
ch1, ch2, *markers = sampled
return BinaryWaveform.from_sampled(ch1, ch2, markers)
return (BinaryWaveform.from_sampled(ch1, ch2, markers),)


def get_unique_wfs(n=10000, duration=32, defined_channels=frozenset(['A'])):
Expand Down Expand Up @@ -371,9 +375,9 @@ def test_to_node_clusters(self):
def dummy_loop_to_seqc(loop, **kwargs):
return loop

loops = [wf1, wf2, wf1, wf1, wf3, wf1, wf1, wf1]
loops = [wf1, wf2, wf1, wf1, wf3, wf1, wf1, wf1, wf3, wf1, wf3, wf1, wf3]
expected_calls = [mock.call(loop, **loop_to_seqc_kwargs) for loop in loops]
expected_result = [[wf1, wf2, wf1, wf1], [wf3], [wf1, wf1, wf1]]
expected_result = [[wf1, wf2, wf1, wf1], [wf3], [wf1, wf1, wf1], [Scope([wf3, wf1]), Scope([wf3, wf1])], [wf3]]

with mock.patch('qupulse._program.seqc.loop_to_seqc', wraps=dummy_loop_to_seqc) as mock_loop_to_seqc:
result = to_node_clusters(loops, loop_to_seqc_kwargs)
Expand Down Expand Up @@ -751,19 +755,23 @@ def test_full_run(self):

manager.add_program('test', root, channels, markers, amplitudes, offsets, volatage_transformations, sample_rate)

# 0: Program selection
# 1: Trigger
self.assertEqual({UserRegister(zero_based_value=2): 7}, manager.get_register_values('test'))
seqc_program = manager.to_seqc_program()
expected_program = """const PROG_SEL_REGISTER = 0;
const TRIGGER_REGISTER = 1;
const TRIGGER_RESET_MASK = 0b1000000000000000;
const TRIGGER_RESET_MASK = 0b10000000000000000000000000000000;
const PROG_SEL_NONE = 0;
const NO_RESET_MASK = 0b1000000000000000;
const PROG_SEL_MASK = 0b111111111111111;
const NO_RESET_MASK = 0b10000000000000000000000000000000;
const PLAYBACK_FINISHED_MASK = 0b1000000000000000000000000000000;
const PROG_SEL_MASK = 0b111111111111111111111111111111;
const INVERTED_PROG_SEL_MASK = 0b11000000000000000000000000000000;
const IDLE_WAIT_CYCLES = 300;
wave test_concatenated_waveform = "3e0090e8ffd002d1134ce38827c6a35fede89cf23d126a44057ef43f466ae4cd";
wave test_concatenated_waveform_0 = "3e0090e8ffd002d1134ce38827c6a35fede89cf23d126a44057ef43f466ae4cd";
wave test_shared_waveform_121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518 = "121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518";
//function used by manually triggered programs
// function used by manually triggered programs
void waitForSoftwareTrigger() {
while (true) {
var trigger_register = getUserReg(TRIGGER_REGISTER);
Expand All @@ -784,7 +792,7 @@ def test_full_run(self):
repeat(1000) { // stepping repeat
repeat(10) {
repeat(42) {
playWaveIndexed(test_concatenated_waveform, pos, 32); // advance disabled do to parent repetition
playWaveIndexed(test_concatenated_waveform_0, pos, 32); // advance disabled do to parent repetition
}
repeat(98) {
playWave(test_shared_waveform_121f5c6e8822793b3836fb3098fa4591b91d4c205cc2d8afd01ee1bf6956e518);
Expand All @@ -793,35 +801,55 @@ def test_full_run(self):
pos = pos + 32;
}
repeat(21) {
playWaveIndexed(test_concatenated_waveform, pos, 32); // advance disabled do to parent repetition
playWaveIndexed(test_concatenated_waveform_0, pos, 32); // advance disabled do to parent repetition
}
pos = pos + 32;
repeat(23) {
playWaveIndexed(test_concatenated_waveform, pos, 48); // advance disabled do to parent repetition
playWaveIndexed(test_concatenated_waveform_0, pos, 48); // advance disabled do to parent repetition
}
pos = pos + 48;
var idx_2;
for(idx_2 = 0; idx_2 < user_reg_2; idx_2 = idx_2 + 1) {
playWaveIndexed(test_concatenated_waveform, pos, 48); // advance disabled do to parent repetition
playWaveIndexed(test_concatenated_waveform_0, pos, 48); // advance disabled do to parent repetition
}
pos = pos + 48;
}
}
// INIT program switch.
// Declare and initialize global variables
// Selected program index (0 -> None)
var prog_sel = 0;
//runtime block
// Value that gets written back to program selection register.
// Used to signal that at least one program was played completely.
var new_prog_sel = 0;
// Is OR'ed to new_prog_sel.
// Set to PLAYBACK_FINISHED_MASK if a program was played completely.
var playback_finished = 0;
// runtime block
while (true) {
// read program selection value
prog_sel = getUserReg(PROG_SEL_REGISTER);
if (!(prog_sel & NO_RESET_MASK)) setUserReg(PROG_SEL_REGISTER, 0);
prog_sel = prog_sel & PROG_SEL_MASK;
// calculate value to write back to PROG_SEL_REGISTER
new_prog_sel = prog_sel | playback_finished;
if (!(prog_sel & NO_RESET_MASK)) new_prog_sel &= INVERTED_PROG_SEL_MASK;
setUserReg(PROG_SEL_REGISTER, new_prog_sel);
// reset playback flag
playback_finished = 0;
// only use part of prog sel that does not mean other things to select the program.
prog_sel &= PROG_SEL_MASK;
switch (prog_sel) {
case 1:
test_function();
waitWave();
playback_finished = PLAYBACK_FINISHED_MASK;
default:
wait(IDLE_WAIT_CYCLES);
}
Expand Down
@@ -0,0 +1,5 @@
{
"/{device_serial}/system/clocks/sampleclock/freq": 2e9,
"/{device_serial}/awgs/*/time": 0,
"/{device_serial}/sigouts/*/on": 0
}
75 changes: 75 additions & 0 deletions tests/backward_compatibility/hardware_test_helper.py
@@ -0,0 +1,75 @@
import unittest
import os
import json
import typing
import importlib.util
import sys

from qupulse.serialization import Serializer, FilesystemBackend, PulseStorage
from qupulse.pulses.pulse_template import PulseTemplate

class LoadingAndSequencingHelper:
def __init__(self, data_folder, pulse_name):
self.data_folder = data_folder
self.pulse_name = pulse_name

self.parameters = self.load_json('parameters.json')
self.window_mapping = self.load_json('measurement_mapping.json')
self.channel_mapping = self.load_json('channel_mapping.json')

self.validate_programs = self.load_function_from_file('binary_program_validation.py', 'validate_programs')
self.validation_data = self.load_json('binary_program_validation.json')

self.pulse = None
self.program = None

self.simulator_manager = None

self.hardware_setup = None # type: HardwareSetup
self.dac = None # type: DummyDAC

def load_json(self, file_name):
complete_file_name = os.path.join(self.data_folder, file_name)
if os.path.exists(complete_file_name):
with open(complete_file_name, 'r') as file_handle:
return json.load(file_handle)
else:
return None

def load_function_from_file(self, file_name, function_name):
full_file_name = os.path.join(self.data_folder, file_name)
if not os.path.exists(full_file_name):
return None
module_name = os.path.normpath(os.path.splitext(full_file_name)[0]).replace(os.sep, '.')

if module_name in sys.modules:
module = sys.modules[module_name]
else:
try:
spec = importlib.util.spec_from_file_location(module_name, full_file_name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
except ImportError:
return None
return getattr(module, function_name, None)

def deserialize_pulse(self):
serializer = Serializer(FilesystemBackend(os.path.join(self.data_folder, 'pulse_storage')))
self.pulse = typing.cast(PulseTemplate, serializer.deserialize(self.pulse_name))

def deserialize_pulse_2018(self) -> None:
pulse_storage = PulseStorage(FilesystemBackend(os.path.join(self.data_folder, 'pulse_storage_converted_2018')))
self.pulse = typing.cast(PulseTemplate, pulse_storage[self.pulse_name])

def sequence_pulse(self):
self.program = self.pulse.create_program(
parameters=self.parameters,
measurement_mapping=self.window_mapping,
channel_mapping=self.channel_mapping)

def register_program(self):
self.hardware_setup.register_program(self.pulse_name, self.program)

def arm_program(self):
self.hardware_setup.arm_program(self.pulse_name)

0 comments on commit 7a139d2

Please sign in to comment.