diff --git a/qupulse/examples/VolatileParameters.py b/qupulse/examples/VolatileParameters.py new file mode 100644 index 000000000..53dcc3dab --- /dev/null +++ b/qupulse/examples/VolatileParameters.py @@ -0,0 +1,89 @@ +from qupulse.hardware.setup import HardwareSetup, PlaybackChannel, MarkerChannel +from qupulse.pulses import PointPT, RepetitionPT, TablePT + + +#%% +""" Connect and setup to your AWG. Change awg_address to the address of your awg and awg_name to the name of +your AWGs manufacturer (Zürich Instruments: ZI, TaborElectronics: Tabor). +""" + +awg_name = 'TABOR' +awg_address = '127.0.0.1' + +hardware_setup = HardwareSetup() + +if awg_name == 'ZI': + from qupulse.hardware.awgs.zihdawg import HDAWGRepresentation + awg = HDAWGRepresentation(awg_address, 'USB') + + channel_pairs = [] + for pair_name in ('AB', 'CD', 'EF', 'GH'): + channel_pair = getattr(awg, 'channel_pair_%s' % pair_name) + + for ch_i, ch_name in enumerate(pair_name): + playback_name = '{name}_{ch_name}'.format(name=awg_name, ch_name=ch_name) + hardware_setup.set_channel(playback_name, + PlaybackChannel(channel_pair, ch_i)) + hardware_setup.set_channel(playback_name + '_MARKER_FRONT', MarkerChannel(channel_pair, 2 * ch_i)) + hardware_setup.set_channel(playback_name + '_MARKER_BACK', MarkerChannel(channel_pair, 2 * ch_i + 1)) + awg_channel = awg.channel_pair_AB + +elif awg_name == 'TABOR': + from qupulse.hardware.awgs.tabor import TaborAWGRepresentation + awg = TaborAWGRepresentation(awg_address, reset=True) + + channel_pairs = [] + for pair_name in ('AB', 'CD'): + channel_pair = getattr(awg, 'channel_pair_%s' % pair_name) + channel_pairs.append(channel_pair) + + for ch_i, ch_name in enumerate(pair_name): + playback_name = '{name}_{ch_name}'.format(name=awg_name, ch_name=ch_name) + hardware_setup.set_channel(playback_name, PlaybackChannel(channel_pair, ch_i)) + hardware_setup.set_channel(playback_name + '_MARKER', MarkerChannel(channel_pair, ch_i)) + awg_channel = channel_pairs[0] + +else: + ValueError('Unknown AWG') + +#%% +""" Create three simple pulses and put them together to a PulseTemplate called dnp """ + +plus = [(0, 0), ('ta', 'va', 'hold'), ('tb', 'vb', 'linear'), ('tend', 0, 'jump')] +minus = [(0, 0), ('ta', '-va', 'hold'), ('tb', '-vb', 'linear'), ('tend', 0, 'jump')] + +zero_pulse = PointPT([(0, 0), ('tend', 0)], ('X', 'Y')) +plus_pulse = TablePT(entries={'X': plus, 'Y': plus}) +minus_pulse = TablePT(entries={'X': minus, 'Y': minus}) + +dnp = RepetitionPT(minus_pulse, 'n_minus') @ RepetitionPT(zero_pulse, 'n_zero') @ RepetitionPT(plus_pulse, 'n_plus') + +#%% +""" Create a program dnp with the number of pulse repetitions as volatile parameters """ + +sample_rate = awg_channel.sample_rate / 10**9 +n_quant = 192 +t_quant = n_quant / sample_rate + +dnp_prog = dnp.create_program(parameters=dict(tend=float(t_quant), ta=float(t_quant/3), tb=float(2*t_quant/3), + va=0.12, vb=0.25, n_minus=3, n_zero=3, n_plus=3), + channel_mapping={'X': '{}_A'.format(awg_name), 'Y': '{}_B'.format(awg_name)}, + volatile={'n_minus', 'n_zero', 'n_plus'}) +dnp_prog.cleanup() + +#%% +""" Upload this program to the AWG """ + +hardware_setup.register_program('dnp', dnp_prog) +hardware_setup.arm_program('dnp') + +#%% +""" Run initial program """ + +awg_channel.run_current_program() + +#%% +""" Change volatile parameters to new values and run the modified program """ + +hardware_setup.update_parameters('dnp', dict(n_zero=1, n_plus=5)) +awg_channel.run_current_program() diff --git a/qupulse/hardware/setup.py b/qupulse/hardware/setup.py index b49c479e0..bc2c76a8e 100644 --- a/qupulse/hardware/setup.py +++ b/qupulse/hardware/setup.py @@ -1,8 +1,8 @@ -from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any +from typing import NamedTuple, Set, Callable, Dict, Tuple, Union, Iterable, Any, Mapping from collections import defaultdict import warnings +import numbers -from qupulse.pulses.parameters import Parameter, ConstantParameter from qupulse.hardware.awgs.base import AWG from qupulse.hardware.dacs import DAC from qupulse._program._loop import Loop @@ -282,13 +282,9 @@ def rm_channel(self, identifier: ChannelID) -> None: def registered_channels(self) -> Dict[ChannelID, Set[_SingleChannel]]: return self._channel_map - def update_parameters(self, name: str, parameters): + def update_parameters(self, name: str, parameters: Mapping[str, numbers.Real]): *_, awgs, dacs = self._registered_programs[name] - for parameter_name, value in parameters.items(): - if not isinstance(value, Parameter): - parameters[parameter_name] = ConstantParameter(value) - for awg in self.known_awgs: if awg in awgs: awg.set_volatile_parameters(name, parameters) diff --git a/tests/hardware/setup_tests.py b/tests/hardware/setup_tests.py index a12ed1c70..d36467e23 100644 --- a/tests/hardware/setup_tests.py +++ b/tests/hardware/setup_tests.py @@ -1,4 +1,5 @@ import unittest +from unittest import mock import numpy as np @@ -363,6 +364,33 @@ def test_known_awgs(self) -> None: expected = {awg1, awg2} self.assertEqual(expected, setup.known_awgs) + def test_update_parameters(self) -> None: + setup = HardwareSetup() + awg1 = DummyAWG(num_channels=2, num_markers=0) + awg2 = DummyAWG(num_channels=1, num_markers=1) + dac1 = DummyDAC() + dac2 = DummyDAC() + setup.set_channel('A', PlaybackChannel(awg1, 0)) + setup.set_channel('B', PlaybackChannel(awg1, 1)) + setup.set_channel('C', PlaybackChannel(awg2, 0)) + setup.set_measurement('m1', MeasurementMask(dac1, 'DAC_1')) + setup.set_measurement('m2', MeasurementMask(dac2, 'DAC_2')) + loop1, loop2, _ = self.get_test_loops() + name1 = 'prog1' + name2 = 'prog2' + setup.register_program(name1, loop1) + setup.register_program(name2, loop2) + + parameters = dict(a=1, b=5) + + with mock.patch.object(DummyAWG, 'set_volatile_parameters') as setpara: + setup.update_parameters(name1, parameters) + setpara.assert_called_once_with(name1, parameters) + with mock.patch.object(DummyAWG, 'set_volatile_parameters') as setpara: + setup.update_parameters(name2, parameters) + assert setpara.call_count == 2 + setpara.assert_called_with(name2, parameters) + def test_clear_programs(self) -> None: setup = HardwareSetup() awg1 = DummyAWG(num_channels=2, num_markers=0) diff --git a/tests/hardware/tabor_dummy_based_tests.py b/tests/hardware/tabor_dummy_based_tests.py index ba5226f3c..554d489ac 100644 --- a/tests/hardware/tabor_dummy_based_tests.py +++ b/tests/hardware/tabor_dummy_based_tests.py @@ -11,7 +11,6 @@ from qupulse.hardware.awgs.base import AWGAmplitudeOffsetHandling from qupulse.hardware.awgs.tabor import TaborProgram, TaborAWGRepresentation, TaborProgramMemory from qupulse._program.tabor import TableDescription, TimeType, TableEntry -from qupulse.pulses.parameters import ConstantParameter from tests.hardware.dummy_modules import import_package diff --git a/tests/hardware/tabor_simulator_based_tests.py b/tests/hardware/tabor_simulator_based_tests.py index fd4bfdfdc..124a2914d 100644 --- a/tests/hardware/tabor_simulator_based_tests.py +++ b/tests/hardware/tabor_simulator_based_tests.py @@ -9,7 +9,6 @@ from qupulse.hardware.awgs.tabor import TaborAWGRepresentation, TaborChannelPair from qupulse._program.tabor import TaborSegment, PlottableProgram, TaborException, TableDescription, TableEntry -from qupulse.pulses.parameters import ConstantParameter from typing import List, Tuple, Optional, Any class TaborSimulatorManager: