diff --git a/qupulse/_program/_loop.py b/qupulse/_program/_loop.py index f8cf509c3..eebc11bd1 100644 --- a/qupulse/_program/_loop.py +++ b/qupulse/_program/_loop.py @@ -23,7 +23,10 @@ class Loop(Node): MAX_REPR_SIZE = 2000 __slots__ = ('_waveform', '_measurements', '_repetition_count', '_cached_body_duration', '_repetition_parameter') - """Build a loop tree. The leaves of the tree are loops with one element.""" + """Build a loop tree. The leaves of the tree are loops with one element. + + Loop objects are equal if all children are/the waveform is equal, the repetition count is equal + """ def __init__(self, parent: Union['Loop', None] = None, children: Iterable['Loop'] = (), @@ -39,7 +42,7 @@ def __init__(self, waveform: measurements: repetition_count: - repetition_expression: + repetition_parameter: """ super().__init__(parent=parent, children=children) @@ -49,16 +52,19 @@ def __init__(self, self._repetition_parameter = repetition_parameter self._cached_body_duration = None - if abs(self._repetition_count - repetition_count) > 1e-10: - raise ValueError('Repetition count was not an integer') - - if not isinstance(waveform, (type(None), Waveform)): - raise Exception() - - @property - def compare_key(self) -> Tuple: - return self._waveform, self.repetition_count, self._measurements if self._measurements else None,\ - super().compare_key + assert self._repetition_count == repetition_count, "Repetition count was not an integer: %r" % repetition_count + assert isinstance(waveform, (type(None), Waveform)) + + def __eq__(self, other: 'Loop') -> bool: + if type(self) == type(other): + return (self._repetition_count == other._repetition_count and + self.waveform == other.waveform and + (self._measurements or None) == (other._measurements or None) and + self._repetition_parameter == other._repetition_parameter and + len(self) == len(other) and + all(self_child == other_child for self_child, other_child in zip(self, other))) + else: + return NotImplemented def append_child(self, loop: Optional['Loop']=None, **kwargs) -> None: # do not invalidate but update cached duration @@ -118,7 +124,7 @@ def body_duration(self) -> TimeType: @property def duration(self) -> TimeType: - return self.repetition_count*self.body_duration + return self.body_duration * self.repetition_count @property def repetition_parameter(self) -> Optional[MappedParameter]: @@ -138,6 +144,8 @@ def repetition_count(self, val) -> None: def unroll(self) -> None: if self.is_leaf(): raise RuntimeError('Leaves cannot be unrolled') + if self.repetition_parameter is not None: + warnings.warn("Unrolling a Loop with volatile repetition count", VolatileModificationWarning) i = self.parent_index self.parent[i:i+1] = (child.copy_tree_structure(new_parent=self.parent) @@ -150,19 +158,24 @@ def __setitem__(self, idx, value): self._invalidate_duration() def unroll_children(self) -> None: + if self._repetition_parameter is not None: + warnings.warn("Unrolling a Loop with volatile repetition count", VolatileModificationWarning) old_children = self.children self[:] = (child.copy_tree_structure() for _ in range(self.repetition_count) for child in old_children) self.repetition_count = 1 + self._repetition_parameter = None self.assert_tree_integrity() def encapsulate(self) -> None: self[:] = [Loop(children=self, repetition_count=self.repetition_count, + repetition_parameter=self._repetition_parameter, waveform=self._waveform, measurements=self._measurements)] self.repetition_count = 1 + self._repetition_parameter = None self._waveform = None self._measurements = None self.assert_tree_integrity() @@ -197,6 +210,7 @@ def copy_tree_structure(self, new_parent: Union['Loop', bool]=False) -> 'Loop': return type(self)(parent=self.parent if new_parent is False else new_parent, waveform=self._waveform, repetition_count=self.repetition_count, + repetition_parameter=self._repetition_parameter, measurements=self._measurements, children=(child.copy_tree_structure() for child in self)) @@ -243,18 +257,30 @@ def get_measurement_windows(self) -> Dict[str, Tuple[np.ndarray, np.ndarray]]: def split_one_child(self, child_index=None) -> None: """Take the last child that has a repetition count larger one, decrease it's repetition count and insert a copy with repetition cout one after it""" - if child_index: + if child_index is not None: if self[child_index].repetition_count < 2: raise ValueError('Cannot split child {} as the repetition count is not larger 1') + else: - try: - child_index = next(i for i in reversed(range(len(self))) - if self[i].repetition_count > 1) - except StopIteration: - raise RuntimeError('There is no child with repetition count > 1') + for i, child in enumerate(reversed(self)): + if child.repetition_count > 1: + if child.repetition_parameter is None: + child_index = i + break + elif child_index is None: + child_index = i + else: + if child_index is None: + raise RuntimeError('There is no child with repetition count > 1') + + if self[child_index]._repetition_parameter is not None: + warnings.warn("Splitting a child with volatile repetition count", VolatileModificationWarning) + self[child_index]._repetition_parameter = MappedParameter(expression=self[child_index]._repetition_parameter.expression - 1, + dependencies=self[child_index]._repetition_parameter.dependencies) new_child = self[child_index].copy_tree_structure() new_child.repetition_count = 1 + new_child._repetition_parameter = None self[child_index].repetition_count -= 1 @@ -281,12 +307,30 @@ def flatten_and_balance(self, depth: int) -> None: elif sub_program.depth() == depth - 1: i += 1 - elif len(sub_program) == 1 and len(sub_program[0]) == 1: + elif len(sub_program) == 1 and len(sub_program[0]) == 1 and not sub_program._measurements: sub_sub_program = cast(Loop, sub_program[0]) - sub_program.repetition_count = sub_program.repetition_count * sub_sub_program.repetition_count + measurements = sub_sub_program._measurements + repetition_count = sub_program.repetition_count * sub_sub_program.repetition_count + if sub_program._repetition_parameter is None and sub_sub_program._repetition_parameter is None: + repetition_parameter = None + else: + if sub_program._repetition_parameter is None: + repetition_parameter = MappedParameter(expression=sub_sub_program._repetition_parameter.expression * sub_program.repetition_count, + dependencies=sub_sub_program._repetition_parameter.dependencies) + elif sub_sub_program._repetition_parameter is None: + repetition_parameter = MappedParameter(expression=sub_program._repetition_parameter.expression * sub_sub_program.repetition_count, + dependencies=sub_program._repetition_parameter.dependencies) + else: + # TODO: possible but requires complicated code elsewhere + repetition_parameter = None + sub_program[:] = sub_sub_program[:] - sub_program.waveform = sub_sub_program.waveform + sub_program._waveform = sub_sub_program._waveform + sub_program._repetition_parameter = repetition_parameter + sub_program._repetition_count = repetition_count + sub_program._measurements = measurements + sub_program._invalidate_duration() elif not sub_program.is_leaf(): sub_program.unroll() @@ -332,7 +376,7 @@ def cleanup(self): elif child._measurements: warnings.warn("Dropping measurement since there is no waveform in children") - if len(new_children) == 1 and not self._measurements: + if len(new_children) == 1 and not self._measurements and not self._repetition_parameter: assert not self._waveform only_child = new_children[0] @@ -344,7 +388,7 @@ def cleanup(self): elif len(self) != len(new_children): self[:] = new_children - def get_duration_structure(self) -> Tuple[int, Union[int, tuple]]: + def get_duration_structure(self) -> Tuple[int, Union[TimeType, tuple]]: if self.is_leaf(): return self.repetition_count, self.waveform.duration else: diff --git a/qupulse/comparable.py b/qupulse/comparable.py index d8ce1303f..2582faa8b 100644 --- a/qupulse/comparable.py +++ b/qupulse/comparable.py @@ -1,6 +1,6 @@ """This module defines the abstract Comparable class.""" from abc import abstractmethod -from typing import Any +from typing import Hashable, Any from qupulse.utils.types import DocStringABCMeta @@ -20,7 +20,7 @@ class Comparable(metaclass=DocStringABCMeta): @property @abstractmethod - def compare_key(self) -> Any: + def compare_key(self) -> Hashable: """Return a unique key used in comparison and hashing operations. The key must describe the essential properties of the object. diff --git a/qupulse/pulses/loop_pulse_template.py b/qupulse/pulses/loop_pulse_template.py index 992b3d20a..e60db036b 100644 --- a/qupulse/pulses/loop_pulse_template.py +++ b/qupulse/pulses/loop_pulse_template.py @@ -235,7 +235,8 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional['Transformation'], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: self.validate_parameter_constraints(parameters=parameters) try: @@ -245,6 +246,8 @@ def _internal_create_program(self, *, for parameter_name in self.duration.variables} except KeyError as e: raise ParameterNotProvidedException(str(e)) from e + assert not volatile.intersection(measurement_parameters.keys()) + assert not volatile.intersection(duration_parameters.keys()) if self.duration.evaluate_numeric(**duration_parameters) > 0: measurements = self.get_measurement_windows(measurement_parameters, measurement_mapping) @@ -257,7 +260,8 @@ def _internal_create_program(self, *, channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, + volatile=volatile) def build_waveform(self, parameters: Dict[str, Parameter]) -> ForLoopWaveform: return ForLoopWaveform([self.body.build_waveform(local_parameters) diff --git a/qupulse/pulses/mapping_pulse_template.py b/qupulse/pulses/mapping_pulse_template.py index 4e28061aa..9a9edc31f 100644 --- a/qupulse/pulses/mapping_pulse_template.py +++ b/qupulse/pulses/mapping_pulse_template.py @@ -298,6 +298,26 @@ def map_parameters(self, else: raise TypeError('Values of parameter dict are neither all Parameter nor Real') + def map_volatile(self, volatile: Set[str]) -> Set[str]: + """Deduce set of inner volatile parameters. + + TODO: Does not handle the case of dropped dependencies i.e.: + x is volatile but a == 0 => y is actually not volatile + y = a * x + m + + Args: + volatile: a set of outer volatile parameters + + Returns: + Set of inner volatile parameters + """ + if volatile: + return {parameter + for parameter, mapping_function in self.__parameter_mapping.items() + if volatile.intersection(mapping_function.variables)} + else: + return volatile + def get_updated_measurement_mapping(self, measurement_mapping: Dict[str, str]) -> Dict[str, str]: return {k: measurement_mapping[v] for k, v in self.__measurement_mapping.items()} @@ -328,14 +348,16 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional['Transformation'], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: # parameters are validated in map_parameters() call, no need to do it here again explicitly self.template._create_program(parameters=self.map_parameter_objects(parameters), measurement_mapping=self.get_updated_measurement_mapping(measurement_mapping), channel_mapping=self.get_updated_channel_mapping(channel_mapping), global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, + volatile=self.map_volatile(volatile)) def build_waveform(self, parameters: Dict[str, numbers.Real], diff --git a/qupulse/pulses/parameters.py b/qupulse/pulses/parameters.py index f4fbbc94c..3faaca164 100644 --- a/qupulse/pulses/parameters.py +++ b/qupulse/pulses/parameters.py @@ -9,8 +9,9 @@ """ from abc import abstractmethod -from typing import Optional, Union, Dict, Any, Iterable, Set, List +from typing import Optional, Union, Dict, Any, Iterable, Set, List, Mapping from numbers import Real +import types import sympy import numpy @@ -47,12 +48,8 @@ def requires_stop(self) -> bool: True, if evaluating this Parameter instance requires an interruption. """ - @abstractmethod - def __hash__(self) -> int: - """Returns a hash value of the parameter. Must be implemented.""" - - def __eq__(self, other) -> bool: - return type(self) is type(other) and hash(self) == hash(other) + def __eq__(self, other: 'Parameter') -> bool: + return numpy.array_equal(self.get_value(), other.get_value()) class ConstantParameter(Parameter): @@ -103,7 +100,7 @@ class MappedParameter(Parameter): def __init__(self, expression: Expression, - dependencies: Optional[Dict[str, Parameter]]=None) -> None: + dependencies: Optional[Mapping[str, Parameter]]=None) -> None: """Create a MappedParameter instance. Args: @@ -114,8 +111,17 @@ def __init__(self, """ super().__init__() self._expression = expression - self.dependencies = dict() if dependencies is None else dependencies - self._cached_value = (None, None) + self._dependencies = dict() if dependencies is None else dependencies + self._cached_value = None + + @property + def dependencies(self): + return types.MappingProxyType(self._dependencies) + + @dependencies.setter + def dependencies(self, new_dependencies): + self._dependencies = new_dependencies + self._cached_value = None def _collect_dependencies(self) -> Dict[str, float]: # filter only real dependencies from the dependencies dictionary @@ -127,13 +133,13 @@ def _collect_dependencies(self) -> Dict[str, float]: def get_value(self) -> Union[Real, numpy.ndarray]: """Does not check explicitly if a parameter requires to stop.""" - current_hash = hash(self) - if current_hash != self._cached_value[0]: - self._cached_value = (current_hash, self._expression.evaluate_numeric(**self._collect_dependencies())) - return self._cached_value[1] + if self._cached_value is None: + self._cached_value = self._expression.evaluate_numeric(**self._collect_dependencies()) + return self._cached_value - def __hash__(self): - return hash(tuple(self.dependencies.items())) + @property + def expression(self): + return self._expression @property def requires_stop(self) -> bool: diff --git a/qupulse/pulses/pulse_template.py b/qupulse/pulses/pulse_template.py index 3516f97d3..1ef46dd1e 100644 --- a/qupulse/pulses/pulse_template.py +++ b/qupulse/pulses/pulse_template.py @@ -224,7 +224,8 @@ def _create_program(self, *, channel_mapping=channel_mapping, to_single_waveform=to_single_waveform, global_transformation=global_transformation, - parent_loop=parent_loop) + parent_loop=parent_loop, + volatile=volatile) class AtomicPulseTemplate(PulseTemplate, MeasurementDefiner): @@ -271,7 +272,8 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional[Transformation], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: """Parameter constraints are validated in build_waveform because build_waveform is guaranteed to be called during sequencing""" ### current behavior (same as previously): only adds EXEC Loop and measurements if a waveform exists. @@ -285,6 +287,7 @@ def _internal_create_program(self, *, except KeyError as e: raise ParameterNotProvidedException(str(e)) from e + assert not volatile.intersection(parameters), "not supported" waveform = self.build_waveform(parameters=parameters, channel_mapping=channel_mapping) if waveform: diff --git a/qupulse/pulses/repetition_pulse_template.py b/qupulse/pulses/repetition_pulse_template.py index a8994967f..494d0fa82 100644 --- a/qupulse/pulses/repetition_pulse_template.py +++ b/qupulse/pulses/repetition_pulse_template.py @@ -18,7 +18,7 @@ from qupulse.pulses.sequencing import Sequencer from qupulse._program.instructions import InstructionBlock, InstructionPointer from qupulse._program.waveforms import RepetitionWaveform -from qupulse.pulses.parameters import Parameter, ParameterConstrainer, ParameterNotProvidedException +from qupulse.pulses.parameters import Parameter, ParameterConstrainer, ParameterNotProvidedException, MappedParameter from qupulse.pulses.conditions import Condition from qupulse.pulses.measurement import MeasurementDefiner, MeasurementDeclaration @@ -134,7 +134,8 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional['Transformation'], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: self.validate_parameter_constraints(parameters=parameters) relevant_params = set(self._repetition_count.variables).union(self.measurement_parameters) try: @@ -147,13 +148,22 @@ def _internal_create_program(self, *, # todo (2018-07-19): could in some circumstances possibly just multiply subprogram repetition count? # could be tricky if any repetition count is volatile ? check later and optimize if necessary if repetition_count > 0: - repj_loop = Loop(repetition_count=repetition_count) + if volatile.intersection(self.repetition_count.variables): + repetition_expression = self.repetition_count.evaluate_symbolic( + {parameter: value for parameter, value in real_parameters.items() + if parameter not in volatile}) + repetition_parameter = MappedParameter(repetition_expression, parameters) + else: + repetition_parameter = None + + repj_loop = Loop(repetition_count=repetition_count, repetition_parameter=repetition_parameter) self.body._create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=repj_loop) + parent_loop=repj_loop, + volatile=volatile) if repj_loop.waveform is not None or len(repj_loop.children) > 0: measurements = self.get_measurement_windows(real_parameters, measurement_mapping) if measurements: diff --git a/qupulse/pulses/sequence_pulse_template.py b/qupulse/pulses/sequence_pulse_template.py index 1cee16e7b..4048773aa 100644 --- a/qupulse/pulses/sequence_pulse_template.py +++ b/qupulse/pulses/sequence_pulse_template.py @@ -172,7 +172,8 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional['Transformation'], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: self.validate_parameter_constraints(parameters=parameters) try: @@ -180,6 +181,8 @@ def _internal_create_program(self, *, for parameter_name in self.measurement_parameters} duration_parameters = {parameter_name: parameters[parameter_name].get_value() for parameter_name in self.duration.variables} + assert not volatile.intersection(measurement_mapping), "not supported" + assert not volatile.intersection(duration_parameters), "not supported" except KeyError as e: raise ParameterNotProvidedException(e) from e @@ -194,7 +197,8 @@ def _internal_create_program(self, *, channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, + volatile=volatile) def get_serialization_data(self, serializer: Optional[Serializer]=None) -> Dict[str, Any]: data = super().get_serialization_data(serializer) diff --git a/qupulse/utils/tree.py b/qupulse/utils/tree.py index 4c11cdbc8..d8cdc780a 100644 --- a/qupulse/utils/tree.py +++ b/qupulse/utils/tree.py @@ -1,9 +1,9 @@ -from typing import Iterable, Union, List, Generator, Tuple, TypeVar, Optional +from typing import Iterable, Union, List, Generator, Tuple, TypeVar, Optional, Sequence from collections import deque, namedtuple from copy import copy as shallow_copy import weakref -from qupulse.comparable import Comparable +from qupulse.utils.types import SequenceProxy __all__ = ['Node'] @@ -16,7 +16,7 @@ def make_empty_weak_reference() -> weakref.ref: _NodeType = TypeVar('_NodeType', bound='Node') -class Node(Comparable): +class Node: debug = False __slots__ = ('__parent', '__children', '__parent_index', '__weakref__') @@ -53,6 +53,9 @@ def is_balanced(self) -> bool: def __iter__(self: _NodeType) -> Iterable[_NodeType]: return iter(self.__children) + def __reversed__(self: _NodeType) -> Iterable[_NodeType]: + return reversed(self.__children) + def __setitem__(self: _NodeType, idx: Union[int, slice], value: Union[_NodeType, Iterable[_NodeType]]): if isinstance(idx, slice): if isinstance(value, Node): @@ -115,11 +118,11 @@ def assert_tree_integrity(self) -> None: raise AssertionError('Parent is missing child reference') @property - def children(self: _NodeType) -> List[_NodeType]: + def children(self: _NodeType) -> Sequence[_NodeType]: """ :return: shallow copy of children """ - return shallow_copy(self.__children) + return SequenceProxy(self.__children) @property def parent(self: _NodeType) -> Union[None, _NodeType]: @@ -148,10 +151,6 @@ def locate(self: _NodeType, location: Tuple[int, ...]) -> _NodeType: else: return self - @property - def compare_key(self) -> List: - return self.__children - def is_tree_circular(root: Node) -> Union[None, Tuple[List[Node], int]]: NodeStack = namedtuple('NodeStack', ['node', 'stack']) diff --git a/qupulse/utils/types.py b/qupulse/utils/types.py index 4cbf93486..fcab95aa3 100644 --- a/qupulse/utils/types.py +++ b/qupulse/utils/types.py @@ -10,7 +10,7 @@ import numpy __all__ = ["MeasurementWindow", "ChannelID", "HashableNumpyArray", "TimeType", "time_from_float", "DocStringABCMeta", - "SingletonABCMeta"] + "SingletonABCMeta", "SequenceProxy"] MeasurementWindow = typing.Tuple[str, numbers.Real, numbers.Real] ChannelID = typing.Union[str, int] @@ -335,3 +335,39 @@ class Collection(typing.Sized, typing.Iterable[typing.T_co], typing.Container[ty """Fallback for typing.Collection if python 3.5 copied from https://github.com/python/cpython/blob/3.5/Lib/typing.py""" __slots__ = () + + +class SequenceProxy(collections.abc.Sequence): + __slots__ = ('_inner',) + + def __init__(self, inner: typing.Sequence): + self._inner = inner + + def __getitem__(self, item): + return self._inner.__getitem__(item) + + def __iter__(self): + return self._inner.__iter__() + + def __len__(self): + return self._inner.__len__() + + def __contains__(self, item): + return self._inner.__contains__(item) + + def __reversed__(self): + return self._inner.__reversed__() + + def index(self, i, **kwargs): + return self._inner.index(i, **kwargs) + + def count(self, elem): + return self._inner.count(elem) + + def __eq__(self, other): + """Not part of Sequence interface""" + if type(other) is SequenceProxy: + return (len(self) == len(other) + and all(x == y for x, y in zip(self, other))) + else: + return NotImplemented diff --git a/tests/_program/loop_tests.py b/tests/_program/loop_tests.py index f53083ec4..a2a6bbd57 100644 --- a/tests/_program/loop_tests.py +++ b/tests/_program/loop_tests.py @@ -615,7 +615,7 @@ def test_make_compatible_partial_unroll(self): _make_compatible(program, min_len=5, quantum=1, sample_rate=TimeType.from_float(1.)) self.assertIsInstance(program.waveform, SequenceWaveform) - self.assertEqual(program.children, []) + self.assertEqual(list(program.children), []) self.assertEqual(program.repetition_count, 2) self.assertEqual(len(program.waveform._sequenced_waveforms), 2) @@ -634,7 +634,7 @@ def test_make_compatible_complete_unroll(self): _make_compatible(program, min_len=5, quantum=10, sample_rate=TimeType.from_float(1.)) self.assertIsInstance(program.waveform, RepetitionWaveform) - self.assertEqual(program.children, []) + self.assertEqual(list(program.children), []) self.assertEqual(program.repetition_count, 1) self.assertIsInstance(program.waveform, RepetitionWaveform) diff --git a/tests/pulses/loop_pulse_template_tests.py b/tests/pulses/loop_pulse_template_tests.py index ba628f68f..bbbe02624 100644 --- a/tests/pulses/loop_pulse_template_tests.py +++ b/tests/pulses/loop_pulse_template_tests.py @@ -199,8 +199,8 @@ def test_create_program_constraint_on_loop_var_exception(self): channel_mapping=dict(), parent_loop=program, to_single_waveform=set(), - global_transformation=None) - self.assertEqual(children, program.children) + global_transformation=None, volatile=set()) + self.assertEqual(children, list(program.children)) self.assertEqual(1, program.repetition_count) self.assertIsNone(program._measurements) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -222,9 +222,9 @@ def test_create_program_invalid_params(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertEqual(1, program.repetition_count) self.assertIsNone(program._measurements) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -247,9 +247,9 @@ def test_create_program_invalid_measurement_mapping(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertEqual(1, program.repetition_count) self.assertIsNone(program._measurements) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -262,7 +262,7 @@ def test_create_program_invalid_measurement_mapping(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) def test_create_program_missing_params(self) -> None: dt = DummyPulseTemplate(parameter_names={'i'}, waveform=DummyWaveform(duration=4.0), duration='t', measurements=[('b', 2, 1)]) @@ -283,7 +283,7 @@ def test_create_program_missing_params(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) # test parameter in measurement mappings parameters = {'a': ConstantParameter(1), 'b': ConstantParameter(4), 'c': ConstantParameter(2)} @@ -293,7 +293,7 @@ def test_create_program_missing_params(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) # test parameter in duration parameters = {'a': ConstantParameter(1), 'b': ConstantParameter(4), 'c': ConstantParameter(2), 'alph': ConstantParameter(0)} @@ -303,9 +303,9 @@ def test_create_program_missing_params(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertEqual(1, program.repetition_count) self.assertIsNone(program._measurements) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -326,11 +326,11 @@ def test_create_program_body_none(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) self.assertEqual(0, len(program.children)) self.assertEqual(1, program.repetition_count) - self.assertEqual([], program.children) + self.assertEqual([], list(program.children)) # ensure same result as from Sequencer sequencer = Sequencer() @@ -339,7 +339,7 @@ def test_create_program_body_none(self) -> None: block = sequencer.build() program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.repetition_count, program.repetition_count) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) self.assertEqual(program_old.waveform, program.waveform) # program_old defines measurements while program does not @@ -355,6 +355,7 @@ def test_create_program(self) -> None: 'meas_param': ConstantParameter(.1)} measurement_mapping = dict(A='B', b='b') channel_mapping = dict(C='D') + volatile = {'inner'} to_single_waveform = {'tom', 'jerry'} global_transformation = TransformationStub() @@ -370,7 +371,7 @@ def test_create_program(self) -> None: channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=program) + parent_loop=program, volatile=volatile) expected_create_program_calls = [mock.call(**expected_create_program_kwargs, parameters=dict(i=ConstantParameter(i))) for i in (1, 3)] @@ -384,7 +385,8 @@ def test_create_program(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=to_single_waveform, - global_transformation=global_transformation) + global_transformation=global_transformation, + volatile=volatile) validate_parameter_constraints.assert_called_once_with(parameters=parameters) get_measurement_windows.assert_called_once_with(expected_meas_params, measurement_mapping) @@ -401,6 +403,7 @@ def test_create_program_append(self) -> None: parameters = {'a': ConstantParameter(1), 'b': ConstantParameter(4), 'c': ConstantParameter(2)} measurement_mapping = dict(A='B', b='b') channel_mapping = dict(C='D') + volatile = {'inner'} children = [Loop(waveform=DummyWaveform(duration=2.0))] program = Loop(children=children) @@ -409,7 +412,7 @@ def test_create_program_append(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=volatile) self.assertEqual(3, len(program.children)) self.assertIs(children[0], program.children[0]) diff --git a/tests/pulses/mapping_pulse_template_tests.py b/tests/pulses/mapping_pulse_template_tests.py index 777d14dd4..06bd882f8 100644 --- a/tests/pulses/mapping_pulse_template_tests.py +++ b/tests/pulses/mapping_pulse_template_tests.py @@ -288,7 +288,7 @@ def test_create_program(self) -> None: channel_mapping=st.get_updated_channel_mapping(pre_channel_mapping), to_single_waveform=to_single_waveform, global_transformation=global_transformation, - parent_loop=program) + parent_loop=program, volatile=set()) with mock.patch.object(template, '_create_program') as inner_create_program: st._internal_create_program(parameters=pre_parameters, @@ -296,7 +296,7 @@ def test_create_program(self) -> None: channel_mapping=pre_channel_mapping, to_single_waveform=to_single_waveform, global_transformation=global_transformation, - parent_loop=program) + parent_loop=program, volatile=set()) inner_create_program.assert_called_once_with(**expected_inner_args) # as we mock the inner function there shouldnt be any changes @@ -326,7 +326,7 @@ def test_create_program_invalid_measurement_mapping(self) -> None: channel_mapping=pre_channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) def test_create_program_missing_params(self) -> None: measurement_mapping = {'meas1': 'meas2'} @@ -352,7 +352,7 @@ def test_create_program_missing_params(self) -> None: channel_mapping=pre_channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) def test_create_program_parameter_constraint_violation(self) -> None: measurement_mapping = {'meas1': 'meas2'} @@ -379,12 +379,13 @@ def test_create_program_parameter_constraint_violation(self) -> None: channel_mapping=pre_channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) def test_create_program_subtemplate_none(self) -> None: measurement_mapping = {'meas1': 'meas2'} parameter_mapping = {'t': 'k'} channel_mapping = {'B': 'default'} + volatile = {'t'} template = DummyPulseTemplate(measurements=[('meas1', 0, 1)], measurement_names={'meas1'}, defined_channels={'B'}, @@ -404,13 +405,13 @@ def test_create_program_subtemplate_none(self) -> None: channel_mapping=pre_channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=volatile) self.assertEqual(1, len(template.create_program_calls)) self.assertEqual((st.map_parameters(pre_parameters), st.get_updated_measurement_mapping(pre_measurement_mapping), st.get_updated_channel_mapping(pre_channel_mapping), - program), + program, st.map_volatile(volatile)), template.create_program_calls[-1]) self.assertEqual(1, program.repetition_count) @@ -571,7 +572,7 @@ def test_issue_451(self): input_variables = {'period': float(gates_template.duration), 'uptime': 0} marker_sequence = (TablePT({'m': [(0, 1), ('uptime', 0), ('period', 0)]}), input_variables) - combined_template = AtomicMultiChannelPT(gates_template, marker_sequence ) + combined_template = AtomicMultiChannelPT(gates_template, marker_sequence) combined_template.create_program() marker_sequence2 = TablePT({'m': [(0, 1), (0, 0), (gates_template.duration, 0)]}) diff --git a/tests/pulses/multi_channel_pulse_template_tests.py b/tests/pulses/multi_channel_pulse_template_tests.py index 6f1f2bb19..85d602404 100644 --- a/tests/pulses/multi_channel_pulse_template_tests.py +++ b/tests/pulses/multi_channel_pulse_template_tests.py @@ -450,7 +450,7 @@ def test_internal_create_program(self): other_kwargs = dict(measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, volatile=set()) pccpt = ParallelConstantChannelPulseTemplate(template, overwritten_channels) parameters = {'c': ConstantParameter(1.2), 'a': ConstantParameter(3.4)} diff --git a/tests/pulses/pulse_template_tests.py b/tests/pulses/pulse_template_tests.py index 304f13a79..551af9cc3 100644 --- a/tests/pulses/pulse_template_tests.py +++ b/tests/pulses/pulse_template_tests.py @@ -193,7 +193,7 @@ def test_create_program(self) -> None: channel_mapping=channel_mapping, to_single_waveform=to_single_waveform, global_transformation=global_transformation) - _create_program.assert_called_once_with(**expected_internal_kwargs, parent_loop=program) + _create_program.assert_called_once_with(**expected_internal_kwargs, parent_loop=program, volatile=set()) self.assertEqual(expected_program, program) self.assertEqual(previos_measurement_mapping, measurement_mapping) self.assertEqual(previous_channel_mapping, channel_mapping) @@ -214,14 +214,14 @@ def test__create_program(self): channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, volatile=set()) _internal_create_program.assert_called_once_with(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, volatile=set()) self.assertEqual(parent_loop, Loop()) @@ -234,6 +234,7 @@ def test__create_program_single_waveform(self): measurement_mapping = {'M': 'N'} channel_mapping = {'B': 'A'} parent_loop = Loop() + volatile = {'a'} wf = DummyWaveform() single_waveform = DummyWaveform() @@ -262,14 +263,15 @@ def test__create_program_single_waveform(self): channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=parent_loop) + parent_loop=parent_loop, volatile=volatile) _internal_create_program.assert_called_once_with(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=to_single_waveform, - parent_loop=expected_inner_program) + parent_loop=expected_inner_program, + volatile=volatile) to_waveform.assert_called_once_with(expected_inner_program) @@ -294,7 +296,7 @@ def test_create_program_defaults(self) -> None: '_internal_create_program', wraps=get_appending_internal_create_program(dummy_waveform, True)) as _internal_create_program: program = template.create_program() - _internal_create_program.assert_called_once_with(**expected_internal_kwargs, parent_loop=program) + _internal_create_program.assert_called_once_with(**expected_internal_kwargs, parent_loop=program, volatile=set()) self.assertEqual(expected_program, program) def test_create_program_channel_mapping(self): @@ -304,7 +306,7 @@ def test_create_program_channel_mapping(self): measurement_mapping=dict(), channel_mapping={'A': 'C', 'B': 'B'}, global_transformation=None, - to_single_waveform=set()) + to_single_waveform=set(), volatile=set()) with mock.patch.object(template, '_internal_create_program') as _internal_create_program: template.create_program(channel_mapping={'A': 'C'}) @@ -323,7 +325,7 @@ def test_create_program_none(self) -> None: measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, global_transformation=None, - to_single_waveform=set()) + to_single_waveform=set(), volatile=set()) with mock.patch.object(template, '_internal_create_program') as _internal_create_program: @@ -417,7 +419,7 @@ def test_internal_create_program(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) build_waveform.assert_called_once_with(parameters=expected_parameters, channel_mapping=channel_mapping) self.assertEqual(expected_program, program) @@ -445,7 +447,7 @@ def test_internal_create_program_transformation(self): channel_mapping={}, parent_loop=program, to_single_waveform=set(), - global_transformation=global_transformation) + global_transformation=global_transformation, volatile=set()) self.assertEqual(expected_program, program) @@ -470,7 +472,7 @@ def test_internal_create_program_no_waveform(self) -> None: channel_mapping=channel_mapping, parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) build_waveform.assert_called_once_with(parameters=expected_parameters, channel_mapping=channel_mapping) get_meas_windows.assert_not_called() @@ -496,7 +498,7 @@ def test_internal_create_program_invalid_measurement_mapping(self) -> None: template._internal_create_program(parameters=parameters, measurement_mapping=dict(), channel_mapping=dict(), - parent_loop=program) + parent_loop=program, volatile=set()) def test_internal_create_program_missing_parameters(self) -> None: measurement_windows = [('M', 'z', 5)] @@ -513,4 +515,4 @@ def test_internal_create_program_missing_parameters(self) -> None: channel_mapping=dict(), parent_loop=program, to_single_waveform=set(), - global_transformation=None) + global_transformation=None, volatile=set()) diff --git a/tests/pulses/repetition_pulse_template_tests.py b/tests/pulses/repetition_pulse_template_tests.py index 20cbde970..e9ef99a09 100644 --- a/tests/pulses/repetition_pulse_template_tests.py +++ b/tests/pulses/repetition_pulse_template_tests.py @@ -135,7 +135,7 @@ def test_internal_create_program(self): channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertEqual(program, expected_program) body_create_program.assert_called_once_with(parameters=parameters, @@ -143,7 +143,7 @@ def test_internal_create_program(self): channel_mapping=channel_mapping, global_transformation=global_transformation, to_single_waveform=to_single_waveform, - parent_loop=program.children[0]) + parent_loop=program.children[0], volatile=set()) validate_parameter_constraints.assert_called_once_with(parameters=parameters) get_repetition_count_value.assert_called_once_with(real_relevant_parameters) get_meas.assert_called_once_with(real_relevant_parameters, measurement_mapping) @@ -154,6 +154,7 @@ def test_create_program_constant_success_measurements(self) -> None: t = RepetitionPulseTemplate(body, repetitions, parameter_constraints=['foo<9'], measurements=[('my', 2, 2)]) parameters = {'foo': 8} measurement_mapping = {'my': 'thy', 'b': 'b'} + volatile = set() channel_mapping = {} program = Loop() t._internal_create_program(parameters=parameters, @@ -161,14 +162,14 @@ def test_create_program_constant_success_measurements(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=volatile) self.assertEqual(1, len(program.children)) internal_loop = program.children[0] # type: Loop self.assertEqual(repetitions, internal_loop.repetition_count) self.assertEqual(1, len(internal_loop)) - self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop), body.create_program_calls[-1]) + self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop, volatile), body.create_program_calls[-1]) self.assertEqual(body.waveform, internal_loop[0].waveform) self.assert_measurement_windows_equal({'b': ([0, 2, 4], [1, 1, 1]), 'thy': ([2], [2])}, program.get_measurement_windows()) @@ -187,13 +188,14 @@ def test_create_program_declaration_success(self) -> None: parameters = dict(foo=ConstantParameter(3)) measurement_mapping = dict(moth='fire') channel_mapping = dict(asd='f') + volatile = set() program = Loop() t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertEqual(1, program.repetition_count) self.assertEqual(1, len(program.children)) @@ -201,7 +203,7 @@ def test_create_program_declaration_success(self) -> None: self.assertEqual(parameters[repetitions].get_value(), internal_loop.repetition_count) self.assertEqual(1, len(internal_loop)) - self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop), body.create_program_calls[-1]) + self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop, volatile), body.create_program_calls[-1]) self.assertEqual(body.waveform, internal_loop[0].waveform) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -217,7 +219,7 @@ def test_create_program_declaration_success_appended_measurements(self) -> None: parameters = dict(foo=ConstantParameter(3), meas_end=ConstantParameter(7.1)) measurement_mapping = dict(moth='fire', b='b') channel_mapping = dict(asd='f') - + volatile = set() children = [Loop(waveform=DummyWaveform(duration=0))] program = Loop(children=children, measurements=[('a', [0], [1])], repetition_count=2) @@ -226,7 +228,7 @@ def test_create_program_declaration_success_appended_measurements(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=volatile) self.assertEqual(2, program.repetition_count) self.assertEqual(2, len(program.children)) @@ -235,7 +237,7 @@ def test_create_program_declaration_success_appended_measurements(self) -> None: self.assertEqual(parameters[repetitions].get_value(), internal_loop.repetition_count) self.assertEqual(1, len(internal_loop)) - self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop), body.create_program_calls[-1]) + self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop, volatile), body.create_program_calls[-1]) self.assertEqual(body.waveform, internal_loop[0].waveform) self.assert_measurement_windows_equal({'fire': ([0, 6], [7.1, 7.1]), @@ -252,13 +254,14 @@ def test_create_program_declaration_success_measurements(self) -> None: parameters = dict(foo=ConstantParameter(3), meas_end=ConstantParameter(7.1)) measurement_mapping = dict(moth='fire', b='b') channel_mapping = dict(asd='f') + volatile = set() program = Loop() t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertEqual(1, program.repetition_count) self.assertEqual(1, len(program.children)) @@ -266,7 +269,7 @@ def test_create_program_declaration_success_measurements(self) -> None: self.assertEqual(parameters[repetitions].get_value(), internal_loop.repetition_count) self.assertEqual(1, len(internal_loop)) - self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop), body.create_program_calls[-1]) + self.assertEqual((parameters, measurement_mapping, channel_mapping, internal_loop, volatile), body.create_program_calls[-1]) self.assertEqual(body.waveform, internal_loop[0].waveform) self.assert_measurement_windows_equal({'fire': ([0], [7.1]), 'b': ([0, 2, 4], [1, 1, 1])}, program.get_measurement_windows()) @@ -296,10 +299,10 @@ def test_create_program_declaration_exceeds_bounds(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertEqual(1, program.repetition_count) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertIsNone(program.waveform) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -318,7 +321,7 @@ def test_create_program_declaration_parameter_not_provided(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) parameters = {'foo': ConstantParameter(7)} with self.assertRaises(ParameterNotProvidedException): @@ -327,11 +330,11 @@ def test_create_program_declaration_parameter_not_provided(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertEqual(1, program.repetition_count) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertIsNone(program.waveform) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -350,10 +353,10 @@ def test_create_program_declaration_parameter_value_not_whole(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertEqual(1, program.repetition_count) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertIsNone(program.waveform) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -370,9 +373,9 @@ def test_create_program_constant_measurement_mapping_failure(self) -> None: t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, - to_single_waveform=set(), - global_transformation=None, - parent_loop=program) + to_single_waveform=set(), + global_transformation=None, + parent_loop=program, volatile=set()) # test for failure on child level measurement_mapping = dict(a='a') @@ -380,12 +383,12 @@ def test_create_program_constant_measurement_mapping_failure(self) -> None: t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, - to_single_waveform=set(), - global_transformation=None, - parent_loop=program) + to_single_waveform=set(), + global_transformation=None, + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertEqual(1, program.repetition_count) - self.assertEqual(children, program.children) + self.assertEqual(children, list(program.children)) self.assertIsNone(program.waveform) self.assert_measurement_windows_equal({}, program.get_measurement_windows()) @@ -408,7 +411,7 @@ def test_create_program_rep_count_zero_constant(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -441,7 +444,7 @@ def test_create_program_rep_count_zero_constant_with_measurement(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -455,7 +458,7 @@ def test_create_program_rep_count_zero_constant_with_measurement(self) -> None: program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.repetition_count, program.repetition_count) self.assertEqual(program_old.waveform, program.waveform) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) # program_old will have measurements which program has not! def test_create_program_rep_count_zero_declaration(self) -> None: @@ -477,7 +480,7 @@ def test_create_program_rep_count_zero_declaration(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -510,7 +513,7 @@ def test_create_program_rep_count_zero_declaration_with_measurement(self) -> Non channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -524,7 +527,7 @@ def test_create_program_rep_count_zero_declaration_with_measurement(self) -> Non program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.repetition_count, program.repetition_count) self.assertEqual(program_old.waveform, program.waveform) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) # program_old will have measurements which program has not! def test_create_program_rep_count_neg_declaration(self) -> None: @@ -546,7 +549,7 @@ def test_create_program_rep_count_neg_declaration(self) -> None: channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -579,7 +582,7 @@ def test_create_program_rep_count_neg_declaration_with_measurements(self) -> Non channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=set()) self.assertFalse(body.create_program_calls) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) @@ -593,7 +596,7 @@ def test_create_program_rep_count_neg_declaration_with_measurements(self) -> Non program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.repetition_count, program.repetition_count) self.assertEqual(program_old.waveform, program.waveform) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) # program_old will have measurements which program has not! def test_create_program_none_subprogram(self) -> None: @@ -603,13 +606,14 @@ def test_create_program_none_subprogram(self) -> None: parameters = dict(foo=ConstantParameter(3)) measurement_mapping = dict(moth='fire') channel_mapping = dict(asd='f') + volatile = set() program = Loop() t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=volatile) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) self.assertEqual(None, program._measurements) @@ -621,7 +625,7 @@ def test_create_program_none_subprogram(self) -> None: block = sequencer.build() program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.waveform, program.waveform) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) self.assertEqual(program_old._measurements, program._measurements) # Sequencer does set a repetition count if no inner program is present; create_program does not @@ -632,13 +636,15 @@ def test_create_program_none_subprogram_with_measurement(self) -> None: parameters = dict(foo=ConstantParameter(3), meas_end=ConstantParameter(7.1)) measurement_mapping = dict(moth='fire', b='b') channel_mapping = dict(asd='f') + volatile = set() program = Loop() + t._internal_create_program(parameters=parameters, measurement_mapping=measurement_mapping, channel_mapping=channel_mapping, to_single_waveform=set(), global_transformation=None, - parent_loop=program) + parent_loop=program, volatile=volatile) self.assertFalse(program.children) self.assertEqual(1, program.repetition_count) self.assertEqual(None, program._measurements) @@ -650,7 +656,7 @@ def test_create_program_none_subprogram_with_measurement(self) -> None: block = sequencer.build() program_old = MultiChannelProgram(block, channels={'A'}).programs[frozenset({'A'})] self.assertEqual(program_old.waveform, program.waveform) - self.assertEqual(program_old.children, program.children) + self.assertEqual(list(program_old.children), list(program.children)) # program_old will have measurements which program has not! # Sequencer does set a repetition count if no inner program is present; create_program does not diff --git a/tests/pulses/sequence_pulse_template_tests.py b/tests/pulses/sequence_pulse_template_tests.py index aceee7960..ab814766f 100644 --- a/tests/pulses/sequence_pulse_template_tests.py +++ b/tests/pulses/sequence_pulse_template_tests.py @@ -253,14 +253,14 @@ def test_internal_create_program(self): mock.patch.object(sub_templates[1], '_create_program', wraps=get_appending_internal_create_program(wfs[1], True)) as create_1: - spt._internal_create_program(**kwargs, parent_loop=program) + spt._internal_create_program(**kwargs, parent_loop=program, volatile=set()) self.assertEqual(expected_program, program) validate_parameter_constraints.assert_called_once_with(parameters=kwargs['parameters']) get_measurement_windows.assert_called_once_with(dict(a=.1, b=.2), kwargs['measurement_mapping']) - create_0.assert_called_once_with(**kwargs, parent_loop=program) - create_1.assert_called_once_with(**kwargs, parent_loop=program) + create_0.assert_called_once_with(**kwargs, parent_loop=program, volatile=set()) + create_1.assert_called_once_with(**kwargs, parent_loop=program, volatile=set()) def test_create_program_internal(self) -> None: sub1 = DummyPulseTemplate(duration=3, waveform=DummyWaveform(duration=3), measurements=[('b', 1, 2)], defined_channels={'A'}) @@ -268,6 +268,7 @@ def test_create_program_internal(self) -> None: parameters = {'foo': DummyNoValueParameter()} measurement_mapping = {'a': 'a', 'b': 'b'} channel_mapping = dict() + volatile = set() seq = SequencePulseTemplate(sub1, sub2, measurements=[('a', 0, 1)]) loop = Loop() seq._internal_create_program(parameters=parameters, @@ -275,12 +276,12 @@ def test_create_program_internal(self) -> None: channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=set(), - parent_loop=loop) + parent_loop=loop, volatile=volatile) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) self.assertEqual([Loop(repetition_count=1, waveform=sub1.waveform), Loop(repetition_count=1, waveform=sub2.waveform)], - loop.children) + list(loop.children)) self.assert_measurement_windows_equal({'a': ([0], [1]), 'b': ([1], [2])}, loop.get_measurement_windows()) # ensure same result as from Sequencer @@ -298,12 +299,13 @@ def test_create_program_internal(self) -> None: channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) self.assertEqual([Loop(repetition_count=1, waveform=sub2.waveform), Loop(repetition_count=1, waveform=sub1.waveform)], - loop.children) + list(loop.children)) self.assert_measurement_windows_equal({'a': ([0], [1]), 'b': ([3], [2])}, loop.get_measurement_windows()) # ensure same result as from Sequencer @@ -327,11 +329,12 @@ def test_internal_create_program_no_measurement_mapping(self) -> None: channel_mapping=dict(), global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) self.assertFalse(sub1.create_program_calls) self.assertFalse(sub2.create_program_calls) - self.assertEqual(children, loop.children) + self.assertEqual(children, list(loop.children)) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) self.assert_measurement_windows_equal({}, loop.get_measurement_windows()) @@ -341,8 +344,9 @@ def test_internal_create_program_no_measurement_mapping(self) -> None: seq._internal_create_program(parameters=parameters, measurement_mapping=dict(a='a'), channel_mapping=dict(), - global_transformation=None, - to_single_waveform=set(), + global_transformation=None, + to_single_waveform=set(), + volatile=set(), parent_loop=loop) def test_internal_create_program_one_child_no_duration(self) -> None: @@ -351,6 +355,7 @@ def test_internal_create_program_one_child_no_duration(self) -> None: parameters = {'foo': DummyNoValueParameter()} measurement_mapping = {'a': 'a', 'b': 'b'} channel_mapping = dict() + volatile = set() seq = SequencePulseTemplate(sub1, sub2, measurements=[('a', 0, 1)]) loop = Loop() seq._internal_create_program(parameters=parameters, @@ -358,11 +363,11 @@ def test_internal_create_program_one_child_no_duration(self) -> None: channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=set(), - parent_loop=loop) + parent_loop=loop, volatile=volatile) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) self.assertEqual([Loop(repetition_count=1, waveform=sub2.waveform)], - loop.children) + list(loop.children)) self.assert_measurement_windows_equal({'a': ([0], [1])}, loop.get_measurement_windows()) # ensure same result as from Sequencer @@ -380,11 +385,11 @@ def test_internal_create_program_one_child_no_duration(self) -> None: channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=set(), - parent_loop=loop) + parent_loop=loop, volatile=volatile) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) self.assertEqual([Loop(repetition_count=1, waveform=sub2.waveform)], - loop.children) + list(loop.children)) self.assert_measurement_windows_equal({'a': ([0], [1])}, loop.get_measurement_windows()) # ensure same result as from Sequencer @@ -400,6 +405,7 @@ def test_internal_create_program_both_children_no_duration(self) -> None: parameters = {'foo': DummyNoValueParameter()} measurement_mapping = {'a': 'a', 'b': 'b'} channel_mapping = dict() + volatile = set() seq = SequencePulseTemplate(sub1, sub2, measurements=[('a', 0, 1)]) loop = Loop(measurements=None) @@ -408,10 +414,11 @@ def test_internal_create_program_both_children_no_duration(self) -> None: channel_mapping=channel_mapping, global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) self.assertEqual(1, loop.repetition_count) self.assertIsNone(loop.waveform) - self.assertEqual([], loop.children) + self.assertEqual([], list(loop.children)) self.assertIsNone(loop._measurements) # ensure same result as from Sequencer @@ -436,6 +443,7 @@ def test_internal_create_program_parameter_constraint_violations(self) -> None: channel_mapping=dict(), global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) def test_internal_create_program_parameter_missing(self) -> None: @@ -452,6 +460,7 @@ def test_internal_create_program_parameter_missing(self) -> None: channel_mapping=dict(), global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) # test parameter from measurements @@ -462,6 +471,7 @@ def test_internal_create_program_parameter_missing(self) -> None: channel_mapping=dict(), global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) # test parameter from duration @@ -472,6 +482,7 @@ def test_internal_create_program_parameter_missing(self) -> None: channel_mapping=dict(), global_transformation=None, to_single_waveform=set(), + volatile=set(), parent_loop=loop) diff --git a/tests/pulses/sequencing_dummies.py b/tests/pulses/sequencing_dummies.py index 75ac6ceab..21a0a755c 100644 --- a/tests/pulses/sequencing_dummies.py +++ b/tests/pulses/sequencing_dummies.py @@ -397,9 +397,10 @@ def _internal_create_program(self, *, channel_mapping: Dict[ChannelID, Optional[ChannelID]], global_transformation: Optional['Transformation'], to_single_waveform: Set[Union[str, 'PulseTemplate']], - parent_loop: Loop) -> None: + parent_loop: Loop, + volatile: Set[str]) -> None: measurements = self.get_measurement_windows(parameters, measurement_mapping) - self.create_program_calls.append((parameters, measurement_mapping, channel_mapping, parent_loop)) + self.create_program_calls.append((parameters, measurement_mapping, channel_mapping, parent_loop, volatile)) if self._program: parent_loop.add_measurements(measurements) parent_loop.append_child(waveform=self._program.waveform, children=self._program.children)