def test_make_compatible_partial_unroll(self): wf1 = DummyWaveform(duration=1.5) wf2 = DummyWaveform(duration=2.0) program = Loop(children=[Loop(waveform=wf1, repetition_count=2), Loop(waveform=wf2)]) _make_compatible(program, min_len=1, quantum=1, sample_rate=TimeType.from_float(1.)) self.assertIsNone(program.waveform) self.assertEqual(len(program), 2) self.assertIsInstance(program[0].waveform, RepetitionWaveform) self.assertIs(program[0].waveform._body, wf1) self.assertEqual(program[0].waveform._repetition_count, 2) self.assertIs(program[1].waveform, wf2) program = Loop(children=[Loop(waveform=wf1, repetition_count=2), Loop(waveform=wf2)], repetition_count=2) _make_compatible(program, min_len=5, quantum=1, sample_rate=TimeType.from_float(1.)) self.assertIsInstance(program.waveform, SequenceWaveform) self.assertEqual(list(program.children), []) self.assertEqual(program.repetition_count, 2) self.assertEqual(len(program.waveform._sequenced_waveforms), 2) self.assertIsInstance(program.waveform._sequenced_waveforms[0], RepetitionWaveform) self.assertIs(program.waveform._sequenced_waveforms[0]._body, wf1) self.assertEqual(program.waveform._sequenced_waveforms[0]._repetition_count, 2) self.assertIs(program.waveform._sequenced_waveforms[1], wf2)
def test_make_compatible(self): program = Loop() pub_kwargs = dict(minimal_waveform_length=5, waveform_quantum=10, sample_rate=TimeType.from_float(1.)) priv_kwargs = dict(min_len=5, quantum=10, sample_rate=TimeType.from_float(1.)) with mock.patch('qupulse._program._loop._is_compatible', return_value=_CompatibilityLevel.incompatible_too_short) as mocked: with self.assertRaisesRegex(ValueError, 'too short'): make_compatible(program, **pub_kwargs) mocked.assert_called_once_with(program, **priv_kwargs) with mock.patch('qupulse._program._loop._is_compatible', return_value=_CompatibilityLevel.incompatible_fraction) as mocked: with self.assertRaisesRegex(ValueError, 'not an integer'): make_compatible(program, **pub_kwargs) mocked.assert_called_once_with(program, **priv_kwargs) with mock.patch('qupulse._program._loop._is_compatible', return_value=_CompatibilityLevel.incompatible_quantum) as mocked: with self.assertRaisesRegex(ValueError, 'not a multiple of quantum'): make_compatible(program, **pub_kwargs) mocked.assert_called_once_with(program, **priv_kwargs) with mock.patch('qupulse._program._loop._is_compatible', return_value=_CompatibilityLevel.action_required) as is_compat: with mock.patch('qupulse._program._loop._make_compatible') as make_compat: make_compatible(program, **pub_kwargs) is_compat.assert_called_once_with(program, **priv_kwargs) make_compat.assert_called_once_with(program, **priv_kwargs)
def test_is_compatible_leaf(self): self.assertEqual(_is_compatible(Loop(waveform=DummyWaveform(duration=1.1), repetition_count=10), min_len=11, quantum=1, sample_rate=TimeType.from_float(1.)), _CompatibilityLevel.action_required) self.assertEqual(_is_compatible(Loop(waveform=DummyWaveform(duration=1.1), repetition_count=10), min_len=11, quantum=1, sample_rate=TimeType.from_float(10.)), _CompatibilityLevel.compatible)
def test_get_sample_times_single_wf(self): sample_rate = TimeType(12, 10) wf = DummyWaveform(duration=TimeType(40, 12)) expected_times = np.arange(4) / 1.2 times, n_samples = get_sample_times(wf, sample_rate_in_GHz=sample_rate) np.testing.assert_equal(times, expected_times) np.testing.assert_equal(n_samples, np.asarray(4))
def test_is_compatible_node(self): program = Loop(children=[Loop(waveform=DummyWaveform(duration=1.5), repetition_count=2), Loop(waveform=DummyWaveform(duration=2.0))]) self.assertEqual(_is_compatible(program, min_len=1, quantum=1, sample_rate=TimeType.from_float(2.)), _CompatibilityLevel.compatible) self.assertEqual(_is_compatible(program, min_len=1, quantum=1, sample_rate=TimeType.from_float(1.)), _CompatibilityLevel.action_required)
def test_evaluate_with_exact_rationals(self): expr = ExpressionScalar('1 / 3') self.assertEqual(TimeType.from_fraction(1, 3), expr.evaluate_with_exact_rationals({})) expr = ExpressionScalar('a * (1 / 3)') self.assertEqual(TimeType.from_fraction(2, 3), expr.evaluate_with_exact_rationals({'a': 2})) expr = ExpressionScalar('dot(a, b) * (1 / 3)') self.assertEqual(TimeType.from_fraction(10, 3), expr.evaluate_with_exact_rationals({'a': [2, 2], 'b': [1, 4]}))
def test_unsafe_get_subset_for_channels(self): dwf_1 = DummyWaveform(duration=2.2, defined_channels={'A', 'B', 'C'}) dwf_2 = DummyWaveform(duration=3.3, defined_channels={'A', 'B', 'C'}) wf = SequenceWaveform([dwf_1, dwf_2]) subset = {'A', 'C'} sub_wf = wf.unsafe_get_subset_for_channels(subset) self.assertIsInstance(sub_wf, SequenceWaveform) self.assertEqual(len(sub_wf.compare_key), 2) self.assertEqual(sub_wf.compare_key[0].defined_channels, subset) self.assertEqual(sub_wf.compare_key[1].defined_channels, subset) self.assertEqual(sub_wf.compare_key[0].duration, TimeType.from_float(2.2)) self.assertEqual(sub_wf.compare_key[1].duration, TimeType.from_float(3.3))
def _get_measurement_windows(self) -> DefaultDict[str, np.ndarray]: temp_meas_windows = defaultdict(list) if self._measurements: for (mw_name, begin, length) in self._measurements: temp_meas_windows[mw_name].append((begin, length)) for mw_name, begin_length_list in temp_meas_windows.items(): temp_meas_windows[mw_name] = [np.asarray(begin_length_list, dtype=float)] # calculate duration together with meas windows in the same iteration if self.is_leaf(): body_duration = float(self.body_duration) else: offset = TimeType(0) for child in self: for mw_name, begins_length_array in child._get_measurement_windows().items(): begins_length_array[:, 0] += float(offset) temp_meas_windows[mw_name].append(begins_length_array) offset += child.duration body_duration = float(offset) # repeat and add repetition based offset for mw_name, begin_length_list in temp_meas_windows.items(): temp_begin_length_array = np.concatenate(begin_length_list) begin_length_array = np.tile(temp_begin_length_array, (self.repetition_count, 1)) shaped_begin_length_array = np.reshape(begin_length_array, (self.repetition_count, -1, 2)) shaped_begin_length_array[:, :, 0] += (np.arange(self.repetition_count) * body_duration)[:, np.newaxis] temp_meas_windows[mw_name] = begin_length_array return temp_meas_windows
def _upload(self, name: str, program: Loop, channels: Tuple[Optional[ChannelID], ...], markers: Tuple[Optional[ChannelID], ...], voltage_transformation: Tuple[Callable, ...], force: bool): assert self._idle_program_index if name in self._programs and not force: raise ProgramOverwriteException(name) # group markers in by channels markers = tuple(zip(markers[0::2], markers[1::2])) if self._amplitude_offset_handling == AWGAmplitudeOffsetHandling.IGNORE_OFFSET: offsets = None elif self._amplitude_offset_handling == AWGAmplitudeOffsetHandling.CONSIDER_OFFSET: offsets = self.device.get_offset() else: raise ValueError('{} is invalid as AWGAmplitudeOffsetHandling'.format(self._amplitude_offset_handling)) tek_program = TektronixProgram(program, channels=channels, markers=markers, amplitudes=self.device.get_amplitude(), offsets=offsets, voltage_transformations=voltage_transformation, sample_rate=TimeType(self.sample_rate)) self.logger.debug("Successfully parsed %s", name) if name in self._programs: self._unload(name) self._upload_parsed(name, tek_program)
def setUp(self) -> None: self.channels = ('A', None, 'C') self.marker = (None, 'M') self.amplitudes = (1., 1., .5) self.offset = (0., .5, .0) self.voltage_transformations = (mock.Mock(wraps=lambda x: x), mock.Mock(wraps=lambda x: x), mock.Mock(wraps=lambda x: x)) self.sample_rate = TimeType.from_float(1) N = 100 t = np.arange(N) self.sampled = [ dict(A=np.linspace(-.1, .1, num=N), C=.1 * np.sin(t), M=np.arange(N) % 2), dict(A=np.linspace(.1, -.1, num=N // 2), C=.1 * np.cos(t[::2]), M=np.arange(N // 2) % 3) ] self.waveforms = [ wf for wf in (DummyWaveform(sample_output=sampled, duration=sampled['A'].size) for sampled in self.sampled) ] self.loop = Loop(children=[Loop(waveform=wf) for wf in self.waveforms] * 2)
def test_from_sequence(self): dwf = DummyWaveform(duration=1.1, defined_channels={'A'}) self.assertIs(dwf, SequenceWaveform.from_sequence((dwf,))) swf1 = SequenceWaveform.from_sequence((dwf, dwf)) swf2 = SequenceWaveform.from_sequence((swf1, dwf)) assert_constant_consistent(self, swf1) assert_constant_consistent(self, swf2) self.assertEqual(3*(dwf,), swf2.sequenced_waveforms) cwf_2_a = ConstantWaveform(duration=1.1, amplitude=2.2, channel='A') cwf_3 = ConstantWaveform(duration=1.1, amplitude=3.3, channel='A') cwf_2_b = ConstantWaveform(duration=1.1, amplitude=2.2, channel='A') with mock.patch.object(ConstantWaveform, 'from_mapping', return_value=mock.sentinel) as from_mapping: new_constant = SequenceWaveform.from_sequence((cwf_2_a, cwf_2_b)) self.assertIs(from_mapping.return_value, new_constant) from_mapping.assert_called_once_with(2*TimeType.from_float(1.1), {'A': 2.2}) swf3 = SequenceWaveform.from_sequence((cwf_2_a, dwf)) self.assertEqual((cwf_2_a, dwf), swf3.sequenced_waveforms) self.assertIsNone(swf3.constant_value('A')) assert_constant_consistent(self, swf3) swf3 = SequenceWaveform.from_sequence((cwf_2_a, cwf_3)) self.assertEqual((cwf_2_a, cwf_3), swf3.sequenced_waveforms) self.assertIsNone(swf3.constant_value('A')) assert_constant_consistent(self, swf3)
def set_measurement_mask(self, program_name, mask_name, begins, lengths) -> Tuple[np.ndarray, np.ndarray]: sample_factor = TimeType( int( self.config.captureClockConfiguration.numeric_sample_rate( self.card.model)), 10**9) return self._registered_programs[program_name].set_measurement_mask( mask_name, sample_factor, begins, lengths)
def __init__(self, duration: float = 0, sample_output: Union[numpy.ndarray, dict] = None, defined_channels={'A'}) -> None: super().__init__() self.duration_ = TimeType.from_float(duration) self.sample_output = sample_output self.defined_channels_ = defined_channels self.sample_calls = []
def arm_program(self, program_name: str) -> None: to_arm = self._registered_programs[program_name] if self.update_settings or self.__armed_program is not to_arm: config = self.config config.masks, config.operations, total_record_size = self._registered_programs[ program_name].iter(self._make_mask) sample_factor = TimeType.from_fraction( self.config.captureClockConfiguration.numeric_sample_rate( self.card.model), 10**9) if not config.operations: raise RuntimeError( "No operations: Arming program without operations is an error as there will " "be no result: %r" % program_name) elif not config.masks: raise RuntimeError( "No masks although there are operations in program: %r" % program_name) elif self._registered_programs[ program_name].sample_factor != sample_factor: raise RuntimeError( "Masks were registered with a different sample rate {}!={}" .format( self._registered_programs[program_name].sample_factor, sample_factor)) assert total_record_size > 0 minimum_record_size = self.__card.minimum_record_size total_record_size = (( (total_record_size - 1) // minimum_record_size) + 1) * minimum_record_size if config.totalRecordSize == 0: config.totalRecordSize = total_record_size elif config.totalRecordSize < total_record_size: raise ValueError( 'specified total record size is smaller than needed {} < {}' .format(config.totalRecordSize, total_record_size)) old_aimed_buffer_size = config.aimedBufferSize # work around for measurments not working with one buffer if config.totalRecordSize < 5 * config.aimedBufferSize: config.aimedBufferSize = config.totalRecordSize // 5 self.__card.applyConfiguration(config, True) # "Hide" work around from the user config.aimedBufferSize = old_aimed_buffer_size self.update_settings = False self.__armed_program = to_arm self.__card.startAcquisition(1)
def __init__(self, duration: Union[float, TimeType]=0, sample_output: Union[numpy.ndarray, dict]=None, defined_channels=None) -> None: super().__init__(duration=duration if isinstance(duration, TimeType) else TimeType.from_float(duration)) self.sample_output = sample_output if defined_channels is None: if isinstance(sample_output, dict): defined_channels = set(sample_output.keys()) else: defined_channels = {'A'} self.defined_channels_ = defined_channels self.sample_calls = []
def test_get_sample_times(self): sample_rate = TimeType.from_fraction(12, 10) wf1 = DummyWaveform(duration=TimeType.from_fraction(20, 12)) wf2 = DummyWaveform(duration=TimeType.from_fraction(400000000001, 120000000000)) wf3 = DummyWaveform(duration=TimeType.from_fraction(1, 10**15)) expected_times = np.arange(4) / 1.2 times, n_samples = get_sample_times([wf1, wf2], sample_rate_in_GHz=sample_rate) np.testing.assert_equal(expected_times, times) np.testing.assert_equal(n_samples, np.asarray([2, 4])) with self.assertRaises(AssertionError): get_sample_times([], sample_rate_in_GHz=sample_rate) with self.assertRaisesRegex(ValueError, "non integer length"): get_sample_times([wf1, wf2], sample_rate_in_GHz=sample_rate, tolerance=0.) with self.assertRaisesRegex(ValueError, "length <= zero"): get_sample_times([wf1, wf3], sample_rate_in_GHz=sample_rate)
def register_measurement_windows(self, program_name: str, windows: Dict[str, Tuple[np.ndarray, np.ndarray]]) -> None: program = self._registered_programs[program_name] sample_factor = TimeType.from_fraction(int(self.config.captureClockConfiguration.numeric_sample_rate(self.card.model)), 10 ** 9) program.clear_masks() for mask_name, (begins, lengths) in windows.items(): program.set_measurement_mask(mask_name, sample_factor, begins, lengths)
def body_duration(self) -> TimeType: if self._cached_body_duration is None: if self.is_leaf(): if self.waveform: self._cached_body_duration = self.waveform.duration else: self._cached_body_duration = TimeType(0) else: self._cached_body_duration = sum(child.duration for child in self) return self._cached_body_duration
def arm_program(self, program_name: str) -> None: logger.debug("Arming program %s on %r", program_name, self.__card) to_arm = self._registered_programs[program_name] if self.update_settings or self.__armed_program is not to_arm: logger.info("Arming %r by calling applyConfiguration. Update settings flag: %r", self.__card, self.update_settings) config = copy.deepcopy(self.default_config) config.masks, config.operations, total_record_size = self._registered_programs[program_name].iter( self._make_mask) sample_rate = config.captureClockConfiguration.numeric_sample_rate(self.card.model) # sample rate in GHz sample_factor = TimeType.from_fraction(sample_rate, 10 ** 9) if not config.operations: raise RuntimeError("No operations: Arming program without operations is an error as there will " "be no result: %r" % program_name) elif not config.masks: raise RuntimeError("No masks although there are operations in program: %r" % program_name) elif self._registered_programs[program_name].sample_factor != sample_factor: raise RuntimeError("Masks were registered with a different sample rate {}!={}".format( self._registered_programs[program_name].sample_factor, sample_factor)) assert total_record_size > 0 # extend the total record size to be a multiple of record_size_factor record_size_factor = self.record_size_factor total_record_size = (((total_record_size - 1) // record_size_factor) + 1) * record_size_factor if config.totalRecordSize == 0: config.totalRecordSize = total_record_size elif config.totalRecordSize < total_record_size: raise ValueError('specified total record size is smaller than needed {} < {}'.format(config.totalRecordSize, total_record_size)) self.__card.applyConfiguration(config, True) self._current_config = config self.update_settings = False self.__armed_program = to_arm elif self.__armed_program is to_arm and self._remaining_auto_triggers > 0: self._remaining_auto_triggers -= 1 logger.info("Relying on atsaverage auto-arm with %d auto triggers remaining after this one", self._remaining_auto_triggers) return self.__card.startAcquisition(to_arm.auto_rearm_count) self._remaining_auto_triggers = to_arm.auto_rearm_count - 1
def setUp(self) -> None: self._instr_props = None self.program_entry_kwargs = dict( amplitudes=(1., 1.), offsets=(0., 0.), voltage_transformations=(mock.Mock(wraps=lambda x: x), mock.Mock(wraps=lambda x: x)), sample_rate=TimeType.from_fraction(192, 1), mode=None) if tabor_control is not None: self._instr_props = tabor_control.util.model_properties_dict[ 'WX2184C'].copy()
def test_build_waveform_time_type(self): from qupulse.utils.types import TimeType table = TablePulseTemplate({0: [(0, 0), ('foo', 'v', 'linear'), ('bar', 0, 'jump')]}, parameter_constraints=['foo>1'], measurements=[('M', 'b', 'l'), ('N', 1, 2)]) parameters = {'v': 2.3, 'foo': TimeType.from_float(1.), 'bar': TimeType.from_float(4), 'b': TimeType.from_float(2), 'l': TimeType.from_float(1)} channel_mapping = {0: 'ch'} with self.assertRaises(ParameterConstraintViolation): table.build_waveform(parameters=parameters, channel_mapping=channel_mapping) parameters['foo'] = TimeType.from_float(1.1) waveform = table.build_waveform(parameters=parameters, channel_mapping=channel_mapping) self.assertIsInstance(waveform, TableWaveform) self.assertEqual(waveform._table, ((0, 0, HoldInterpolationStrategy()), (TimeType.from_float(1.1), 2.3, LinearInterpolationStrategy()), (4, 0, JumpInterpolationStrategy()))) self.assertEqual(waveform._channel_id, 'ch')
def _get_measurement_windows(self) -> Mapping[str, np.ndarray]: """Private implementation of get_measurement_windows with a slightly different data format for easier tiling. Returns: A dictionary (measurement_name -> array) with begin == array[:, 0] and length == array[:, 1] """ temp_meas_windows = defaultdict(list) if self._measurements: for (mw_name, begin, length) in self._measurements: temp_meas_windows[mw_name].append((begin, length)) for mw_name, begin_length_list in temp_meas_windows.items(): temp_meas_windows[mw_name] = [ np.asarray(begin_length_list, dtype=float) ] # calculate duration together with meas windows in the same iteration if self.is_leaf(): body_duration = float(self.body_duration) else: offset = TimeType(0) for child in self: for mw_name, begins_length_array in child._get_measurement_windows( ).items(): begins_length_array[:, 0] += float(offset) temp_meas_windows[mw_name].append(begins_length_array) offset += child.duration body_duration = float(offset) # this gives us regular dict behaviour of the returned object temp_meas_windows.default_factory = None # repeat and add repetition based offset for mw_name, begin_length_list in temp_meas_windows.items(): temp_begin_length_array = np.concatenate(begin_length_list) begin_length_array = np.tile(temp_begin_length_array, (self.repetition_count, 1)) shaped_begin_length_array = np.reshape( begin_length_array, (self.repetition_count, -1, 2)) shaped_begin_length_array[:, :, 0] += (np.arange(self.repetition_count) * body_duration)[:, np.newaxis] temp_meas_windows[mw_name] = begin_length_array # the cast is here because static type analysis struggles to detect that we replace _all_ values by ndarray in # the previous loop return cast(Mapping[str, np.ndarray], temp_meas_windows)
def setUp(self) -> None: # we currently allow overlapping masks in AlazarProgram (It will throw an error on upload) # This probably will change in the future self.masks = { 'unsorted': (np.array([1., 100, 13]), np.array([10., 999, 81])), 'sorted': (np.array([30., 100, 1300]), np.array([10., 990, 811])), 'overlapping': (np.array([30., 100, 300]), np.array([20., 900, 100])) } self.sample_factor = TimeType(10**8, 10**9) self.expected = { 'unsorted': (np.array([0, 1, 10]).astype(np.uint64), np.array([1, 8, 99]).astype(np.uint64)), 'sorted': (np.array([3, 10, 130]).astype(np.uint64), np.array([1, 99, 81]).astype(np.uint64)), 'overlapping': (np.array([3, 10, 30]).astype(np.uint64), np.array([2, 90, 10]).astype(np.uint64)) }
def test_init_several_channels(self) -> None: dwf_a = DummyWaveform(duration=2.2, defined_channels={'A'}) dwf_b = DummyWaveform(duration=2.2, defined_channels={'B'}) dwf_c = DummyWaveform(duration=2.3, defined_channels={'C'}) waveform = MultiChannelWaveform([dwf_a, dwf_b]) self.assertEqual({'A', 'B'}, waveform.defined_channels) self.assertEqual(TimeType.from_float(2.2), waveform.duration) with self.assertRaises(ValueError): MultiChannelWaveform([dwf_a, dwf_c]) with self.assertRaises(ValueError): MultiChannelWaveform([waveform, dwf_c]) with self.assertRaises(ValueError): MultiChannelWaveform((dwf_a, dwf_a)) dwf_c_valid = DummyWaveform(duration=2.2, defined_channels={'C'}) waveform_flat = MultiChannelWaveform((waveform, dwf_c_valid)) self.assertEqual(len(waveform_flat.compare_key), 3)
def __init__(self, expression: ExpressionScalar, duration: float, channel: ChannelID) -> None: """Creates a new FunctionWaveform instance. Args: expression: The function represented by this FunctionWaveform as a mathematical expression where 't' denotes the time variable. It must not have other variables duration: The duration of the waveform measurement_windows: A list of measurement windows channel: The channel this waveform is played on """ super().__init__() if set(expression.variables) - set('t'): raise ValueError( 'FunctionWaveforms may not depend on anything but "t"') self._expression = expression self._duration = TimeType.from_float(duration) self._channel_id = channel
def test_make_compatible_complete_unroll(self): wf1 = DummyWaveform(duration=1.5) wf2 = DummyWaveform(duration=2.0) program = Loop(children=[Loop(waveform=wf1, repetition_count=2), Loop(waveform=wf2, repetition_count=1)], repetition_count=2) _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(program.repetition_count, 1) self.assertIsInstance(program.waveform, RepetitionWaveform) self.assertIsInstance(program.waveform._body, SequenceWaveform) body_wf = program.waveform._body self.assertEqual(len(body_wf._sequenced_waveforms), 2) self.assertIsInstance(body_wf._sequenced_waveforms[0], RepetitionWaveform) self.assertIs(body_wf._sequenced_waveforms[0]._body, wf1) self.assertEqual(body_wf._sequenced_waveforms[0]._repetition_count, 2) self.assertIs(body_wf._sequenced_waveforms[1], wf2)
def test_init(self, mock_parse_program, mock_make_compatible): mock_parse_program.return_value = ('seq_el', 'wfs') mock_program = mock.MagicMock(spec=Loop) copied = mock.MagicMock(spec=Loop) channels = ('A', 'B', None, None) markers = (('A1', None), (None, None), (None, 'C2'), (None, None)) sample_rate = TimeType(12) amplitudes = (1, 1, 1, 1) offsets = (0, 0, 0, 0) voltage_transformations = tuple( mock.Mock(wraps=lambda x: x) for _ in range(4)) mock_program.copy_tree_structure.return_value = copied tek_program = TektronixProgram( program=mock_program, channels=channels, markers=markers, sample_rate=sample_rate, amplitudes=amplitudes, offsets=offsets, voltage_transformations=voltage_transformations) self.assertIs(tek_program._program, copied) copied.flatten_and_balance.assert_called_once_with(1) mock_make_compatible.assert_called_once_with(copied, 250, 1, sample_rate / 10**9) mock_parse_program.assert_called_once_with( program=copied, channels=channels, markers=markers, sample_rate=sample_rate, amplitudes=amplitudes, offsets=offsets, voltage_transformations=voltage_transformations)
eval_sum = [ (Sum(a_[i], (i, 0, Len(a) - 1)), { 'a': np.array([1, 2, 3]) }, 6), ] eval_array_expression = [(np.array([a * c, b * c]), { 'a': 2, 'b': 3, 'c': 4 }, np.array([8, 12]))] eval_exact_rational = [ (a * Rational('1/3'), { 'a': 2 }, TimeType.from_fraction(2, 3)), (a * Rational('1/3'), { 'a': Rational(1, 5) }, TimeType.from_fraction(1, 15)), # TODO: this fails # (np.array([a, Rational(1, 3)]), {'a': 2}, np.array([2, TimeType.from_fraction(1, 3)])) ] class TestCase(unittest.TestCase): def assertRaises(self, expected_exception, *args, **kwargs): if expected_exception is None: return contextlib.suppress() else: return super().assertRaises(expected_exception, *args, **kwargs)
def test_parse_program(self): ill_formed_program = Loop(children=[Loop(children=[Loop()])]) with self.assertRaisesRegex(AssertionError, 'Invalid program depth'): parse_program(ill_formed_program, (), (), TimeType(), (), (), ()) channels = ('A', 'B', None, None) markers = (('A1', None), (None, None), (None, 'C2'), (None, None)) # we do test offset handling separately amplitudes = (1, 1, 1, 1) offsets = (0, 0, 0, 0) voltage_transformations = tuple( mock.Mock(wraps=lambda x: x) for _ in range(4)) used_channels = {'A', 'B', 'A1', 'C2'} wf_defined_channels = used_channels & {'other'} sampled_6 = [ np.zeros((6, )), np.arange(6) / 6, np.ones((6, )) * 0.42, np.array([0., .1, .2, 0., 1, 0]), np.array([1., .0, .0, 0., 0, 1]) ] sampled_4 = [ np.zeros((4, )), np.arange(-4, 0) / 4, np.ones((4, )) * 0.2, np.array([0., -.1, -.2, 0.]), np.array([0., 0, 0, 1.]) ] sample_rate_in_GHz = TimeType.from_fraction(1, 2) # channel A is the same in wfs_6[1] and wfs_6[2] wfs_6 = [ DummyWaveform(duration=12, sample_output={ 'A': sampled_6[0], 'B': sampled_6[0], 'A1': sampled_6[0], 'C2': sampled_6[0] }, defined_channels=used_channels), DummyWaveform(duration=12, sample_output={ 'A': sampled_6[1], 'B': sampled_6[2], 'A1': sampled_6[3], 'C2': sampled_6[4] }, defined_channels=used_channels), DummyWaveform(duration=12, sample_output={ 'A': sampled_6[1], 'B': sampled_6[0], 'A1': sampled_6[3], 'C2': sampled_6[2] }, defined_channels=used_channels) ] wfs_4 = [ DummyWaveform(duration=8, sample_output={ 'A': sampled_4[0], 'B': sampled_4[0], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels), DummyWaveform(duration=8, sample_output={ 'A': sampled_4[1], 'B': sampled_4[2], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels), DummyWaveform(duration=8, sample_output={ 'A': sampled_4[2], 'B': sampled_4[0], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels) ] # unset is equal to sampled_n[0] binary_waveforms_6 = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (1, 3, 0), (2, 0, 0), (0, 0, 4), (1, 3, 0), (0, 0, 0), (0, 0, 2)] binary_waveforms_4 = [(0, 2, 0), (0, 0, 0), (0, 0, 3), (1, 2, 0), (2, 0, 0), (0, 0, 3), (2, 2, 0), (0, 0, 0), (0, 0, 3)] n_bin_waveforms = len(set(binary_waveforms_6)) + len( set(binary_waveforms_4)) tek_waveforms_6 = [ tek_awg.Waveform(channel=voltage_to_uint16(sampled_6[ch], 1, 0, 14), marker_1=sampled_6[m1], marker_2=sampled_6[m2]) for (ch, m1, m2) in binary_waveforms_6 ] tek_waveforms_4 = [ tek_awg.Waveform(channel=voltage_to_uint16(sampled_4[ch], 1, 0, 14), marker_1=sampled_4[m1], marker_2=sampled_4[m2]) for (ch, m1, m2) in binary_waveforms_4 ] tek_waveforms = set(tek_waveforms_4 + tek_waveforms_6) # equivalent of wfs_6 tek_6 = [ tek_waveforms_6[:3] + [6], tek_waveforms_6[3:6] + [6], tek_waveforms_6[6:] + [6] ] tek_4 = [ tek_waveforms_4[:3] + [4], tek_waveforms_4[3:6] + [4], tek_waveforms_4[6:] + [4] ] program = [(wfs_6[0], 1), (wfs_4[0], 2), (wfs_6[0], 3), (wfs_6[1], 4), (wfs_4[1], 5), (wfs_6[2], 6), (wfs_4[2], 7), (wfs_6[1], 8), (wfs_6[2], 9)] expected_sequence_entries_wfs = [ tek_6[0], tek_4[0], tek_6[0], tek_6[1], tek_4[1], tek_6[2], tek_4[2], tek_6[1], tek_6[2] ] expected_sequence_entries = tuple( tek_awg.SequenceEntry(entries=wfs, loop_count=idx + 1) for idx, wfs in enumerate(expected_sequence_entries_wfs)) loop_program = Loop(children=[ Loop(waveform=waveform, repetition_count=repetition_count) for waveform, repetition_count in program ]) sequence_entries, waveforms = parse_program( program=loop_program, channels=channels, markers=markers, sample_rate=sample_rate_in_GHz * 10**9, amplitudes=amplitudes, voltage_transformations=voltage_transformations, offsets=offsets) waveform_set = set(waveforms) self.assertEqual(len(waveform_set), len(waveforms)) self.assertIn(4, waveforms) self.assertIn(6, waveforms) waveform_set = waveform_set - {4, 6} self.assertEqual(len(waveform_set), n_bin_waveforms) self.assertEqual(tek_waveforms, waveform_set) self.assertEqual(len(sequence_entries), 9) self.assertEqual(expected_sequence_entries, sequence_entries)
def duration(self) -> TimeType: return TimeType.from_float(self._table[-1].t)